Я читаю данные из пользовательского формата данных, который концептуально хранит данные в таблице. Каждый столбец может иметь отдельный тип. Типы зависят от формата файла и сопоставляются с типами C#.
У меня есть тип столбца, который инкапсулирует идею столбца, с универсальным параметром T, указывающим тип С#, который находится в столбце. Column.FormatType указывает тип с точки зрения типов формата. Итак, чтобы прочитать значение столбца, у меня есть простой метод:
protected T readColumnValue<T>(Column<T> column)
{
switch (column.FormatType)
{
case FormatType.Int:
return (T)readInt();
}
}
Как просто и изящно! Теперь все, что мне нужно сделать, это:
Column<int> column=new Column<int>(...)
int value=readColumnValue(column);
Приведенное выше приведение к типу T будет работать в Java (хотя и с предупреждением), и из-за стирания приведение не будет оцениваться до тех пор, пока значение не будет фактически использовано вызывающей стороной — в этот момент будет выброшено исключение ClassCastException, если приведение было неправильно.
Это не работает в С#. Однако, поскольку C# не отбрасывает универсальные типы, можно сделать его еще лучше! Кажется, я могу запросить тип T во время выполнения:
Type valueType=typeof(T);
Отлично, так что у меня есть тип значения, которое я буду возвращать. Что я могу с этим сделать? Если бы это была Java, потому что существует метод Class.Cast, который выполняет приведение во время выполнения, я был бы свободен дома! (Поскольку каждый класс класса Java имеет параметр универсального типа, указывающий, что класс также обеспечивает безопасность типов во время компиляции.) Нижеследующее из мира моей мечты, где класс C# Type работает как класс Java Class:
protected T readColumnValue<T>(Column<T> column)
{
Type<T> valueType=typeof(T);
switch (column.FormatType)
{
case FormatType.Int:
return valueType.Cast(readInt());
}
}
Очевидно, что Type.Cast() отсутствует --- так что мне делать?
(Да, я знаю, что есть метод Convert.ChangeType(), но он, похоже, выполняет преобразования, а не простое приведение типов.)
Обновление: кажется, что это просто невозможно без упаковки/распаковки с использованием (T)(object)readInt(). Но это неприемлемо. Эти файлы действительно большие, например, 80 МБ. Допустим, я хочу прочитать весь столбец значений. У меня был бы элегантный маленький метод, который использует дженерики и вызывает метод, описанный выше, следующим образом:
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
Упаковка/распаковка для миллионов значений? Это не звучит хорошо. Я нахожу абсурдным, что мне придется отказаться от дженериков и прибегнуть к readColumnInt(), readColumnFloat() и т. д. и воспроизвести весь этот код только для предотвращения упаковки/распаковки!
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
Это жалко. :(