質問

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);
        }

    }
役に立ちましたか?

解決 2

o is an IEnumerable<object[]>.

conversion(o) is an IEnumerable<TSource>. You are converting a sequence of objects into a sequence of TSource items.

You then cast that IEnumerable<TSource> to TSource. You're basically saying, "Treat this sequence of TSource items as a single TSource item." What the runtime is telling you is that, "I'm not allowed to treat that sequence of items as a TSource item, because that's not what it is.

What you almost certainly want to actually do is just replace the last line with:

return conversion(o);

You have a sequence of TSource items, and that's exactly what you need to return. You were overthinking it by trying to use an iterator block.

If you really, really want to use an iterator block, then you need to yield each item in the sequence, like this:

foreach (TSource item in conversion(o))
    yield return item;

But why bother.

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top