Я перехожу к C # из опыта Java и постоянно сталкиваюсь с той же проблемой с дженериками, которую было бы тривиально решить на Java.
Учитывая классы:
interface IUntypedField { }
class Field<TValue> : IUntypedField { }
interface IFieldMap
{
void Put<TValue>(Field<TValue> field, TValue value);
TValue Get<TValue>(Field<TValue> field);
}
I ' Я бы хотел написать что-то вроде:
class MapCopier
{
void Copy(IEnumerable<IUntypedField> fields, IFieldMap from, IFieldMap to)
{
foreach (var field in fields)
Copy(field, from, to); // <-- clearly doesn't compile as field is IUntypedField not Field
}
void Copy<TValue>(Field<TValue> field, IFieldMap from, IFieldMap to)
{
to.Put(field, from.Get(field));
}
}
В Java это было бы просто решить, поскольку коллекция полей была бы Iterable
, и вы могли бы вызвать Copy (Field, IFieldMap, IFieldMap)
напрямую.
В C # я обнаружил, что выполняю переключение / приведение для всех возможных значений TValue
(что ужасно пахнет, необходимость добавлять регистр для каждого добавляемого вами типа явно является ошибкой ожидает, когда произойдет, и выполнимо только в том случае, если набор типов конечен):
foreach (var field in fields)
{
switch (field.Type) // added an enum to track the type of Field's parameterised type
{
case Type.Int: Copy((Field<int>)field, from, to); break;
case Type.Long: Copy((Field<long>)field, from, to); break;
...
}
}
Другой вариант, который я иногда делаю, - это переместить функциональность в класс Field, что опять же неприятно. Это не ответственность поля. По крайней мере, это позволяет избежать огромного переключения:
interface IUntypedField { void Copy(IFieldMap from, IFieldMap to); }
class Field<TValue> : IUntypedField
{
void Copy(IFieldMap from, IFieldMap to)
{
to.Put(this, from.Get(this));
}
}
...
foreach (var field in fields)
field.Copy(from, to);
Если бы вы могли написать методы полиморфного расширения (т.е. методы копирования в IUntypedField и Field выше), то вы могли бы, по крайней мере, сохранить код вместе с классом, за который он несет ответственность.
Am Мне не хватает какой-то функции C #, которая сделала бы это возможным. Или есть какой-то функциональный шаблон, который можно было бы использовать? Есть идеи?
(И последнее, я сейчас застрял на .Net 3.5, поэтому не могу использовать какие-либо ковариации / контравариантности, но мне все равно было бы интересно узнать, как они могут здесь помочь, если вообще) .