【问题标题】:IEnumerable<T> conversionIEnumerable<T> 转换
【发布时间】:2010-12-06 21:17:35
【问题描述】:

鉴于以下情况:


class Base<T> {/*...*/}
class Der<T>: Base<T> {/*...*/}

interface Sth<T>{
  IEnumerable<Base<T>> Foo {get;}
}

// and implementation...
class Impl<T>: Sth<T> {
  public IEnumerable<Base<T>> Foo {
    get {
      return new List<Der<T>>();
    }
  }
}

我怎样才能让它编译?显然,错误不是从 List> 到 List> 的隐式转换。如果我明确地转换它,就会发生 InvalidCastException。

【问题讨论】:

标签: c#


【解决方案1】:

.NET Framework 3.5 版不支持您尝试进行的转换。由于添加了generic covariance/contravariance,它将在版本 4.0 (Visual Studio 2010) 中得到支持。虽然这仍然不允许您将List&lt;Der&gt; 转换为List&lt;Base&gt;,但它允许您将IEnumerator&lt;Der&gt;(列表实现)转换为IEnumerator&lt;Base&gt;

与此同时,您可以编写自己的类来实现IEnumerable&lt;Base&gt; 并返回一个自定义的IEnumerator&lt;Base&gt;,它简单地包装了List&lt;Der&gt;IEnumerator&lt;Der&gt;。或者,如果您使用的是 .NET Framework 3.5 版,则可以使用 Cast extension method,正如其他人所建议的那样。

【讨论】:

  • 请注意,泛型变量不允许将 List 强制转换为 List,因为 List 具有类型 T 的 in 和 out 成员。(它 使用 IEnumerable 代替 IEnumerable。)
  • 是的,但是,如果我理解正确,它将允许编译 OP 的代码示例。 List&lt;Der&gt; 实现了IEnumerable&lt;Der&gt;,可以转换为IEnumerable&lt;Base&gt;
  • 当然,我相信;我之所以提到它,是因为在问题文本中他提到了列表类型之间的隐式转换,所以我想澄清一下,您的评论适用于他的代码(使用 IEnumerable),而不是他的补充评论(引用了 List)。跨度>
  • 这是一个公平的观点。我更新了我的答案以(希望)澄清一下。
  • 感谢您的解释。我刚刚习惯了 C++,很多事情都刚刚好 :]]
【解决方案2】:

List&lt;Der&lt;T&gt;&gt; 不能转换为 List&lt;Base&lt;T&gt;&gt;,因为后者可以添加 Base&lt;T&gt; 而前者不能。

您可以使用 Cast 扩展方法解决此问题:return new List&lt;Der&lt;T&gt;&gt;().Cast&lt;Base&lt;T&gt;&gt;();

【讨论】:

    【解决方案3】:

    让它编译...

    class Impl<T> : Sth<T>
    {
        public IEnumerable<Base<T>> Foo
        {
            get
            {
                return new List<Base<T>>(); //change the List type to Base here
            }
        }
    } 
    

    你也可以做这样的事情,它会从 Der 的实现中返回 Base 类的 IEnumerable

    class Impl<T> : Sth<T>
    {
        public IEnumerable<Base<T>> Foo
        {
            get
            {
                List<Der<T>> x = new List<Der<T>>();
    
                foreach (Der<T> dt in x)
                {
                    yield return dt;
                }
            }
        }
    } 
    

    【讨论】:

      【解决方案4】:

      您可以使用 LINQ 将 List&lt;Der&lt;T&gt;&gt; 转换为 IEnumerable&lt;Base&lt;T&gt;&gt;,,方法是:

      class Impl<T>: Sth<T>
      {
          public IEnumerable<Base<T>> Foo
          {
              get
              {
                  return new List<Der<T>>().Cast<Base<T>>();
              }
          }
      }
      

      正如其他答案所述,v3.5 不支持泛型协变,但您可以使用 LINQ 创建实现 IEnumerable&lt;Base&lt;T&gt;&gt;. 的包装器对象

      【讨论】:

      • 谢谢,有帮助。演员表会减慢多少应用程序?它对性能有很大影响吗?
      • 拿出一个秒表,试几百万次,然后你就知道了。您是唯一有权访问您的应用程序的计时数据的人;我们无法回答这个问题。
      • 我从来没有专门检查过开销,但是当你转换为基本类型时(而不是实际将对象转换为不同的类型,或者装箱值类型)我不会认为这是昂贵的代码。但埃里克是对的;我们无法判断您的整个应用程序将如何反应;如果你投一个包含 1000 亿个项目的列表,则需要一段时间才能将它们全部投出:-D
      • 另外,在性能方面,请记住 LINQ 是惰性求值的;这意味着当您调用 Cast 时不会执行转换操作,而是在您遍历返回的 IEnumerable 时执行。这可能发生在 foreach 中(在这种情况下,项目通过 foreach 在每个循环上单独投射,分散“负载”)或者当您使用 ToArray 或 ToList 将 IEnumerable 转换为更具体的类型时。这也意味着如果您迭代 IEnumerable 两次(例如,两个单独的 foreach 循环),您将执行两次整个强制转换操作。只是需要注意的事情。
      【解决方案5】:

      这些答案帮助了我,我只是想针对我遇到的类似问题发布我的解决方案。

      我为 IEnumerable 编写了一个扩展方法,当我想将 List 转换为 IEnumerable 时,它会自动转换 TSource(我仍在使用 3.5)。

      public static SpecStatus Evaluate<TSource, TSpecSource>(this IEnumerable<TSource> source, ISpec<IEnumerable<TSpecSource>> spec)
              where TSource : TSpecSource
      {
          return spec.Evaluate(source.Cast<TSpecSource>());
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-24
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        • 2018-08-04
        相关资源
        最近更新 更多