【问题标题】:Extract a Func<T, U> from Expression object e (where usage guarantees typeof(e) == typeof(Expression<Func<T,U>>))从表达式对象 e 中提取 Func<T, U> (其中使用保证 typeof(e) == typeof(Expression<Func<T,U>>))
【发布时间】:2015-04-06 11:11:42
【问题描述】:

我有一个希望存储在集合中的通用类。因此,我采用了通常的方法,即创建泛型继承的抽象类,并在顶部添加泛型属性(注意:这种方法的新方法,所以可能我仍然做错了什么)。

最终结果是,我的泛型类看起来像:

public class DependencyDeclaration<TDependantHost, TDependant, TFoundationHost, TFoundation> : DependencyDeclaration {

    public DependencyDeclaration(Expression<Func<TDependandHost, TDependant>> dependantRef, Expression<Func<TFoundationHost, TFoundation>> foundationRef, Expression<Func<TDependantHost, TFoundationHost>> foundationHostRef)

    public Expression<Func<TDependantHost, TDependant>> DependantRef {get;private set;}
    public Expression<Func<TDependantHost, TDependant>> FoundationRef {get;private set;}
    public Expression<Func<TDependantHost, TDependant>> FoundationHostRef {get; private set;}

}

还有基类:

public abstract class DependencyDeclaration {
    public string GetDependantName() {
        // Some code to return the name of the leaf property from 
        // DependantRef, roughly akin to GetPropertyNameFromExpression
        // in BindableObjectBase from
        // http://www.pochet.net/blog/2010/07/02/inotifypropertychanged-automatic-dependent-property-and-nested-object-support/

        var e = this
            .GetType()
            .GetProperties()
            .Where(p=>p.Name=="DependantRef")
            .First()
            .GetValue(this) as Expression;
        return GetPropertyName(e).Name; // GetPropertyName requires a Func<T,U>, so this doesn't work as is.
    }

    public string GetFoundationName(Expression e) {
        // ... similar implementation to GetDependantName
    }


    public string GetFoundationHostName(Expression e) {
        // ... similar implementation to GetDependantName
    }

    public object GetFoundationHostRef(object dependantHost, Expression e) {
        // Evaluates this.FoundationHostRef via reflection against 
        // dependantHost.
        // Will function based on the comment below about using 
        // LambdaExpression, so not that worried about this one.
        // Something like, though:
        var e = this
            .GetType()
            .GetProperties()
            .Where(p=>p.Name=="DependantRef")
            .First()
            .GetValue(this) as LambdaExpression;
        if (e == null)
            return;

        var foundationHostRefFunc = e.Compile()
        return foundationHostRefFunc(dependantHost);
    }

    // Returns the PropertyInfo of a property via an Expression
    // as provided [here][http://stackoverflow.com/a/672212/847721]
    public GetPropertyInfo(Expression<Func<T, U>> targetExpression) 
    {
        // ... Magic expression parsing stuff ...
    }
}

此类声明的列表将在支持ISupportsDependencyManager 的类的静态构造中填充,例如

public class MyClassWithCalculatedProperties : ISupportsDependencyManager {
    // MyPropertySource inherits from INotifyPropertyChanged
    public object MyPropertySource {get;set;}

    // Trite example calculated property. Obviously actual calculated 
    // properties could be arbitrary.
    public int MyCalculatedProperty 
    {
        get {
            return MyPropertySource.SomeProperty + 50;
        }
    }
    private static DependencyDeclaration MyCalculatedPropertyDependencies = 
        new DependencyDelcaration<MyClassWithCalculatedProperties, int, MyPropertySource, int>(
            (dependantHost)=>dependantHost.MyCalculatedProperty,
            (foundationHost)=>foundationHost.SomeProperty,
            (dependantHost)=>dependantHost.MyPropertySource
        );

    static MyClassWithCalculatedProperties() 
    {
        // Static constructor iterates over the properties in the type
        // using DependencyManager and appends them to a list that 
        // DependencyManager maintains of DependencyDeclaration instances
        // 
        // This is where I'm going to generate a list of DependencyDeclarations - see below.
        DependencyManager
            .RegisterDependencies(
                typeof(MyClassWithCalculatedProperties)
            );
    }

