将List(of MyType)列表转换为IEnumerable(IEnumerable(MyType))

时间:2012-12-11 15:07:54

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

我有一个接受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中得到解决。

是否有任何想法可以避免此错误?

1 个答案:

答案 0 :(得分:2)

发生运行时错误,因为在.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