表达<FUNC <T>&GT; - 在没有Compile()

时间:2015-11-12 17:35:51

标签: c# lambda expression-trees

编辑:更新了我正在做的事情的例子。

编辑2:原因:
我的班级“掌握”一个属性设定者(或方法,匿名与否)并“控制”它一段时间。用户可以尝试附加我的类的另一个实例,并将其附加到已经“受控”的属性。我需要一种可靠地检测这些冲突的方法。我可以放弃()=&gt; {property}方法但需要用户将target属性包装在方法中,如果他/她想要冲突检测,则传递包含该属性的对象。这样做会带来更高的漏洞风险,因为它们可能会输入错误的对象,而且班级也没有办法检查错误。

我无法使用Lambda Compile(),因为除了JIT,我的目标是AOT。


我有一个方法,需要

Expression<Func<T>> 

作为一个论点。 Expression始终求值为具有类型T的setter的属性。

private static TargetInfo GetTargetInfo<T>(Expression<Func<T>> _propertyExpression, out Action<T> _setter)
{
    //Get Property info
    var propInfo = ((MemberExpression)_propertyExpression.Body).Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException("_propertyExpression must be a property.");


    var declType = propInfo.DeclaringType;
    var methodInfo = propInfo.GetSetMethod(true) ?? declType.GetProperty(propInfo.Name).GetSetMethod(true);
    if (methodInfo == null)
        throw new Exception("Could not create setter from property '" + _propertyExpression + "'");
    var isStaticProp = methodInfo.IsStatic;

    //Get Target Object
    object targetObject = null;
    var memberExp = _propertyExpression.Body as MemberExpression;
    var bodyExp = memberExp.Expression;
    if (bodyExp != null)
// PROBLEM LINE BELOW - Can't use Compile()  *********************************
        targetObject = Expression.Lambda<Func<object>>(bodyExp).Compile()();   
// PROBLEM LINE ABOVE  *******************************************************
    else if (isStaticProp)
        targetObject = memberExp.Member.DeclaringType;
    else
        throw new Exception("Could not determine target object.  Use Action<T> overload. (no conflict detection)");


    //Cache setter of property
    if (isStaticProp)
        _setter = (Action<T>) Delegate.CreateDelegate(typeof (Action<T>), methodInfo);
    else
        _setter = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), targetObject, methodInfo);

    return new TargetInfo(methodInfo, targetObject);
}

表达式(_propertyExpression)始终是属性。该物业总是有一个二传手。

  • 我需要将属性的setter提取为Action(我可以这样做) 没问题)
  • 我还需要包含此对象的对象的实例 不使用Compile()的属性,因为它不可用于某些 我定位的目标平台。

我需要该实例的原因是确定我的类的另一个对象实例当前是否正在该对象上使用该属性SETTER。 (没有线程,只是在一段时间内使用)我将对象实例和属性设置器信息一起存储为一个键,如果两个相等的键存在则存在冲突并且它是根据用户的配置设置来处理的。我的班级。

我能够获得简单表达式的对象实例,如:

() => MyProperty

() => MyObjectInst.PropertyFoo

() => MyStaticClass.PropertyFooBar

没有汗!

但如果用户这样做会怎样:

//Need instance of 'someObject' here.
() => SomeArray[idx].someObject.propertyA

或吹我的上衣:

//Need instance of 'someField[4]' here
() => SomeStaticClass.somePropertyList[5].SomeDictionary[objKey].SomeProperty.someFieldArray[4].propertyB

AAAK!

我已经看了一段时间,我相信我需要遍历树回到基础对象(ConstantExpression)然后开始使用收集到的信息向后回到树上。鉴于上面的例子我决定我需要“更大的枪支”。

  1. 除了完全走树而不使用Compile()之外,是否有更简单的方法来获取属性的调用者实例?请解释一下!

  2. 如果不是1.如果有人能指出我正确的方向,如何穿越树和处理如上所述的东西。那太好了。我没有找到关于遍历表达式树,表达式类型等的大量信息。即使是对特定于该主题的推荐书籍的参考也会有所帮助。

  3. 叹息我应该放弃并强制用户使用简单的表达式。 即。不允许用户键入冗长的属性访问行。

  4. 澄清: 正在缓存setter以供机制类“FOO”使用,该机制类将在某个定义的时间段内将该值设置为类型T的UNKNOWN值范围。缓存对象实例并将其用作检测FOO的另一个实例尝试附加并将值设置为该同一对象上的同一属性的情况的键。对象实例用于检测这些冲突,并且还用于跟踪附加到任何特定对象的所有“FOO”。用户可以将多个“FOO”附加到同一对象,到不同的属性。在用户将FOO的实例附加到已经为该属性附加了FOO的对象上的属性的情况下,发生冲突事件。 FOO可以配置如何处理这种情况。

