I'm reading data from a custom data format that conceptually stores data in a table. Each column can have a distinct type. The types are specific to the file format and map to C# types.
I have a Column type that encapsulates the idea of a column, with generic parameter T indicating the C# type that is in the column. The Column.FormatType indicates the type in terms of the format types. So to read a value for a column, I have a simple method:
protected T readColumnValue<T>(Column<T> column)
{
switch (column.FormatType)
{
case FormatType.Int:
return (T)readInt();
}
}
How simple and elegant! Now all I have to do is:
Column<int> column=new Column<int>(...)
int value=readColumnValue(column);
The above cast to type T would work in Java (albeit with a warning), and because of erasure the cast would not be evaluated until the value was actually used by the caller---at which point a ClassCastException would be thrown if the cast wasn't correct.
This doesn't work in C#. However, because C# doesn't throw away the generic types it should be possible to make it even better! I appears that I can ask for the type of T at runtime:
Type valueType=typeof(T);
Great---so I have the type of value that I'll be returning. What can I do with it? If this were Java, because there exists a Class.Cast method which performs a runtime cast, I would be home free! (Because each Java Class class has a generic type parameter indicating of the class is for it would also provide compile-time type safety.) The following is from my dream-world where C# Type class works like the Java Class class:
protected T readColumnValue<T>(Column<T> column)
{
Type<T> valueType=typeof(T);
switch (column.FormatType)
{
case FormatType.Int:
return valueType.Cast(readInt());
}
}
Obviously there is no Type.Cast()---so what do I do?
(Yes, I know there is a Convert.ChangeType() method, but that seems to perform conversions, not make a simple cast.)
Update: So it's seeming like this is simply not possible without boxing/unboxing using (T)(object)readInt(). But this is not acceptable. These files are really big---80MB, for example. Let's say I want to read an entire column of values. I'd have an elegant little method that uses generics and calls the method above like this:
public T[] readColumn<T>(Column<T> column, int rowStart, int rowEnd, T[] values)
{
... //seek to column start
for (int row = rowStart; row < rowEnd; ++row)
{
values[row - rowStart] = readColumnValue(column);
... //seek to next row
Boxing/unboxing for millions of values? That doesn't sound good. I find it absurd that I'm going to have to throw away generics and resort to readColumnInt(), readColumnFloat(), etc. and reproduce all this code just to prevent boxing/unboxing!
public int[] readColumnInt(Column<int> column, int rowStart, int rowEnd, int[] values)
{
... //seek to column start
for (int row = rowStart; row < rowEnd; ++row)
{
values[row - rowStart] = readInt();
... //seek to next row
public float[] readColumnFloat(Column<float> column, int rowStart, int rowEnd, float[] values)
{
... //seek to column start
for (int row = rowStart; row < rowEnd; ++row)
{
values[row - rowStart] = readFloat();
... //seek to next row
This is pitiful. :(