Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to cast object of type '<>d__6' to type 'System.Object[]'

I'm trying to lazy load (extension with yield return) the rows in a 2D object array. I get the following error:

c# Unable to cast object of type '<>d__6' to type 'System.Object[]'.

The exception occurs on this line found in the Parse method:

yield return (TSource) conversion(o);

I don't understand why C# thinks the return value is <>d__6 instead of Object[]. I normally program in VB.NET so I don't think I'm understanding the nuances of C#. What am I doing wrong? I looked at the other similar questions/answers but am still confused.

public static IEnumerable<TSource> Parse<TSource>(this object[,] array
        , Func<IEnumerable<object[]>, IEnumerable<TSource>> conversion
        , int rowStart, int columnStart, int rowCount, int columnCount)
    {

        IEnumerable<object[]> o 
            = array.ForEachRow(rowStart, columnStart, rowCount, columnCount);

        yield return (TSource) conversion(o);

    }

ForEachRow method:

    public static IEnumerable<object[]> ForEachRow(this object[,] array, 
               int rowStart, int columnStart, int rowCount, int columnCount)
    {

        object[] array1d=new object[columnCount];
        for (int row = rowStart; row < rowCount; row++)
        {
            for (int column = columnStart; column < columnCount; column++)
            {
                array1d[column] = array[row, column];
            }
            yield return (object[]) array1d;
        }
    }

I know this question has been asked before, but the other answers didn't make sense to me unfortunately (I program in VB mostly).

Code you can use to compile and test (In VB.NET):

Basically I'm getting a 2D object array from Excel and wanting to put it in a class and use Linq to evaluate.

 Dim oData As Object(,) = {{"OrderDate", "Region", "Rep", "Item", "Units", "Unit Cost", "Total"} _
        , {New DateTime(2011, 1, 6), "East", "Jones", "Pencil", 95, 1.99, 189.05} _
        , {New DateTime(2011, 1, 23), "Central", "Kivell", "Binder", 50, 19.99, 999.5} _
        , {New DateTime(2011, 2, 9), "Central", "Jardine", "Pencil", 36, 4.99, 179.64} _
        , {New DateTime(2011, 2, 26), "Central", "Gill", "Pen", 27, 19.99, 539.73} _
        , {New DateTime(2011, 3, 15), "West", "Sorvino", "Pencil", 56, 2.99, 167.44} _
        }

    Dim clsSales = oData.Parse(Of SaleOrder)(Function(o As Object()) New SaleOrder( _
                                         If(IsDate(o(0)), o(0), #1/1/1900#) _
                                         , o(1).ToString _
                                         , o(2).ToString _
                                         , o(3).ToString _
                                         , If(IsNumeric(o(4)), CInt(o(4)), 0) _
                                         , If(IsNumeric(o(5)), o(5), 0) _
                                         ), 1, 0, oData.GetUpperBound(0), 6)

    For Each cls In clsSales
        Console.WriteLine(cls.ToString)
    Next

Where the class is:

 Class SaleOrder

Public Sub New(ByVal date_ As Date, ByVal region_ As String, ByVal rep As String, ByVal item_ As String, ByVal units As Integer _
               , ByVal cost As Double)

    OrderDate = date_
    Region = region_
    Representative = rep
    Item = item_
    UnitCount = units
    UnitCost = cost

End Sub

Public OrderDate As DateTime
Public Region As String
Public Representative As String
Public Item As String
Public UnitCount As Integer = 5
Public UnitCost As Double
Public ReadOnly Property Total() As Double
    Get
        Return UnitCount * UnitCost
    End Get
End Property

Public Overrides Function ToString() As String
    Return String.Format("{0} {1} {2} {3} {4} {5} {6}", OrderDate, Region, Representative, Item, UnitCount, UnitCost, Total)
End Function

End Class

Final Solution

    public static IEnumerable<TSource> Parse<TSource>(this object[,] array
        , Func<object[], TSource> conversion
        , int rowStart, int columnStart, int rowCount, int columnCount)
    {

        for (int row = rowStart; row < rowCount; row++)
        {
            object[] array1d = new object[columnCount];
            for (int column = columnStart; column < columnCount; column++)
            {
                array1d[column] = array[row, column];
            }
            yield return conversion(array1d);
        }

    }
like image 300
Jon49 Avatar asked Jan 14 '23 08:01

Jon49


1 Answers

With all the information in the comments it is now clear what is going on here. Let's make a much simpler repro:

public static IEnumerable<Tiger> Parse()
{
  object obj = ForEachRow();
  yield return (Tiger) obj;
}

public static IEnumerable<Tiger> ForEachRow()
{
  yield return new Tiger();
}

OK, what does the compiler do with the bottom method? It rewrites it like this:

class ForEachRowEnumerable : IEnumerable<Tiger>
{
    ... a class which implements an IEnumerable<Tiger> 
    that yields a single tiger...
}

public static IEnumerable<Tiger> ForEachRow()
{
  return new ForEachRowEnumerable();
}

So now what does the first method do?

You call ForEachRow. That returns a new ForEachRowEnumerable. You convert that to object. You then cast the object to Tiger. But ForEachRowEnumerable is not a tiger; it is a class that will give you a sequence of tigers. So the runtime gives you the error "cannot cast ForEachRowEnumerable to Tiger".

The C# compiler of course does not name that class "ForEachRowEnumerable". It names it <>d__6 to make sure that it is impossible for you to actually use that class by name.

like image 98
Eric Lippert Avatar answered Jan 23 '23 05:01

Eric Lippert