2013-05-06 2 views
19

Предположим, у меня есть неограниченный общий метод, который работает на всех типах, поддерживающих равенство. Он выполняет проверку равенства попарно и так работает в О (п):Как условно вызывать общий метод с ограничениями?

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

У меня также есть общий метод ограниченного, который работает только с типами поддержки сортировки. Она начинается с сортировки списка в O (п журнал п), а затем подсчитывает все дубликаты за один проход:

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: тип 'Т' не может быть использован в качестве параметра типа 'Т' в универсальном типе или методе «Program.CountDuplicatesFast <T> (System.Collections.Generic.IList < Т >). Нет преобразования преобразования бокса или преобразования параметров типа из «T» в «System.IComparable <T>».

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

+1

Вы пробовали использовать Cast? 'вернуть CountDuplicatesFast (list.Cast >() ToList().);' – Nevyn

+0

@Nevyn, который производит «тип 'System.IComparable ' не может быть использован в качестве параметра типа 'Т' в универсальном типе или метод 'UserQuery.MyType.CountDuplicatesFast (System.Collections.Generic.IList ). Нет никакого неявного преобразования ссылок из' System.IComparable 'to' System.IComparable > '. " –

+0

Интересно. Почти правильный путь, но полностью неправильное исполнение.Полагаю, мне нужно будет написать тестовую программу и попробовать несколько разных вещей. Я думаю, что актеры могли бы это сделать ... но я понятия не имею, как точно ... ну, для первой догадки это было не так уж далеко :-) – Nevyn

ответ

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).

+3

Nice! Я не думал о том, чтобы привести аргумент. Я думаю, что мой подход необходим только в редких случаях, когда есть параметр типа, но нет параметров значения. –

+0

@ Владимир Решетников хм .. как бы это помогло, даже тогда? Если класс-помощник не добавил фиктивный параметр, чтобы тип был выведен, я его не вижу. –

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.

Смежные вопросы