重载,泛型类型推断和'params'关键字

Tho*_*que 5 c# generics type-inference params-keyword overload-resolution

我刚注意到一个带有重载决策的奇怪行为.

假设我有以下方法:

public static void DoSomething<T>(IEnumerable<T> items)
{
    // Whatever

    // For debugging
    Console.WriteLine("DoSomething<T>(IEnumerable<T> items)");
}
Run Code Online (Sandbox Code Playgroud)

现在,我知道通常会使用少量显式参数调用此方法,因此为方便起见,我添加了此重载:

public static void DoSomething<T>(params T[] items)
{
    // Whatever

    // For debugging
    Console.WriteLine("DoSomething<T>(params T[] items)");
}
Run Code Online (Sandbox Code Playgroud)

现在我尝试调用这些方法:

var items = new List<string> { "foo", "bar" };
DoSomething(items);
DoSomething("foo", "bar");
Run Code Online (Sandbox Code Playgroud)

但在这两种情况下,params都会调用重载.我希望IEnumerable<T>在a的情况下调用重载List<T>,因为它似乎是一个更好的匹配(至少对我而言).

这种行为是否正常?谁能解释一下呢?我在MSDN文档中找不到任何关于它的明确信息......这里涉及的重载决策规则是什么?

Jon*_*eet 9

C#3.0规范的7.4.3节是相关的.基本上参数数组是扩展的,所以你要比较:

public static void DoSomething<T>(T item)
Run Code Online (Sandbox Code Playgroud)

public static void DoSomething<T>(IEnumerable<T> item)
Run Code Online (Sandbox Code Playgroud)

T用于第一匹配推断为List<string>T用于所述第二匹配推断为string.

现在考虑对涉及到的参数参数类型的转换-在第一个是List<string>List<string>; 第二是List<string>IEnumerable<string>.根据7.4.3.4中的规则,第一次转换比第二次转换更好.

反直觉位是类型推断.如果你把它排除在等式之外,它会像你期望的那样工作:

var items = new List<string> { "foo", "bar" };
DoSomething<string>(items);
DoSomething<string>("foo", "bar");
Run Code Online (Sandbox Code Playgroud)

此时,每次调用中只有一个适用的函数成员.

  • 我建议使用一个不同的名称,只是为了清楚 - 或者如果你只打算使用多个项目,你可以做`(先T,para T []其他) (4认同)
  • 好一个乔恩.这里要问的一个好问题是"如果没有IE <T>重载,你会发生什么?" 在这种情况下,显然最好的行为是推断T是List <string>.类型推断的一个重要设计原则是,无论其他候选者存在什么,它都会给出相同的结果.如果在添加新的重载时类型推断发生了变化,那就太奇怪了! (3认同)
  • 还有一个奇怪的例子可能会混淆这里的东西,那就是`DoSomething("foo")` - 在这种情况下它*看起来像一个明确的情况,只有参数数组重载才能起作用......但当然是` IEnumerable <T>`重载也适用,`T = char`.总是很容易错过:) (3认同)