2 个答案:

答案 0 :(得分:1)

以下是您建议的方法存在的问题:

  1.   

    是否有更简单的方法可以让该物业的来电者获得其他实例   而不是完全走树而不使用Compile()?请   如果是这样解释的话!

  2. 简短的回答,没有。表达式意在编译。进入.NET Funcs / Actions或其他一些平台(LINQ-to-Entities本质上将它们编译成SQL)。如果你不想编译它,你可能使用了错误的工具。

    1.   

      如果不是1.如果有人能指出我正确的方向如何   遍历树并处理上述内容。那就是   大。我没有找到关于遍历Expression的大量信息   树木,表达类型等。甚至是对推荐书籍的引用   具体的主题将有所帮助。

    2. 遍历表达式树的最佳方法是继承ExpressionVisitor。一个好的博客将是Matt Warren的博客http://blogs.msdn.com/b/mattwar/,阅读有关实施IQueryable的系列文章。但不,这并不容易。

      可以(从理论上)继承ExpressionVisitor以向下/向上走链接到原始实例。但是你必须以某种方式向上/向下走回链条,最简单的方法是编译子表达式。理论上,你可以使用反射来回过头来,正如你的答案所尝试的那样,但是如果你的无编译姿势是铁质的,我希望你明显地得到你选择了错误工具的印象。

      1.   

        叹息我应该放弃并强迫用户使用简单的表达方式。

      2. 你还没有概述这个的好处/缺点。

答案 1 :(得分:0)

我已确认以下适用于MONO - &gt; iOS ...至少有基础和数组。避风港能够得到字典/列表滚动。似乎AOT不喜欢MethodInfo.Invoke。

编辑:

我在MSDN上找到了这个答案:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/af305a45-36e4-4607-9190-f1b33a0bea57/get-class-instance-from-lambda-expression

遍历Expression树,根据需要评估树的每个部分,最后返回指定属性的包含对象。

加入此(以及添加下面的方法代码):

targetObject = GetContainer(_propertyExpression);

而不是:

targetObject = Expression.Lambda<Func<object>>(bodyExp).Compile()();

在上面的代码中。

我还没有在其他平台上对它进行全面测试,我确定我会遇到其他需要添加更多案例的情况,但这可能足以帮助其他人并且足够让我继续前进,我会尝试更新,因为我遇到了更多:

public static object GetContainer<T>(Expression<Func<T>> propertyLambdaExpression)
{
    return Evaluate((propertyLambdaExpression.Body as MemberExpression).Expression);
}
public static object Evaluate(Expression e)
{
    switch (e.NodeType)
    {
        case ExpressionType.Constant:
            return (e as ConstantExpression).Value;
        case ExpressionType.MemberAccess:
            {
                var propertyExpression = e as MemberExpression;
                var field = propertyExpression.Member as FieldInfo;
                var property = propertyExpression.Member as PropertyInfo;
                var container = propertyExpression.Expression == null ? null : Evaluate(propertyExpression.Expression);
                if (field != null)
                    return field.GetValue(container);
                else if (property != null)
                    return property.GetValue(container, null);
                else
                    return null;
            }
        case ExpressionType.ArrayIndex: //Arrays
        {
            var arrayIndex = e as BinaryExpression;
            var idx = (int)Evaluate(arrayIndex.Right);
            var array = (object[])Evaluate(arrayIndex.Left);
            return array[idx];
        }
        case ExpressionType.Call: //Generic Lists and Dictionaries
        {
            var call = e as MethodCallExpression;
            var callingObj = Evaluate(call.Object);
            object[] args = new object[call.Arguments.Count];
            for (var idx = 0; idx < call.Arguments.Count; ++idx)
                args[idx] = Evaluate(call.Arguments[idx]);
            return call.Method.Invoke(callingObj, args);
        }
        default:
            return null;
    }
}