从IEnumerable <object>转换为IEnumerable <string> </string> </object>

时间:2009-03-04 07:48:07

标签: c# covariance contravariance

最近我在c#中发现了一个非常令人惊讶的行为。 我有一个方法,它以IEnumerable<Object>为参数,我正在传递 IEnumerable<string>,但这是不可能的。 虽然在c#中,所有内容都可以向上转换为对象而不是为什么这是不可能的? 这让我很困惑。 请有人在这个问题上告诉我。

5 个答案:

答案 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...

我们刚刚在字符串列表中添加了一个整数。编译器这样做是为了保护类型安全。