我是从
Java背景来到C#并且继续遇到与Java一样简单易用的泛型问题.
鉴于课程:
interface IUntypedField { } class Field<TValue> : IUntypedField { } interface IFieldMap { void Put<TValue>(Field<TValue> field,TValue value); TValue Get<TValue>(Field<TValue> field); }
我想写点类似的东西:
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 to) { to.Put(field,from.Get(field)); } }
在Java中,这很容易解决,因为字段集合将是Iterable< Field<?>>你可以直接调用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,to); break; case Type.Long: Copy((Field<long>)field,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中的Copy方法),那么您至少可以将代码放在与其职责相关的类旁边.
我错过了C#的一些功能,可以实现这一目标.或者是否有一些可以使用的功能模式?有任何想法吗?
(最后一件事,我目前仍然坚持使用.Net 3.5,因此不能使用任何协方差/逆变,但仍然有兴趣了解它们如何在这里提供帮助,如果有的话).
解决方法
这是一个完全类型安全的方法,它编译lambda来执行复制:
static class MapCopier { public static void Copy(IEnumerable<IUntypedField> fields,to); } // cache generated Copy lambdas static Dictionary<Type,Action<IUntypedField,IFieldMap>> copiers = new Dictionary<Type,IFieldMap>>(); // generate Copy lambda based on passed-in type static void Copy(IUntypedField field,IFieldMap to) { // figure out what type we need to look up; // we know we have a Field<TValue>,so find TValue Type type = field.GetType().GetGenericArguments()[0]; Action<IUntypedField,IFieldMap> copier; if (!copiers.TryGetValue(type,out copier)) { // copier not found; create a lambda and compile it Type tFieldMap = typeof(IFieldMap); // create parameters to lambda ParameterExpression fieldParam = Expression.Parameter(typeof(IUntypedField)),fromParam = Expression.Parameter(tFieldMap),toParam = Expression.Parameter(tFieldMap); // create expression for "(Field<TValue>)field" var converter = Expression.Convert(fieldParam,field.GetType()); // create expression for "to.Put(field,from.Get(field))" var copierExp = Expression.Call( toParam,tFieldMap.GetMethod("Put").MakeGenericMethod(type),converter,Expression.Call( fromParam,tFieldMap.GetMethod("Get").MakeGenericMethod(type),converter)); // create our lambda and compile it copier = Expression.Lambda<Action<IUntypedField,IFieldMap>>( copierExp,fieldParam,fromParam,toParam) .Compile(); // add the compiled lambda to the cache copiers[type] = copier; } // invoke the actual copy lambda copier(field,to); } public static void Copy<TValue>(Field<TValue> field,from.Get(field)); } }
请注意,此方法即时创建复制方法,而不是调用Copy< TValue>方法.这基本上是内联的,并且通过不进行额外呼叫来节省大约50ns的每次呼叫.如果要使Copy方法更复杂,可能更容易调用Copy而不是创建表达式树来内联它.