使用IList <t>和参数T [] </t>键入推断

时间:2014-03-03 04:16:56

标签: c#

这是关于类型推断的编译器选择的另一个问题。

在此代码段中,您可以看到接受DoStuff的{​​{1}}方法正在创建实现params[]的{​​{1}}。但输出是对List<T>方法的递归调用,接受IList<T>,最终导致堆栈溢出。

我知道这里明显的解决方案是简单地将DoStuff声明为params,但我想知道为什么编译器会选择接受set的{​​{1}}方法。< / p>

IList<T>

输出:

DoStuff

3 个答案:

答案 0 :(得分:7)

我们可以为您的问题制作更简单的复制品。

class Animal {}
class Tiger : Animal {}
...
static void M(Animal animal) {}
static void M<T>(params T[] items) {}
...
M(new Tiger());
选择

我们有两个选择M(Animal)和params方法的扩展形式:M<Tiger>(Tiger)。前者要求将老虎转变为动物;后者与参数类型完全匹配。因此选择后者。

现在假设我们有

Animal animal = new Tiger();
M(animal);

现在会发生什么?

我们有两个选择。 M(Animal)以及params方法的扩展形式:M<Animal>(Animal)。形式参数类型是相同的,都是完全匹配。编译器回到了一个有争议的回合:如果其中一个是“自然的”,而其中一个由于通用替换而只有Animal,则自然获胜。所以在这种情况下,前者会赢。

答案 1 :(得分:2)

编译器总是选择更接近的方法Closer is better

DoStuff<T>是通用的,这意味着它可以适用于任何类型,并且当特定类型(在这种情况下为List<T>)没有重载时它更接近,因此编译器选择DoStuff<T>(params T[] items)DoStuff<T>(IList<T> set)

我相信编译器会在没有隐式转换时更接近,在这种情况下将List<T>转换为IList<T>编译器需要隐式转换,但DoStuff<T>(params T[] items)已经是通用的可以采取任何类型,不需要转换,因此编译器很乐意选择此重载。

您可以通过声明void DoStuff<T>(List<T> set)或将变量声明为IList<T> set来解决此问题。

P.S:我可能错了,这是我对编译器的假设,可能会出现Eric Lippert并清除混乱:)

答案 2 :(得分:-3)

这是因为“a”,“b”参数自动创建为2个元素的字符串数组,因为它们可以转换为params T []项,因此第二个DoStuff签名是匹配的。

请参阅:MSDN上的参数数组以获取更多信息。