【发布时间】:2019-06-05 14:34:07
【问题描述】:
我的意图是传递一个类的公共属性,比如:
class MyTestClass
{
public string Name { get; set; }
public DateTime StartedAt { get; set; }
public TimeSpan Duration { get; set; }
}
给函数一个参数:
static void MyCustomFunc(params Expression<Func<MyTestClass, object>>[] props)
{
foreach (var p in props)
{
// The following only works for Name property, not for StartedAt or Duration
MemberExpression member = p.Body as MemberExpression;
PropertyInfo propertyInfo = (PropertyInfo)member.Member;
string name = propertyInfo.Name;
Type type = propertyInfo.PropertyType;
Func<MyTestClass, object> func = p.Compile();
}
}
该函数应该收集此信息并将其提供给导出器类,该类将MyTestClass 对象集导出到 CSV 文件。
写入 CSV 文件的输出取决于输入 MyCustomFunc 的属性的数量、类型和顺序。
所以这个:
MyCustomFunc(x => x.Name, x => x.StartedAt);
产生不同的结果:
MyCustomFunc(x => x.StartedAt, x => x.Name);
和
MyCustomFunc(x => x.StartedAt, x => x.Name, x => x.Duration);
不同于
MyCustomFunc(x => x.Duration, x => x.StartedAt, x => x.Name);
我的问题是让反射起作用。由于某种原因我无法理解p.Body:
- 对于
{x => x.Name}等于{x.Name}但是 - 对于
{x => x.StartedAt}等于{Convert(x.StartedAt)}
第一个可以处理
MemberExpression member = p.Body as MemberExpression;
但是第二个返回null,所以我得到一个空引用异常。
【问题讨论】:
-
Expression<Func<MyTestClass, object>>:你传递的 lambda 表达式返回object。 DateTime 是一个结构,因此必须将其装箱以使其成为对象。任何值类型都会发生这种情况。我只是为值类型添加一个特殊情况,然后继续。 -
有点题外话:现在我们在 C# 中有
nameof,我的建议是只使用编译时字符串(并使用nameof(x.Duration)传递它们)而不是所有运行时表情魔术。另见:Expression vs nameof. -
正如@EdPlunkett 所说,
DateTime是一个结构,你可以实现你想要做的事情member = p.Body as MemberExpression ?? ((UnaryExpression)p.Body).Operand as MemberExpression;。 -
if (p.Body is UnaryExpression ue && ue.Operand is MemberExpression ueMember) { propertyInfo = (PropertyInfo)ueMember.Member; } else if (p.Body is MemberExpression member) {...} else { /*throw*/ }是我的做法。相同的区别。 -
var body = p.Body; if (body is UnaryExpression ua) { body = ua.Operand; }; var member = body as MemberExpression; if (member == null) { throw ... } member.Member.Name;将是我的方法...
标签: c# linq lambda reflection