首页 文章

为什么在Func <T,T>和Action <T>之间没有't C#'的重载解析? [重复]

提问于
浏览
12

这个问题在这里已有答案:

因此,IEnumerable的一个相当常见的扩展方法,运行:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (var item in source)
    {
        action(item);
        yield return item;
    }
}

当我尝试使用它时,例如,DbSet.Add:

invoice.Items.Run(db.InvoiceItems.Add);
// NB: Add method signature is
// public T Add(T item) { ... }

...编译器抱怨它有错误的返回类型,因为它期望一个void方法 . 因此,为Run添加一个重载,它接受一个Func而不是Action:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Func<T, T> action)
{
    return source.Select(action).ToList().AsEnumerable();
}

现在编译器抱怨说“以下方法之间的调用是模糊的......”

所以我的问题是,当Run方法的Action重载对方法组无效时,怎么会导致歧义?

4 回答

  • 5

    这已经由Eric和Jon在this question的答案中解释过了 . 长话短说 - 这就是C#编译器的工作原理;确切地说,在处理方法组转换时,决定将转换为什么委托使用重载分辨率, does not take return types in account

    这里的原则是确定方法组可转换性需要使用重载决策从方法组中选择方法,并且重载决策不考虑返回类型 .

    在您的示例中,编译器将 Action<T>Func<T, T> 视为 Add 的最佳匹配 . 这增加了两种可能的选择,因为它需要一个 - 发出适当的错误 .

  • 0

    尝试以正确的方式过载:

    public static IEnumerable<TDest> Run<TSource, TDest>(this IEnumerable<TSource> source, 
        Func<TSource, TDest> action) 
    { 
     return source.Select(action).ToList(); 
    }
    
  • 0

    我无法回答为什么,但为了解决你可以明确表达你的功能的歧义:

    invoice.Items.Run((Func<T,T>)db.InvoiceItems.Add);
    

    或者使用lambda

    invoice.Items.Run(x => db.InvoiceItems.Add(x));
    
  • 0

    我不知道为什么它无法自动解决它,但这里有两个解决方法:

    // with T replaced with the actual type:
    invoice.Items.Run((Func<T, T>)db.InvoiceItems.Add);
    invoice.Items.Run(new Func<T, T>(db.InvoiceItems.Add));
    

    为什么你还需要这些方法?出了什么问题:

    foreach (var item in invoice.Items)
        db.InvoiceItems.Add(item);
    

    这个的可读性要好得多 . 除非你有充分的理由需要 Run 方法,我已经看到了,所以没有这样的理由,至少对于 Action<T> 重载 .

相关问题