21

假设我有一个不受约束的泛型方法,它适用于所有支持相等的类型。它执行成对相等检查,因此在O(n 2 )中工作:

public static int CountDuplicates<T>(IList<T> list) 
{
    /* ... */ 
}

我还有一个受约束的泛型方法,它只适用于支持排序的类型。它从在O(n log n)中对列表进行排序开始,然后一次计算所有重复项:

public static int CountDuplicatesFast<T>(IList<T> list) 
    where T : IComparable<T> 
{
    /* ... */ 
}

因此,如果静态知道列表的元素类型支持排序,则调用者可以选择调用快速方法。调用者本身可能会IList<T>在 T 不受约束的情况下使用泛型,因此它唯一的选择是调用第一个(慢)方法。

现在,我希望第一个方法在运行时检查该类型是否T实际实现了接口IComparable<T>,如果是,则调用快速方法:

public static int CountDuplicates<T>(IList<T> list)
{
    if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
    {
        return CountDuplicatesFast(list);
    }
    else
    {
        /* use the slow algorithm */
    }
}

问题是编译器拒绝调用CountDuplicatesFast(list)

错误 CS0314:类型“T”不能用作泛型类型或方法“Program.CountDuplicatesFast<T>(System.Collections.Generic.IList<T>)”中的类型参数“T”。没有从“T”到“System.IComparable<T>”的装箱转换或类型参数转换。

是否可以说服编译器相信我知道我在做什么,并跳过约束检查?

4

2 回答 2

8

您可以使用辅助类和dynamic类型来跳过编译时检查:

sealed class CountDuplicatesFastCaller
{
    public int Call<T>(IList<T> list) where T : IComparable<T>
    {
        return CountDuplicatesFast(list);
    }
}

public static int CountDuplicates<T>(IList<T> list)
{
    if (typeof (IComparable<T>).IsAssignableFrom(typeof (T)))
    {
        return ((dynamic) new CountDuplicatesFastCaller()).Call(list);
    }
    else
    {
        /* use the slow algorithm */
    }
}

由于 DLR 缓存机制,这应该比纯反射更快。

于 2013-05-06T19:31:50.473 回答
7

这是一种使用方法dynamic

if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
{
    return CountDuplicatesFast((dynamic)list);
}

或反射:

if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
{
    var method = typeof(MyType).GetMethod("CountDuplicatesFast");
    var generic = method.MakeGenericMethod(typeof(T));
    return (int)generic.Invoke(null, new object[] { list });
}

我不认为有办法静态地做到这一点(即没有反射或dynamic)。

于 2013-05-06T19:32:10.390 回答