【问题标题】:Casting List(of List(of MyType)) to IEnumerable(of IEnumerable(of MyType))将 List(of List(of MyType)) 转换为 IEnumerable(of IEnumerable(of MyType))
【发布时间】:2012-11-29 03:26:36
【问题描述】:

我有一个接受 IEnumerable(Of IEnumerable(Of MyType)) 类型参数的方法

如果我执行以下操作:

Dim list1 as new List(Of MyType) From { obj1, obj2 }
Dim list2 as new List(Of MyType) From { obj3, obj4 }

MyMethod({ list1, list2 })

它有效。

如果我传递了List(Of List(Of MyType)),它会编译但会出现运行时错误,如下所示:

System.InvalidCastException: Unable to cast object of type
'System.Collections.Generic.List`1[System.Collections.Generic.List`1[MyType]]' to type
'System.Collections.Generic.IEnumerable`1[System.Collections.Generic.IEnumerable`1[MyType]]'

如果我传递了MyType()(),它会给出编译时错误,如下所示:

Value of type '2-dimensional array of MyType' cannot be converted to
'System.Collections.Generic.IEnumerable(Of System.Collections.Generic.IEnumerable(Of MyType))'

我目前使用的是 .net 3.5。

这似乎是一个类似于Casting List<MyObject> to IEnumerable<MyInterface> 的问题,我听说在 .net 4 中已解决。

有什么办法可以避免这个错误吗?

【问题讨论】:

    标签: c# vb.net .net-3.5 generic-collections generic-variance


    【解决方案1】:

    出现运行时错误,因为直到 .NET 4 才引入对泛型接口的差异支持。http://msdn.microsoft.com/en-us/library/dd233059.aspx

    一些选项: 您可以升级到 4(显然)。

    您可以将MyMethod 上的签名更改为始终期待List(Of List(Of T))

    您可以编写一个扩展方法来将List(Of List(Of T)) 转换为IEnumerable(Of IEnumerable(Of T))。但是,您可能希望在 C# 程序集中执行此操作,因此您可以利用 yield return。我想不出在 VB.Net 中处理它的好方法。

    诀窍是将需要从 List 隐式转换为 IEnumerable 的泛型维数降至 1。3.5 框架可以处理泛型类型的 1 维的隐式转换,但不能像您的示例中那样处理 2 .

    下面的例子:

    3.5 C# 项目:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication2Helpers
    {
        public static class My35Extensions
        {   
            public static  IEnumerable<IEnumerable<T>> ToIEnumerableOfIEnumerable<T>(this List<List<T>> value)
            {
                foreach (var v1 in value)
                {
                    yield return v1;
                }
            }
        }
    }
    

    3.5 VB.Net 项目基于您的原始示例:

    Imports System.Collections.Generic
    Imports System.Linq
    Imports System.Runtime.CompilerServices
    Imports ConsoleApplication2Helpers.My35Extensions
    
    
    Module Module1
    
        Sub Main()
            Dim obj1 As New MyType, obj2 As New MyType, obj3 As New MyType, obj4 As New MyType
            Dim list1 As New List(Of MyType) From {obj1, obj2}
            Dim list2 As New List(Of MyType) From {obj3, obj4}
            Dim arg1 = {list1, list2}
    
            ' Works in 3.5 and 4.  The non-generic array can be implicitly converted to IEnumerable.
            ' Then the framework only has one more dimension for the second IEnumerable conversion.
            ' The single dimension can convert implicitly in .NET 3.5 or 4.
            MyMethod(arg1)
    
    
            Dim arg2 As New List(Of List(Of MyType))
            arg2.Add(list1)
            arg2.Add(list2)
    
            ' Works in .NET 4 but NOT 3.5 because .NET Framework 4 introduces variance support for several existing generic interfaces.
            'MyMethod(arg2)
    
            ' Works in .NET 4 or 3.5.
            ' Uses custom extension method to implicitly convert the outer List<T> to IEnumerable<T>, so we can run in .NET 3.5.
            MyMethod(arg2.ToIEnumerableOfIEnumerable())
    
            Console.ReadKey()
        End Sub
    
        Sub MyMethod(value As IEnumerable(Of IEnumerable(Of MyType)))
            '
        End Sub
    
    End Module
    
    
    Public Class MyType
        Public Sub New()
    
        End Sub
    End Class
    

    【讨论】:

    • 感谢您的回复。顺便说一句,我真的不明白为什么 {list1, list2} 被接受。你能解释一下这种行为吗?
    • 另外,你能解释一下yield return吗?已经看过,但不知道如何以及为什么要使用它。
    • 感谢您的解释和代码!接受并获得赏金!
    猜你喜欢
    • 1970-01-01
    • 2015-09-04
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多