Domanda

I have a method that accepts an argument of type IEnumerable(Of IEnumerable(Of MyType))

If I do the following:

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

MyMethod({ list1, list2 })

it works.

If I pass a List(Of List(Of MyType)) it compiles but gives runtime error, as following:

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]]'

If I pass a MyType()() it gives compile-time error, as following:

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

I'm currently using .net 3.5.

That seems to be an issue similar to Casting List<MyObject> to IEnumerable<MyInterface> which I heard is resolved in .net 4.

Any idea to avoid this error?

È stato utile?

Soluzione

The runtime error occurs, because variance support for generic interfaces isn't introduced until .NET 4. http://msdn.microsoft.com/en-us/library/dd233059.aspx

Some options: You could upgrade to 4 (obviously).

You could change the signature on MyMethod to always expect List(Of List(Of T)).

You could write an extension method to convert the List(Of List(Of T)) to IEnumerable(Of IEnumerable(Of T)). However, you're probably going to want to do it in a C# assembly, so you can take advantage of yield return. I can't think of a great way to handle it in VB.Net.

The trick is to get the number of dimensions of generics that need to be implicitly converted from List to IEnumerable down to 1. The 3.5 framework can handle the implicit conversions for 1 dimension of generic types, but not 2 as in your example.

Example below:

3.5 C# project:

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 project based on your original example:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top