接受 Expression<Func<T>> 表达式作为参数的扩展方法
Posted
技术标签:
【中文标题】接受 Expression<Func<T>> 表达式作为参数的扩展方法【英文标题】:Extension method that accepts Expression<Func<T>> expression as parameter 【发布时间】:2016-01-12 06:57:14 【问题描述】:我正在使用 .NET4.5
和 C#
我想创建扩展方法,允许我传递对象的属性,如果该对象的 Id 为 0,则 return null
否则返回该属性值。
我可以通过反思来解决问题,因此请考虑更多的训练练习,而不是我试图解决实际问题。
当前扩展方法位于static
类中,如下所示:
public static object GetNullIfNotSet(this WillAnswer answer, Expression<Func<WillAnswer>> expression)
if (answer.Id == 0) return null;
return expression.Compile()();
我希望能够使用它的方式如下(答案类型为WillAnswer
):
var emptyIfNewObject = answer.GetNullIfNotSet(o => o.HasBusinessAssets)
但是它给了我编译错误:
错误 1 委托 'System.Func' 不占用 1 参数 C:\hg\Website\Areas\Wills\ViewModel\Answers.cs 38 59 网站
这让我皱起了眉头,因为我认为我没有传递任何论据(是吗?)。能否请比我聪明的人解释一下我的哪些期望是错误的。
以防万一我不清楚,我会重申。 我想要的是能够打电话
如果Id
的answer
是0
,则var emptyIfNewObject = answer.GetNullIfNotSet(o => o.HasBusinessAssets)
并得到null
。
【问题讨论】:
正如其他人指出的那样,您不需要表达式。你的错误的根源是你定义你的 func 错误的事实。您可能想要 Func 这样称呼它:answer.GetNullIfNotSet(() => (((Func<WillAnswer>)(() => answer.HasBusinessAssets))()));
真是一团糟!回到haim770的答案:-)
如果您在调用方法时有权访问要访问其属性的对象,因为它是对象本身的扩展,那么为什么不只传递属性而不是传递Func
?
【参考方案1】:
根本不需要Expression
,只需使用Func<WillAnswer, TProp>
:
public static TProp GetNullIfNotSet<TProp>(this WillAnswer answer, Func<WillAnswer, TProp> func)
if (answer.Id == 0) return default(TProp);
return func(answer);
请注意,这并不总是返回null
,而是返回默认值(如果属性是值类型)。
更新(根据您的要求):
为了能够为所有传递的属性返回null
,方法签名改为返回object
:
public static object GetNullIfNotSet<TProp>(this WillAnswer answer, Func<WillAnswer, TProp> func)
if (answer.Id == 0) return null;
return func(answer);
但是,您将失去泛型的好处,并且最终将显式强制转换为 Nullable<T>
:
var emptyIfNewObject = (bool?)answer.GetNullIfNotSet(o => o.HasBusinessAssets)
这不太理想。
【讨论】:
很好的答案,但是,我如何让它返回 null? 为了始终返回null
,编译器必须确保传递的属性x => x.SomeProp
是引用类型。这可以通过添加通用约束来完成,但这意味着您将无法返回x => x.SomeInt
(例如)。这是你需要的吗?
您能否在回答时稍微改变一下而不是 TProp
return object
这允许返回 null public static object GetNullIfNotSet<TProp>(this WillAnswer answer, Func<WillAnswer, TProp> func) if (answer.Id == 0) return null; return func(answer);
非常感谢。我很欣赏第二种解决方案并不那么优雅,但是一些用户会发现它很有用。再次感谢。
@MatasVaitkevicius - 在这些情况下,我通常会编写两种方法 - 一种用于引用类型 TProp GetNullIfNotSet<TProp>(…) where TProp:class
,另一种用于值类型 TProp? GetNullableIfNotSet<TProp>(…) where TProp:struct
。【参考方案2】:
看来您需要Func<WillAnswer, T>
而不是表达式:
public static T GetDefaultIfNotSet<T>(this WillAnswer answer, Func<WillAnswer, T> func)
if (null == answer)
throw new ArgumentNullException("answer");
else if (null == func)
throw new ArgumentNullException("func");
return answer.Id == 0 ? return default(T) : func(answer);
编辑:如果你想确保null
,你可以限制通用T
:
public static T GetNullIfNotSet<T>(this WillAnswer answer, Func<WillAnswer, T> func)
where T: class // no structs here
if (null == answer)
throw new ArgumentNullException("answer");
else if (null == func)
throw new ArgumentNullException("func");
return answer.Id == 0 ? return null : func(answer);
【讨论】:
我还建议将方法名称重命名为GetDefaultIfNotSet
之类的名称,因为语义已更改 — 无法为 bool
属性返回 null
值。
@Sergey Kolodiy:谢谢!我已经编辑了答案,GetDefaultIfNotSet
是一个更好的名字。
@Matas Vaitkevicius: null
是类的默认值;例如default(typeof(Object))
是 null
而 default(typeof(int))
是 0
@Matas Vaitkevicius:如果你想要 true、false、null(即 nullabe boolean i>) 看看Boolean?
; default(typeof(Boolean?))
为 null
@MatasVaitkevicius,正如@Dmitry 指出的那样,使用bool?
是更好的选择:使用带有bool?
通用参数的通用版本:var result = answer.GetDefaultIfNotSet<bool?>(o => o.HasBusinessAssets);
。【参考方案3】:
是的,你可以:
public static void LoadProperty<T>(this T t, Func<T, object> func) where T: Entity
你可以使用:
contact.LoadProperty(p => p.LogicalName);
【讨论】:
以上是关于接受 Expression<Func<T>> 表达式作为参数的扩展方法的主要内容,如果未能解决你的问题,请参考以下文章
组合表达式(Expression<Func<TIn,TOut>> 与 Expression<Func<TOut, bool>>)
C# 如何将 Expression<Func<SomeType>> 转换为 Expression<Func<OtherType>>
如何从 Expression<Func<MyClass, string>> 动态创建 Expression<Func<MyClass, bool>> 谓
Expression<Func<T,TResult>>和Func<T,TResult>