接受 Expression<Func<T>> 表达式作为参数的扩展方法

Posted

技术标签:

【中文标题】接受 Expression<Func<T>> 表达式作为参数的扩展方法【英文标题】:Extension method that accepts Expression<Func<T>> expression as parameter 【发布时间】:2016-01-12 06:57:14 【问题描述】:

我正在使用 .NET4.5C# 我想创建扩展方法,允许我传递对象的属性,如果该对象的 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 网站

这让我皱起了眉头,因为我认为我没有传递任何论据(是吗?)。能否请比我聪明的人解释一下我的哪些期望是错误的。

以防万一我不清楚,我会重申。 我想要的是能够打电话 如果Idanswer0,则var emptyIfNewObject = answer.GetNullIfNotSet(o =&gt; o.HasBusinessAssets) 并得到null

【问题讨论】:

正如其他人指出的那样,您不需要表达式。你的错误的根源是你定义你的 func 错误的事实。您可能想要 Func 之类的东西(对象是返回类型)。 这样称呼它:answer.GetNullIfNotSet(() =&gt; (((Func&lt;WillAnswer&gt;)(() =&gt; answer.HasBusinessAssets))())); 真是一团糟!回到haim770的答案:-) 如果您在调用方法时有权访问要访问其属性的对象,因为它是对象本身的扩展,那么为什么不只传递属性而不是传递Func? 【参考方案1】:

根本不需要Expression,只需使用Func&lt;WillAnswer, TProp&gt;

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&lt;T&gt;

var emptyIfNewObject = (bool?)answer.GetNullIfNotSet(o => o.HasBusinessAssets)

这不太理想。

【讨论】:

很好的答案,但是,我如何让它返回 null? 为了始终返回null,编译器必须确保传递的属性x =&gt; x.SomeProp 是引用类型。这可以通过添加通用约束来完成,但这意味着您将无法返回x =&gt; x.SomeInt(例如)。这是你需要的吗? 您能否在回答时稍微改变一下而不是 TProp return object 这允许返回 null public static object GetNullIfNotSet&lt;TProp&gt;(this WillAnswer answer, Func&lt;WillAnswer, TProp&gt; func) if (answer.Id == 0) return null; return func(answer); 非常感谢。我很欣赏第二种解决方案并不那么优雅,但是一些用户会发现它很有用。再次感谢。 @MatasVaitkevicius - 在这些情况下,我通常会编写两种方法 - 一种用于引用类型 TProp GetNullIfNotSet&lt;TProp&gt;(…) where TProp:class,另一种用于值类型 TProp? GetNullableIfNotSet&lt;TProp&gt;(…) where TProp:struct【参考方案2】:

看来您需要Func&lt;WillAnswer, T&gt; 而不是表达式

  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))nulldefault(typeof(int))0 @Matas Vaitkevicius:如果你想要 truefalsenull(即 nullabe boolean i>) 看看Boolean?default(typeof(Boolean?))null @MatasVaitkevicius,正如@Dmitry 指出的那样,使用bool? 是更好的选择:使用带有bool? 通用参数的通用版本:var result = answer.GetDefaultIfNotSet&lt;bool?&gt;(o =&gt; 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>

Func<T> 如何隐式转换为 Expression<Func<T>>?

Expression<Func<T,TResult>>和Func<T,TResult> 与AOP与WCF