最近我在c#中发现了一个非常令人惊讶的行为。
我有一个方法,它以IEnumerable<Object>
为参数,我正在传递
IEnumerable<string>
,但这是不可能的。
虽然在c#中,所有内容都可以向上转换为对象而不是为什么这是不可能的?
这让我很困惑。
请有人在这个问题上告诉我。
答案 0 :(得分:11)
对此的技术术语是泛型在C#3.0及更早版本中是不变的。从C#4.0开始,演员工作。
什么是不变量意味着两个泛型类型之间没有关系,因为它们的泛型类型参数是相关的(即彼此的子类型或超类型)。
在您的示例中,IEnumerable<object>
和IEnumerable<string>
之间没有输入关系,因为string是object的子类型。它们只被认为是两个完全不相关的类型,比如字符串和int(它们仍然是对象的子类型,但一切都是)
您遇到此问题有一些解决方法和例外。
首先,您可以将每个字符串单独转换为对象,如果您使用的是.NET 3.0,则可以使用Cast<T>()
扩展方法执行此操作。否则,您可以使用foreach并将结果放入所需静态类型的新变量中。
其次,数组是引用类型的一个例外,即将string []类型传递给接受object []类型的方法应该有效。
答案 1 :(得分:8)
正如其他人所指出的那样,泛型类型是不变的。 IEnumerable<T>
可能是共变体,但C#目前不支持指定变体。预计C#4.0将支持变体,因此将来可能会支持这种变体。
要解决此问题,您现在可以使用LINQ扩展方法Cast<object>()
。假设您有一个名为Foo
的方法需要IEnumerable<object>>
。你可以这样称呼它,
Foo(stringEnumerable.Cast<object>());
答案 2 :(得分:3)
将IEnumerable<string>
传递给需要IEnumerable<object>
的函数的最简单方法是转换函数,如下所示:
public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
{
foreach (T o in enumerable)
yield return o;
}
当C#4出现时,这不是必要的,因为它将支持协方差和逆变。
答案 3 :(得分:0)
您应该使用
IEnumerable<T>
如果你想传递不同的类型,那么你可以查询T以找出它是什么类型。
答案 4 :(得分:0)
考虑这个问题的一个好方法就是问问自己“如果能做到这一点会怎么样?”。请看以下示例:
IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal
objects.Add(new Integer(5)); // what the...
我们刚刚在字符串列表中添加了一个整数。编译器这样做是为了保护类型安全。