泛型参数T不能隐式分配给对象-泛型方法调用非泛型重载方法

时间:2019-06-04 16:05:12

标签: c# generics casting overloading

鉴于System.Collections.Generic.IEnumerable<T>是C#的最终基类,为什么System.Collections.Generic.IEnumerable<object>不能分配给参数类型object


在执行类似于以下代码的操作时,我偶然发现了这种好奇心。这是一个泛型方法,称为重载的非泛型方法。

void Main()
{
    List<object> objects = new List<object>();

    Method(objects); // This calls the method with IEnumerable, as expected

    MethodCaller(objects); 
}

void MethodCaller<T> (IEnumerable<T> objects) 
{
    Method(objects); // Calls method overload 2, with object as parameter - why?

    // Why is the following line required to call the method overload 1? 
    // Can't C# do this automatically, given that object is the ultimate base class for everything?
    IEnumerable<object> casted = (IEnumerable<object>) objects; 
    Method(casted); // Calls method overload 1
}

void Method (IEnumerable<object> param) 
{
    // Method overload 1
    Console.WriteLine("Method overload 1 - with IEnumerable<object> as parameter");
}

void Method (object param) 
{
    // Method overload 2
    Console.WriteLine("Method overload 2 - with object as parameter");
}

我不明白为什么通用方法不应调用第一个重载而不是第二个重载。我认为编译器应该可以说任何<T>都可以隐式转换为object,因此IEnumerable<T>应该可以隐式转换为IEnumerable<object>

换句话说:

IEnumerable<object> casted = (IEnumerable<object>) objects; 

为什么需要此行才能调用方法重载1?鉴于对象是最终的基类,C#不能自动做到这一点吗?

是不是因为C#假定我可能正在传递与类型<T>不兼容的object -即使实际上所有内容都是object

2 个答案:

答案 0 :(得分:5)

让我们从这里排除过载解决方案。这大约是generic variance。特别是,您期望从IEnumerable<T>IEnumerable<object>的隐式转换。

那是行不通的,因为通用方差仅在已知类型实参为引用类型时才起作用。从链接的文档中:

  

差异仅适用于引用类型;如果为变量类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。

例如,这很好:

IEnumerable<string> strings = ...;
IEnumerable<object> objects = strings;

但这失败了:

IEnumerable<int> ints = ...;
IEnumerable<object> objects = ints;

在一般情况下,T可以是任何类型,包括值类型。这就是为什么它失败了。如果使用T约束将where T : class约束为引用类型,就可以了。

具体来说,这是无效的:

static void Foo<T>(IEnumerable<T> ts)
{
    IEnumerable<object> objects = ts;
}

但这是有效的:

static void Foo<T>(IEnumerable<T> ts) where T : class
{
    IEnumerable<object> objects = ts;
}

答案 1 :(得分:-2)

IEnumerable <对象>不是IEnumerable 的超类(无论T是什么)。您不能将第二个分配给第一种类型的变量,因为它们被认为是完全不同的。您需要将IEnumerable的类型转换为对象,然后才能获得匹配的签名,例如:

void MethodCaller<T> (IEnumerable<T> objects) 
{
    Method(objects.Cast<object>()); 
}