使用 T 类型的反射为具有属性的属性创建 Expression<Func<T, TValue>>
Posted
技术标签:
【中文标题】使用 T 类型的反射为具有属性的属性创建 Expression<Func<T, TValue>>【英文标题】:Using Reflection on Type T to create Expression<Func<T, TValue>> for Properties that have attributes 【发布时间】:2021-07-29 00:18:39 【问题描述】:我目前正在探索封装一些通常是迭代的逻辑的可行性。使用第 3 方库,它具有通用 html Helper 方法,允许您将属性从 Generic T 类映射到表组件。通常你必须写一些东西来达到以下效果:
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 类,但我无法充实任何有效的东西,因为我在技术上没有 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?
【问题讨论】:
【参考方案1】:您可以使用Expression
class 上的方法轻松实现此目的。首先,使用 lambdas 编写表达式是最简单的(例如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
并从属性访问中获取PropertyInfo
。
如果您确实需要传递 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))
。
【讨论】:
当我只处理字符串时,我确认这确实适用于库。但我正在测试的属性之一是 NullableExpression.Convert(..., typeof(object))
。请参阅我的编辑。您的第三方方法完全有可能不知道如何解析它,因此您可能需要退回到第二种方法,但试一试
我在玩游戏时碰巧偶然发现了它,它可以解决可空类型。即使在转换之后,DateTime 的行为也不像其他可为空的值类型,因此我最终明确地检测到 DateTime 并创建了一个转换 DateTime 的特殊规则,因此这可能是我能提出的唯一剩余改进领域。
这很奇怪——你有更多的信息吗?它似乎在这里工作正常:dotnetfiddle.net/4r3vaI以上是关于使用 T 类型的反射为具有属性的属性创建 Expression<Func<T, TValue>>的主要内容,如果未能解决你的问题,请参考以下文章