Подтвердить что ты не робот

Как условно вызывать общий метод с ограничениями?

Предположим, что у меня есть неограниченный общий метод, который работает на всех типах, поддерживающих равенство. Он выполняет парные проверки равенства и поэтому работает в 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' не может использоваться как параметр типа 'T' в общем типе или методе 'Program.CountDuplicatesFast <T> (System.Collections.Generic.IList <T> )'. Нет никакого преобразования бокса или преобразования параметра типа из 'T' в 'System.IComparable <T> '.

Можно ли убедить компилятор доверять мне, что я знаю, что делаю, и пропустить проверку ограничений?

4b9b3361

Ответ 1

Здесь можно сделать это с помощью 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).

Ответ 2

Вы можете использовать класс-помощник и тип 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.