从 Expression 对象 e 中提取 Func<T, U>(其中使用保证 typeof(e) == typeof(Expression<Func<T,U>>))
Extract a Func<T, U> from Expression object e (where usage guarantees typeof(e) == typeof(Expression<Func<T,U>>))
我有一个通用的 class,我希望将其存储在一个集合中。因此,我已经采用了创建泛型继承自的抽象 class 的常用方法,并在顶部添加了泛型属性(注意:这种方法是新方法,所以可能我仍然做错了).
最终结果是,我的通用 class 看起来像:
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;}
}
基数class:
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][
public GetPropertyInfo(Expression<Func<T, U>> targetExpression)
{
// ... Magic expression parsing stuff ...
}
}
此类声明的列表将通过使用
支持 ISupportsDependencyManager
的 classes 的静态构造来填充
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<T,U>
时,我不知道 T
和 U
是什么(我确定它们被埋在 arg 中以被传递,但不确定在任何情况下如何提取或调用)。可能是通过反射,但不确定要使用的具体反射位。
- 如果我真的想计算表达式,我找不到这样做的方法 - 缺少编译方法,可能是因为不知道它是一个
Func
表达式。但是,虽然我知道它将是 Func<>
,并且我知道 Func<>
将有两种类型的参数,但我不知道如何指定它,而不是 what 那些类型参数将是。
有人知道如何处理吗?
注意,我实际上打算用这些 Expression
做的事情也可能会涉及到它。实际的 class 有 4 个类型参数和三个表达式。这三个都用作指定 属性 名称的方式(用于 属性 更改通知目的),但第三个也需要能够编译,以便为我提供一种获取针对类型 T 的对象评估时表达式的当前值。这意味着,在大多数情况下,我并不真正关心内部 Func
,只关心 Expression.Body
最右边的叶子评估为的字符串,但至少在一种情况下,我确实关心编译和评估 Func
.
NB2:每次创建使用它的对象时,预计此代码会 运行 通过一堆表达式,但在创建对象后预计不会 运行 - 所以我没问题原则上反思。
编辑:大幅更新代码以更清楚地了解它的用途。
不使用非通用 Expression
,而是使用 LambdaExpression
,因此您可以访问 Parameters
和 Compile
。 Expression都是LambdaExpression
(见定义:
public sealed class Expression<TDelegate> : LambdaExpression
)
我有一个通用的 class,我希望将其存储在一个集合中。因此,我已经采用了创建泛型继承自的抽象 class 的常用方法,并在顶部添加了泛型属性(注意:这种方法是新方法,所以可能我仍然做错了).
最终结果是,我的通用 class 看起来像:
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;}
}
基数class:
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][
public GetPropertyInfo(Expression<Func<T, U>> targetExpression)
{
// ... Magic expression parsing stuff ...
}
}
此类声明的列表将通过使用
支持ISupportsDependencyManager
的 classes 的静态构造来填充
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<T,U>
时,我不知道T
和U
是什么(我确定它们被埋在 arg 中以被传递,但不确定在任何情况下如何提取或调用)。可能是通过反射,但不确定要使用的具体反射位。 - 如果我真的想计算表达式,我找不到这样做的方法 - 缺少编译方法,可能是因为不知道它是一个
Func
表达式。但是,虽然我知道它将是Func<>
,并且我知道Func<>
将有两种类型的参数,但我不知道如何指定它,而不是 what 那些类型参数将是。
有人知道如何处理吗?
注意,我实际上打算用这些 Expression
做的事情也可能会涉及到它。实际的 class 有 4 个类型参数和三个表达式。这三个都用作指定 属性 名称的方式(用于 属性 更改通知目的),但第三个也需要能够编译,以便为我提供一种获取针对类型 T 的对象评估时表达式的当前值。这意味着,在大多数情况下,我并不真正关心内部 Func
,只关心 Expression.Body
最右边的叶子评估为的字符串,但至少在一种情况下,我确实关心编译和评估 Func
.
NB2:每次创建使用它的对象时,预计此代码会 运行 通过一堆表达式,但在创建对象后预计不会 运行 - 所以我没问题原则上反思。
编辑:大幅更新代码以更清楚地了解它的用途。
不使用非通用 Expression
,而是使用 LambdaExpression
,因此您可以访问 Parameters
和 Compile
。 Expression都是LambdaExpression
(见定义:
public sealed class Expression<TDelegate> : LambdaExpression
)