I'm coming to C# from a Java background and keep bumping in to the same sort of problem with generics that would be trivial to solve in Java.
Given the classes:
interface IUntypedField { }
class Field<TValue> : IUntypedField { }
interface IFieldMap
{
void Put<TValue>(Field<TValue> field, TValue value);
TValue Get<TValue>(Field<TValue> field);
}
I'd like to write something like:
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));
}
}
In Java this would simple to solve as the fields collection would be an Iterable<Field<?>>
and you could call Copy(Field, IFieldMap, IFieldMap)
directly.
In C# I find myself doing switch/cast for all possible values of TValue
(which smells horrifically, having to add a case for every type you add is clearly a bug waiting to happen, and only feasible if the set of types are finite):
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;
...
}
}
The other option I sometimes do is to move the functionality in to the Field class, which again, stinks. It's not the responsibility of the field. At least this avoids the huge switch:
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);
If you could write polymorphic extension methods (i.e. the Copy methods in IUntypedField and Field above) then you could at least keep the code in alongside the class whose responsibility it is.
Am I missing some feature of C# that would make this possible. Or is there some functional pattern that could be used? Any ideas?
(One last thing, I'm currently stuck on .Net 3.5, so can't make use of any covariance/contravariance, but would still be interested to know how they would help here, if at all).