[英]Generic parameter T not implicitly assignable to object — generic method calling nongeneric overloaded method
考慮到object
是C#最終基類,為什么System.Collections.Generic.IEnumerable<T>
不能分配給參數類型System.Collections.Generic.IEnumerable<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#假定我可能會傳遞與類型object
不兼容的<T>
-即使實際上所有object
都是object
?
讓我們在這里從方程式中排除過載解決方案。 這是關於一般差異 。 特別是,您期望從IEnumerable<T>
到IEnumerable<object>
進行隱式轉換。
那是行不通的,因為通用方差僅在已知類型實參為引用類型時才起作用。 從鏈接的文檔中:
方差僅適用於引用類型; 如果為變量類型參數指定值類型,則該類型參數對於生成的構造類型而言是不變的。
因此,例如,這很好:
IEnumerable<string> strings = ...;
IEnumerable<object> objects = strings;
但這失敗了:
IEnumerable<int> ints = ...;
IEnumerable<object> objects = ints;
在您的一般情況下, T
可以是任何類型, 包括value類型 。 這就是為什么它失敗了。 如果你限制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;
}
IEnumerable <object>不是IEnumerable <T>的超類(無論T是什么)。 您不能將第二個分配給第一種類型的變量,因為它們被認為是完全不同的。 您需要將IEnumerable的類型轉換為對象,然后才能獲得匹配的簽名,例如:
void MethodCaller<T> (IEnumerable<T> objects)
{
Method(objects.Cast<object>());
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.