鉴于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
?
答案 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
void MethodCaller<T> (IEnumerable<T> objects)
{
Method(objects.Cast<object>());
}