使用类型 T 的反射为具有属性的属性创建表达式<Func<T, TValue>>
Using Reflection on Type T to create Expression<Func<T, TValue>> for Properties that have attributes
我目前正在探索封装一些通常是迭代的逻辑的可行性。使用第 3 方库,它有一个通用 Html 帮助程序方法,允许您将属性从通用 T class 映射到 table 组件。通常你必须写一些东西来达到以下效果:
HtmlHelper.GenerateTable<ClassName>
.configure(tableDefinition =>{
// Adding each property you want to render here
tableDefinition.add(myClassRef => myClassRef.propertyA);
tableDefinition.add(myClassRef => myClassRef.propertyB);
})
我正在探索将属性添加到属性(例如标准 Display 属性)然后使用反射将 属性 添加到容器的想法。 add
方法只接受类型为 Expression<Func<T, TValue>>
的参数。根据我目前对反射的理解,我知道我可以 属性 通过遍历 PropertyInfo
并使用 GetCustomAttribute
.
检查我想要的属性来识别适用的属性
我很困惑的是,是否可以使用反射来提供 add 方法期望的参数类型?
我将抛出到目前为止我开始使用的辅助方法逻辑。我的假设是,这会引导我转向 Expression 和 Lambda classes,但我无法充实任何有效的东西,因为我在技术上没有 TValue
.
var t = typeof(T);
List<PropertyInfo> properties = t.GetProperties().ToList();
foreach(var prop in properties)
{
var attr = (DisplayAttribute[])prop.GetCustomAttributes(typeof(DisplayAttribute), false);
if (attr.Length > 0)
{
// TODO: Call add with expression?
}
}
您可以使用 Expression
class 中的方法轻松实现此目的。首先,最简单的方法是使用 lambda 编写表达式(例如 Expression<Func<A, B>> expr = x => x.PropertyA
),然后在调试器中检查它以查看编译器构造的内容。
在你的情况下,这样的事情应该有效:
// The parameter passed into the expression (myClassRef) in your code
var parameter = Expression.Parameter(typeof(T), "myClassRef");
// Access the property described by the PropertyInfo 'prop' on the
// myClassRef parameter
var propertyAccess = Expression.Property(parameter, prop);
// Since we're returning an 'object', we'll need to make sure we box value types.
var box = Expression.Convert(propertyAccess, typeof(object));
// Construct the whole lambda
var lambda = Expression.Lambda<Func<T, object>>(box, parameter);
tableDefinition.add(lambda);
请注意,我将 Expression<Func<T, object>>
传递给 add
,而不是 Expression<Func<T, TValue>>
。我 猜测 这无关紧要,它避免了必须使用反射调用 add
。当我过去编写类似于 add
的方法时,我根本不关心 TValue
:我只是检查 Expression
并从属性 访问。
如果您确实需要传递 Expression<Func<T, TValue>>
,您必须执行以下操作:
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), prop.PropertyType);
var lambda = Expression.Lambda(delegateType, propertyAccess, parameter);
// I'm assuming that your `add` method looks like:
// void add<T, TValue>(Expression<Func<T, TValue>> expr)
// I'm also assuming there's only one method called 'add' -- be smarter in that
// 'GetMethod' call if not.
var addMethod = typeof(TableDefinition)
.GetMethod("add")
.MakeGenericMethod(typeof(T), prop.PropertyType);
addMethod.Invoke(tableDefinition, new object[] { lambda });
请注意,在这种情况下您不需要 Expression.Convert(..., typeof(object))
。
我目前正在探索封装一些通常是迭代的逻辑的可行性。使用第 3 方库,它有一个通用 Html 帮助程序方法,允许您将属性从通用 T class 映射到 table 组件。通常你必须写一些东西来达到以下效果:
HtmlHelper.GenerateTable<ClassName>
.configure(tableDefinition =>{
// Adding each property you want to render here
tableDefinition.add(myClassRef => myClassRef.propertyA);
tableDefinition.add(myClassRef => myClassRef.propertyB);
})
我正在探索将属性添加到属性(例如标准 Display 属性)然后使用反射将 属性 添加到容器的想法。 add
方法只接受类型为 Expression<Func<T, TValue>>
的参数。根据我目前对反射的理解,我知道我可以 属性 通过遍历 PropertyInfo
并使用 GetCustomAttribute
.
我很困惑的是,是否可以使用反射来提供 add 方法期望的参数类型?
我将抛出到目前为止我开始使用的辅助方法逻辑。我的假设是,这会引导我转向 Expression 和 Lambda classes,但我无法充实任何有效的东西,因为我在技术上没有 TValue
.
var t = typeof(T);
List<PropertyInfo> properties = t.GetProperties().ToList();
foreach(var prop in properties)
{
var attr = (DisplayAttribute[])prop.GetCustomAttributes(typeof(DisplayAttribute), false);
if (attr.Length > 0)
{
// TODO: Call add with expression?
}
}
您可以使用 Expression
class 中的方法轻松实现此目的。首先,最简单的方法是使用 lambda 编写表达式(例如 Expression<Func<A, B>> expr = x => x.PropertyA
),然后在调试器中检查它以查看编译器构造的内容。
在你的情况下,这样的事情应该有效:
// The parameter passed into the expression (myClassRef) in your code
var parameter = Expression.Parameter(typeof(T), "myClassRef");
// Access the property described by the PropertyInfo 'prop' on the
// myClassRef parameter
var propertyAccess = Expression.Property(parameter, prop);
// Since we're returning an 'object', we'll need to make sure we box value types.
var box = Expression.Convert(propertyAccess, typeof(object));
// Construct the whole lambda
var lambda = Expression.Lambda<Func<T, object>>(box, parameter);
tableDefinition.add(lambda);
请注意,我将 Expression<Func<T, object>>
传递给 add
,而不是 Expression<Func<T, TValue>>
。我 猜测 这无关紧要,它避免了必须使用反射调用 add
。当我过去编写类似于 add
的方法时,我根本不关心 TValue
:我只是检查 Expression
并从属性 访问。
如果您确实需要传递 Expression<Func<T, TValue>>
,您必须执行以下操作:
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), prop.PropertyType);
var lambda = Expression.Lambda(delegateType, propertyAccess, parameter);
// I'm assuming that your `add` method looks like:
// void add<T, TValue>(Expression<Func<T, TValue>> expr)
// I'm also assuming there's only one method called 'add' -- be smarter in that
// 'GetMethod' call if not.
var addMethod = typeof(TableDefinition)
.GetMethod("add")
.MakeGenericMethod(typeof(T), prop.PropertyType);
addMethod.Invoke(tableDefinition, new object[] { lambda });
请注意,在这种情况下您不需要 Expression.Convert(..., typeof(object))
。