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);
}
}
ois anIEnumerable<object[]>.conversion(o)is anIEnumerable<TSource>. You are converting a sequence of objects into a sequence ofTSourceitems.You then cast that
IEnumerable<TSource>toTSource. You’re basically saying, “Treat this sequence ofTSourceitems as a singleTSourceitem.” What the runtime is telling you is that, “I’m not allowed to treat that sequence of items as aTSourceitem, because that’s not what it is.What you almost certainly want to actually do is just replace the last line with:
You have a sequence of
TSourceitems, 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:
But why bother.