    public MyClassWithCalculatedProperties() 
    {
        // Constructor for MyClassWithCalculatedProperties

        // Attach event handlers that allow calculated properties to 
        // have notifications on them.
        DependencyManager.AttachPropertyChangedHandlers(this)
    }
}

// This isn't finished yet, so expect bugs in ANY event 
// (not certain I'm using reflection right)
public class DependencyManager 
{
    private static ConcurrentDictionary<Type, ConcurrentBag<DependencyDeclaration>> _ParsedDependencies;

    // This is where I need the abstract class and/or interface. It 
    // allows all DependencyDeclarations to co-exist in the same 
    // collection to avoid needing to parse the type using reflection
    // more than once in a given program execution.
    public static RegisterDependencies(Type targetType) 
    {
        var dependencyDeclarations = 
            targetType
                .GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)
                .Where(fi => fi.FieldType == typeof(DependencyDeclaration))
                .Select(fi => fi.GetValue(targetType));
        ConcurrentBag<DependencyDeclaration> container = GetManagedDependencyContainer(targetType);
        dependencyDeclarations
            .Cast<DependencyDeclaration>()
            .ToList()
            .ForEach(d => container.Add(d));
    }

    /*
    * Other methods of DependencyManager
    */

}

这给我带来了两个问题:

  • 当我需要调用 GetUonTExpression&lt;T,U&gt; 时,我不知道 TU 是什么(我确定它们被埋在要传递的 arg 中,但不知道如何在任何情况下提取或调用)。可能通过反射,但不确定要使用的具体反射位。
  • 如果我真的想计算表达式,我找不到这样做的方法 - 缺少 compile 方法,可能是因为不知道它是 Func 表达式。但是,虽然我知道它将是 Func&lt;&gt;,并且我知道 Func&lt;&gt; 将有两种类型的参数,但我不知道如何指定它,而不是 what 那些类型args 将是。

有人知道怎么做吗?

注意,我实际上打算用这些Expressions 做的事情也可能涉及到它。实际的类有 4 个类型参数和三个表达式。所有这三个都用作指定属性名称的方法(用于属性更改通知目的),但第 3 个还需要能够编译,以便在评估时为我提供一种获取表达式当前值的方法T 类型的对象。这意味着,在大多数情况下,我并不真正关心内部 Func,只关心 Expression.Body 最右边的叶子计算的字符串,但至少在一种情况下,我确实关心编译和评估Func

NB2:每次创建使用它的对象时,预计此代码都会运行一系列表达式,但预计不会在创建对象后运行 - 所以原则上我可以接受反射。

编辑:大幅更新代码以更清楚地了解其用途。

【问题讨论】:

  • 目前还不清楚你为什么要使用表达式树 - 老实说,我很难理解为什么你有 any .我们真的可以用更多的上下文来做......
  • 我对表达式树的大部分使用基于我所知道的关于解析来自 pochet.net/blog/2010/07/02/… 项目的 GetPropertyNameFromExpression 中的表达式的知识。这需要一个 Expression> 并返回属性的名称。除此之外,正如我所说,主要目标是 3 个字符串和一个对象引用;如果我做错了,请随时纠正我。
  • 上下文确实不够清楚 - 你已经描述你想如何使用它而不是演示它,而后者会是一个更清晰的方法。目前,各个位并没有真正相互连接。请注意,从 C# 6 开始,使用 nameof 是一种以编译时安全的方式指定属性名称的更简洁的方法。
  • 不要使用非通用的Expression,而是使用LambdaExpression,这样您就可以访问ParametersCompileExpression&lt;Func&lt;T,U&gt;&gt; 都是LambdaExpression(见public sealed class Expression&lt;TDelegate&gt; : LambdaExpression
  • 嗯。好的,这有点清楚了,尽管对我来说这一切似乎都相当复杂。不过,仍然不清楚您从非泛型基类中获得了什么好处 - 我会注意,如果泛型派生类是 only 派生类,您至少可以让它更简单通过在基类中声明方法抽象,或者仅使用非泛型接口。

标签: c# generics lambda expression


【解决方案1】:

不要使用非通用的Expression,而是使用LambdaExpression,这样您就可以访问ParametersCompileExpression 都是LambdaExpression(见定义:

public sealed class Expression<TDelegate> : LambdaExpression

)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多