我可以评估一个前pression的方式来确定,并可能设置一个属性,它是空? [英] Can I evaluate an expression in a way to determine and possibly set a property that is null?

查看:171
本文介绍了我可以评估一个前pression的方式来确定,并可能设置一个属性,它是空?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有需要的对象,并根据该属性内将执行不同的操作的服务;与此这些属性可以为空,意味着不执行此操作。

I have a service that takes an object and based on the properties within will perform different actions; with this any of these properties can be null, meaning don't perform this action.

我想创建一个非常简单易用的API来做到这一点的情况下,某些属性可以是多层次深,这里是当前实现的一个例子

I am trying to create a very simple to use API to do this in cases where some properties can be multiple levels deep, here is an example of the current implementation

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

这是一个稍微简化版本,在现实情况下,我有时不得不安装多个父属性这样才能在底部设置一个字符串属性。

This is a slightly simplified version and in real cases I some times have to setup multiple parent properties this way in order to set one string property at the bottom.

我想这样做是GetNewData方法中调整code处理,因为必要的,这样的code看起来是这样的实例化这些属性:

What I would like to do is adjust the code within the GetNewData method to handle instantiating these properties as needed so that the code could look like this:

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

下面是我目前的$ C $下GetNewData:

Here is my current code for 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 数组是防爆pression&lt;作用&LT; T&GT;&GT; [] 的行动,并以某种方式得到一个成员EX pression任何这些家长都为空,这将允许我使用活化剂来创建一个实例。但是我的经验用防爆pression树更高级的功能是渺茫的最好的。

My first thought is to change the params array to be Expression<Action<T>>[] actions and somehow get a member expression for any of these parents that are null, which would allow me to use the activator to create an instance. However my experience with the more advanced features of Expression trees is slim at best.

的原因试图使此API作为简单化尽可能是,它是最终将被用于非开发的UI测试框架

The reason for attempting to make this API as simplistic as possible is that it is a UI testing framework that will eventually be used by non developers.

编辑: 我想补充目前实施的一个进一步的例子,希望能够证明什么,我试图做将提供更具可读性code,是有一个很轻微的'副作用'如果我能顺利完成这件事,但我认为这是一个有益的。

I want to add one further example of the current implementation to hopefully demonstrate that what I'm trying to do will provide for more readable code, yes there is a very slight 'side-effect' if I can pull this off but I would argue it is a helpful one.

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);

编辑2: 来自Apache节俭结构定义生成的我正在与这里的类,因此我对他们没有控制权,以便能够建立一些还挺聪明的构造。

Edit 2: The classes that I'm working with here are generated from Apache Thrift struct definitions and as such I have no control over them to be able to set up some kinda of smart constructor.

推荐答案

在我的其他问题得到答案之后,我现在有这样一个全功能的解决方案,这是不是很简单的语法,因为我最初目标是,但它也不错。

After getting an answer on my other question I now have a fully working solution for this, it isn't quite as simple syntax as I was originally aiming for, but it isn't bad.

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

在DataBuilder类:

The DataBuilder Class:

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();

这篇关于我可以评估一个前pression的方式来确定,并可能设置一个属性,它是空?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