C#泛型集合

我来自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 from, 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, 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中的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));
    }
}

请注意,此方法即时创建复制方法,而不是调用Copy<TValue>方法。 这基本上是内联,并且由于没有额外的呼叫,每次通话可节省约50ns。 如果要使Copy方法更复杂,可能更容易调用Copy而不是创建表达式树来内联它。


至少可以使用反射来避免switch { } 。 在这种情况下,来自fw 4.0的协变/逆变将无济于事。 也许可以从使用dynamic关键字中获益。

// untested, just demonstates concept
class MapCopier
{
    private static void GenericCopy<TValue>(Field<TValue> field, IFieldMap from, IFieldMap to)
    {
        to.Put(field, from.Get(field));
    }

    public void Copy(IEnumerable<IUntypedField> fields, IFieldMap from, IFieldMap to)
    {
        var genericMethod = typeof(MapCopier).GetMethod("GenericCopy");
        foreach(var field in fields)
        {
            var type = field.GetType().GetGenericArguments()[0];
            var method = genericMethod.MakeGenericMethod(type);
            method.Invoke(null, new object[] { field, from, to });
        }
    }
}

由于Java只能通过类型擦除才能避免这种情况,所以为什么不使用非通用版本来模拟擦除呢? 这是一个例子:

interface IUntypedField { }
class Field<TValue> : IUntypedField { }

interface IFieldMap
{
    void Put<TValue>(Field<TValue> field, TValue value);
    TValue Get<TValue>(Field<TValue> field);
    void PutObject(IUntypedField field, object value); // <-- this has a cast
                                                 // to TValue for type safety
    object GetObject(IUntypedField field);
}

class MapCopier
{
    void Copy(IEnumerable<IUntypedField> fields, IFieldMap from, IFieldMap to)
    {
        foreach (var field in fields)
            Copy(field, from, to); // <-- now compiles because
                                   // it calls non-generic overload
    }

    // non-generic overload of Copy
    void Copy(IUntypedField field, IFieldMap from, IFieldMap to)
    {
        to.PutObject(field, from.GetObject(field));
    }

    void Copy<TValue>(Field<TValue> field, IFieldMap from, IFieldMap to)
    {
        to.Put(field, from.Get(field));
    }
}
链接地址: http://www.djcxy.com/p/49753.html

上一篇: C# generic collections

下一篇: Error using jQuery UI Selectmenu plugin in IE