【问题标题】:Casting from IEnumerable<Object> to IEnumerable<string>从 IEnumerable<Object> 转换为 IEnumerable<string>
【发布时间】:2009-03-04 07:48:07
【问题描述】:

最近我在 c# 中发现了一个非常令人惊讶的行为。 我有一个将IEnumerable&lt;Object&gt; 作为参数的方法,我正在传递 IEnumerable&lt;string&gt; 但这是不可能的。 而在 c# 中,一切都可以向上转换为 Object,而不是为什么这是不可能的? 这对我来说完全令人困惑。 请有人在这个问题上澄清我。

【问题讨论】:

  • 怎么不可能?它会抛出 ArgumentException 吗?此外,如果要将通用 IEnumerable 声明为 Object,那么使用它有什么意义?

标签: c# covariance contravariance


【解决方案1】:

对此的技术术语是泛型在 C# 3.0 及更早版本中是不变的。从 C#4.0 开始,演员表工作。

不变的意思是两个泛型类型之间没有关系,仅仅因为它们的泛型类型参数是相关的(即是彼此的子类型或超类型)。

在您的示例中,IEnumerable&lt;object&gt;IEnumerable&lt;string&gt; 之间没有类型关系,因为字符串是对象的子类型。它们只是被认为是两种完全不相关的类型,比如字符串和整数(它们仍然都是对象的子类型,但一切都是)

对于您遇到的这个问题,有一些解决方法和例外情况。

首先,您可以将每个字符串单独转换为对象,如果您使用的是 .NET 3.0,则可以使用 Cast&lt;T&gt;() 扩展方法来实现。否则,您可以使用 foreach 并将结果放入您想要的静态类型的新变量中。

其次,数组是引用类型的一个例外,即将 string[] 类型传递给接受 object[] 类型的方法应该可以工作。

【讨论】:

  • 只是为了更新这个旧答案,C# 现在支持协变和逆变,现在这是一个有效的转换。
【解决方案2】:

正如其他人所指出的,泛型类型是不变的。 IEnumerable&lt;T&gt; 可以是协变体,但 C# 目前不支持指定变体。 C# 4.0 预计将支持变体,因此将来可能会支持。

要解决此问题,您现在可以使用 LINQ 扩展方法 Cast&lt;object&gt;()。假设您有一个名为Foo 的方法,它采用IEnumerable&lt;object&gt;&gt;。可以这样称呼,

Foo(stringEnumerable.Cast<object>());

【讨论】:

    【解决方案3】:

    IEnumerable&lt;string&gt; 传递给需要IEnumerable&lt;object&gt; 的函数的最简单方法是像这样转换函数:

    public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
        {
        foreach (T o in enumerable)
            yield return o;
        }
    

    当 C# 4 出现时,这将不是必需的,因为它支持协变和逆变。

    【讨论】:

    • 这正是 Cast 的用途:)
    【解决方案4】:

    你应该使用

    IEnumerable<T> 
    

    如果你想传入不同的类型,那么你可以查询 T 来找出它是什么类型。

    【讨论】:

      【解决方案5】:

      思考这个问题的一个好方法是问自己“如果你能做到这一点会发生什么?”。举个例子:

      IEnumerable<String> strings=...;
      IEnumerable<Object> objects = strings; // assume this were legal
      
      objects.Add(new Integer(5)); // what the...
      

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

      【讨论】:

      • IEnumerable 虽然没有 Add 方法...在 C# 4.0 中,您实际上可以执行此转换,因为声明将更改为 IEnumerable 允许它(安全地)逆变。
      • 那么替换任何具有 Add 方法的泛型类,例如 List
      • -1: IEnumerable&lt;T&gt; 在 .NET 4 中变为 IEnumerable&lt;out T&gt;,特别是因为您的论点对于容器有意义,但对于枚举而言不是
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-11
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多