首页 > 解决方案 > 强制执行那个 `Expression>` 有一个 Body 是一个 `MemberExpression`

问题描述

public void GiveMeAProp(Expression<Func<TInstance,TProp>> prop)

经常被用作说“请给我一个属性选择器”的标准方式。但是有很多东西可以满足这个签名,但实际上并不是“真正的”属性。例如,以下所有编译。

GiveMeAProp(x => Method());
GiveMeAProp((x) => localVariable);
GiveMeAProp(x => x);
GiveMeAProp(x => (Type)x);
GiveMeAProp((x) => !x.BooleanProp);

我当然可以简单地声明我的方法采用 a MemberExpression,但随后我失去了表达式的严格类型,特别是失去了编写的能力:

public void SetThisProp(Expression<Func<TInstance,TProp>> prop, TProp value)

我可以在运行时检查我最终得到的表达式的 Body 是 a MemberExpression,但是如果我可以让类型系统为我强制执行它会非常好?Expression<T>在我的用例中,实际上没有其他类型的参数是有效的。

问题:无论如何我可以静态强制一个参数代表 aMemberExpression并且它返回一个 type吗?T

标签: c#genericsreflectionexpression

解决方案


Is there anyway that I can enforce statically that an argument represents a MemberExpression and also that it returns a type T?

No, not at present in C#.

It is difficult to prove a negative, but I think it is reasonably clear from all the various libraries which follow the pattern you cite that this is the optimal approach here. If there were a better approach, I think it would have caught on pretty widely by now.

For example, the "OrderBy" method in Linq is a pretty classic example of a library asking for a property in a typesafe way, as you are doing here, and they haven't managed to avoid the same pitfalls you cite:

public static System.Linq.IOrderedQueryable<TSource> OrderBy<TSource,TKey> (this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<Func<TSource,TKey>> keySelector);

var studentsOrderByRank = students.OrderBy(w => w.Rank);

The following will compile, but fail at runtime:

string localVar = "example";
var studentsOrderByRank = students.OrderBy(w => localVar);

I think that, so long as your library gives clear error messages at runtime if users pass a Expression<Func<TInstance,TProp>> which is not in fact a property access, this is the best you can do.

See also this answer for best practices in your library re consuming these expressions.


推荐阅读