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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With