我可以通过计算表达式来确定并可能设置为 null 的属性吗?

2024-01-04

我有一个服务,它接受一个对象,并根据其中的属性执行不同的操作;这样,这些属性中的任何一个都可以为 null,这意味着不执行此操作。

我正在尝试创建一个非常简单易用的 API 来在某些属性可能深达多个级别的情况下执行此操作,这是当前实现的示例

service.PerformActions(DataFactory.GetNewData<ActionsInfo> (
    data => data.SomeParent = DataFactory.GetNewData<SomeParentInfo>(), 
    data => data.SomeParent.SomeProperty = "someValue" ));

这是一个稍微简化的版本,在实际情况下,我有时必须以这种方式设置多个父属性,以便在底部设置一个字符串属性。

我想要做的是调整 GetNewData 方法中的代码以根据需要处理实例化这些属性,以便代码如下所示:

service.PerformActions(DataFactory.GetNewData<ActionsInfo> (
    data => data.SomeParent.SomeProperty = "someValue" ));

这是我当前的 GetNewData 代码:

public static T GetNewData<T>(params Action<T>[] actions)
{
    var data = Activator.CreateInstance<T>();

    foreach (var action in actions)
    {
        try
        {
            action(data);

        }
        catch (NullReferenceException)
        {
            throw new Exception("The property you are attempting to set is within a property that has not been set.");
        }
    }

    return data;
}

我的第一个想法是改变params数组为Expression<Action<T>>[]操作并以某种方式获取任何为空的父级的成员表达式,这将允许我使用激活器创建一个实例。然而,我对表达式树的更高级功能的体验充其量是微乎其微的。

尝试使这个 API 尽可能简单的原因是,它是一个 UI 测试框架,最终将被非开发人员使用。

Edit:我想添加当前实现的另一个示例,以希望证明我正在尝试做的事情将提供更具可读性的代码,是的,如果我能实现这一点,会有一个非常轻微的“副作用”,但我会争辩这是一个很有帮助的。

ExampleDataFactory.GetNewData<ServicesAndFeaturesInfo>(
    x => x.Property1 = ExampleDataFactory.GetNewData<Property1Type>(),
    x => x.Property1.Property2 = ExampleDataFactory.GetNewData<Property2Type>(),
    x => x.Property1.Property2.Property3 = ExampleDataFactory.GetNewData<Property3Type>(),
    x => x.Property1.Property2.Property3.Property4 = true);

Edit 2:我在这里使用的类是从 Apache Thrift 结构定义生成的,因此我无法控制它们来设置某种智能构造函数。


在得到我的其他问题的答案后,我现在有了一个完全可行的解决方案,它的语法并不像我最初的目标那么简单,但它还不错。

 public static DataBuilder<T> GetNewData<T>() where T : class, new()
    {
        return new DataBuilder<T>();
    }

数据构建器类:

public class DataBuilder<T>
{
    public readonly T data;

    public DataBuilder()
    {
        data = Activator.CreateInstance<T>();
    }

    public DataBuilder(T data)
    {
        this.data = data;
    }

    public DataBuilder<T> SetValue<T2>(Expression<Func<T, T2>> expression, T2 value)
    {
        var mExpr = GetMemberExpression(expression);

        var obj = Recurse(mExpr);
        var p = (PropertyInfo)mExpr.Member;
        p.SetValue(obj, value); 
        return this;
    }

    public T Build()
    {
        return data;
    }

    public object Recurse(MemberExpression expr)
    {
        if (expr.Expression.Type != typeof(T))
        {
            var pExpr = GetMemberExpression(expr.Expression);
            var parent = Recurse(pExpr);

            var pInfo = (PropertyInfo) pExpr.Member;
            var obj = pInfo.GetValue(parent);
            if (obj == null)
            {
                obj = Activator.CreateInstance(pInfo.PropertyType);
                pInfo.SetValue(parent, obj);
            }

            return obj;
        }
        return data;
    }

    private static MemberExpression GetMemberExpression(Expression expr)
    {
        var member = expr as MemberExpression;
        var unary = expr as UnaryExpression;
        return member ?? (unary != null ? unary.Operand as MemberExpression : null);
    }

    private static MemberExpression GetMemberExpression<T2>(Expression<Func<T, T2>> expr)
    {
        return GetMemberExpression(expr.Body);
    }
}

用法:

ExampleDataFactory.GetNewData<ServicesAndFeaturesInfo>()
            .SetValue(x=> x.Property1.EnumProperty, EnumType.Own)
            .SetValue(x=> x.Property2.Property3.Property4.BoolProperty, true)
            .Build();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我可以通过计算表达式来确定并可能设置为 null 的属性吗? 的相关文章

随机推荐