【问题标题】:Is it possible to reuse a generic type to call a method with additional constraints?是否可以重用泛型类型来调用具有附加约束的方法?
【发布时间】:2011-02-14 16:19:00
【问题描述】:

示例 1

假设一个方法决定应该使用哪种方法来转换一个值。

public static TTarget ConvertValue<TTarget>(object value)
{
    Type t = typeof(TTarget);
    if (t.IsEnum)
        return ParseEnum<TTarget>(value);
    else //if ...
        return ...;
}

一些处理值的方法有一个带有约束的泛型类型参数。

public static TEnum ParseEnum<TEnum>(object value)
    where TEnum : struct
{
    //do something
    return ...;
}

编译器不允许这种方法,因为TTarget 类型不一定是struct,可以变成NULL,因此不能用作TEnum

示例 2

假设有一个没有约束的泛型方法和一个有附加约束的方法:

public void DoStuff<T>(T obj)
{
    if (obj is IComparable && obj is ICloneable)
        DoSpecialStuff<T>(obj);
}
public void DoSpecialStuff<T>(T obj)
    where T : IComparable, ICloneable
{
}

这也不起作用,因为 (afaik) 无法转换为多个接口。

是否可以重用泛型类型来调用具有附加约束的方法?

【问题讨论】:

标签: c# enums constraints generics


【解决方案1】:

您需要使用反射调用该方法。
没有更好的办法。

您应该考虑改为调用非泛型方法(并将typeof(TTarget) 作为参数传递)—ParseEnum 不需要是泛型的。

【讨论】:

    【解决方案2】:

    As SLaks mentions,实现这一点的唯一方法是使用反射。这是一种方法。 (委托被缓存在字典中,因此对同一类型的后续调用不需要反射。)

    public static TTarget ConvertValue<TTarget>(this object value)
    {
        Type t = typeof(TTarget);
    
        if (t.IsEnum)
        {
            Delegate del = _delegateCache.GetOrAdd(t, t2 =>
                Delegate.CreateDelegate(typeof(Func<object, TTarget>),
                                        _parseEnumInfo.MakeGenericMethod(t2));
            return ((Func<object, TTarget>)del)(value);
        }
        else // if ...
            return ...;
    }
    
    private static readonly MethodInfo _parseEnumInfo =
        typeof(YourParentClass).GetMethod("ParseEnum");
    
    private static readonly ConcurrentDictionary<Type, Delegate> _delegateCache =
        new ConcurrentDictionary<Type, Delegate>();
    
    public static TEnum ParseEnum<TEnum>(object value)
        where TEnum : struct, IComparable, IConvertible, IFormattable
    {
        // do something
        return ...;
    }
    

    或者,为了匹配你的第二个例子:

    public void DoStuff<T>(T obj)
    {
        if ((obj is IComparable) && (obj is ICloneable))
        {
            Delegate del = _delegateCache.GetOrAdd(typeof(T), t =>
                Delegate.CreateDelegate(typeof(Action<T>),
                                        this,
                                        _doSpecialStuffInfo.MakeGenericMethod(t));
            ((Action<T>)del)(obj);
        }
    }
    
    private static readonly MethodInfo _doSpecialStuffInfo =
        typeof(YourParentClass).GetMethod("DoSpecialStuff");
    
    private readonly ConcurrentDictionary<Type, Delegate> _delegateCache =
        new ConcurrentDictionary<Type, Delegate>();
    
    public void DoSpecialStuff<T>(T obj)
        where T : IComparable, ICloneable
    {
    }
    

    【讨论】:

    • stackoverflow.com/questions/686630/… - 应该比 ConcurrentDictionary 更快。
    • @SLaks:很好。我以前见过这样做 - 甚至可能我自己也使用过,我不记得了 - 但在写下这个答案时我完全忘记了它。这绝对是处理缓存的更好方法。
    【解决方案3】:

    如果您为每个感兴趣的接口定义一个带有“额外”通用参数 T 的版本,它继承了通用接口和 ISelf(我建议接口包含单个只读属性“self” , 类型 T),并且如果每个感兴趣的类都实现 ISelf,那么如果例如IFoo 继承自 IFoo 和 T,需要一些东西来实现 IFoo 和 IBar 的例程可以接受 IFoo 类型的参数。如果传入的参数是X,X会实现IFoo,X.Self会实现IBar。这种方法的美妙之处在于,任何实现以这种方式定义的接口的任何组合的对象都可以被类型转换为将以任何顺序实现这些接口的任何组合的东西。这种方法的一个缺点是它假定对象将定义 ISelf.Self 以返回自己,但不能真正保证这样的对象不会返回其他东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-28
      • 1970-01-01
      • 1970-01-01
      • 2015-05-02
      • 1970-01-01
      • 2018-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多