我是从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);
}
Run Code Online (Sandbox Code Playgroud)
我想写一些类似的东西:
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));
}
}
Run Code Online (Sandbox Code Playgroud)
在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, from, to); break;
case Type.Long: Copy((Field<long>)field, from, to); break;
...
}
}
Run Code Online (Sandbox Code Playgroud)
我有时做的另一个选择是将功能移到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);
Run Code Online (Sandbox Code Playgroud)
如果您可以编写多态扩展方法(即上面的IUntypedField和Field中的Copy方法),那么您至少可以将代码保留在其职责范围内.
我错过了C#的一些功能,可以实现这一点.或者是否有一些可以使用的功能模式?有任何想法吗?
(最后一件事,我目前仍然坚持使用.Net 3.5,所以不能使用任何协方差/逆变,但仍然有兴趣知道他们将如何在这里提供帮助,如果有的话).
这是一个完全类型安全的方法,它编译 lambda 来执行复制:
static class MapCopier
{
public static void Copy(IEnumerable<IUntypedField> fields, IFieldMap from, IFieldMap to)
{
foreach (var field in fields)
Copy(field, from, to);
}
// cache generated Copy lambdas
static Dictionary<Type, Action<IUntypedField, IFieldMap, IFieldMap>> copiers =
new Dictionary<Type, Action<IUntypedField, IFieldMap, IFieldMap>>();
// generate Copy lambda based on passed-in type
static void Copy(IUntypedField field, IFieldMap from, 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, 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, IFieldMap>>(
copierExp,
fieldParam,
fromParam,
toParam)
.Compile();
// add the compiled lambda to the cache
copiers[type] = copier;
}
// invoke the actual copy lambda
copier(field, from, to);
}
public static void Copy<TValue>(Field<TValue> field, IFieldMap from, IFieldMap to)
{
to.Put(field, from.Get(field));
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,此方法会即时创建复制方法,而不是调用该Copy<TValue>方法。这本质上是内联,并且由于没有额外的调用,每次调用节省了大约 50 纳秒。如果您要使该Copy方法更复杂,则调用它可能Copy比创建表达式树来内联它更容易。
| 归档时间: |
|
| 查看次数: |
1299 次 |
| 最近记录: |