Я читаю данные из пользовательского формата данных, который концептуально хранит данные в таблице. Каждый столбец может иметь отдельный тип. Типы зависят от формата файла и соответствуют типам C #.
У меня есть тип Column, который воплощает идею столбца, с общим параметром T, указывающим тип C #, который находится в столбце. 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 #. Однако, поскольку C # не выбрасывает универсальные типы, должно быть возможно сделать его еще лучше! Похоже, я могу запросить тип T во время выполнения:
Type valueType=typeof(T);
Отлично --- поэтому у меня есть тип значения, которое я буду возвращать. Что я могу с этим сделать? Если бы это была Java, потому что существует метод Class.Cast, который выполняет приведение во время выполнения, я был бы свободен дома! (Поскольку каждый класс Java-класса имеет параметр общего типа, указывающий на класс, он также обеспечивает безопасность типов во время компиляции.) Следующее из моей мечты, где класс C # Type работает подобно классу Java-класса:
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
Это жалко. (