AC # общая функция для любого контейнера

У меня есть удовольствие статический публичный IQueryable < TSource > OrderData < TSource , TKey > ( это IQueryable < TSource > источник , System . Linq . Выражения . Expression < Func < TSource , TKey >> keySelector , Sort . SortDirection SortDirection ) { если ( SortDirection == Сортировка . Сортировка . По возрастанию ) { return source . OrderBy < TSource , TKey > ( keySelector ); } else { return source . OrderByDescending < TSource , TKey > ( keySelector ); } }, который выглядит так:

static public C<TSource> OrderData<C, TSource, TKey>(this C<TSource> source,
    System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector,
    Sort.SortDirection sortDirection) where C : IEnumerable<TSource>

Теперь это было здорово, пока мне не нужно было делать то же самое для контейнера C. Я мог бы назвать это, бросая контейнер по пути внутрь и наружу, но мне было интересно, есть ли способ сделать сам контейнер универсальным параметром и все еще работать.

Мне хотелось что-то вроде:

IEnumerable<T>

Это не компилируется, давая фундаментальные ошибки, такие как «», «ожидаемый». Есть идеи?

c#,generics,containers,

0

Ответов: public static IEnumerable<TSource> OrderData<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Sort.SortDirection sortDirection) { return sortDirection == Sort.SortDirection.Ascending ? source.OrderBy(keySelector) : source.OrderByDescending(keySelector); }


3 принят

Нет, в C # нет способа выражения параметра типа, который сам по себе является произвольным типом, который должен быть общим с определенной arity. (Это похоже на то, что вы пытаетесь сделать C.)

Также стоит отметить, что вы, вероятно, не хотите иметь одну и ту же подпись, IQueryable<T>так как обычно вы работаете с делегатами, а не с деревьями выражений для запросов в процессе. Таким образом, вы бы

IEnumerable<T>

0

Tl; dr: вы всегда можете создать дешевую упаковку , что отлично и, вероятно, решит вашу проблему:IEnumerable<int> nums_enumerable = new[] { 1, 2, 3 }.AsEnumerable(); IQueryable<int> nums_queryable = nums_enumerable.AsQueryable();IEnumerable

dynamic

Длинная версия заключается в том, что ваша проблема показывает, почему интерфейсы существуют в первую очередь: сообщить компилятору, что если реализовать два объекта IEnumerable, то это контракт, с помощью которого они должны реализовать все методы в этом интерфейсе, и вызов этих методов выиграл «Ошибка во время выполнения.

То, что вы просите, - полностью игнорировать интерфейс, который называется утиным типом и на самом деле даже достижимо в C #, используя отражение или dynamicключевое слово DLR ( ключевое слово). Тем не менее, это действительно пахнет плохим дизайном, когда вам нужно использовать отражение, и вы вдруг потеряете все проверки времени и гарантии: ваш код, возможно, будет работать во время выполнения, может быть, нет. В этом случае это была бы плохая идея.

Примеры для утиного ввода обычно представляют собой простые варианты использования, но дело в том, что сигнатура метода еще должна соответствовать . В этом конкретном случае это еще хуже: разные методы в этом интерфейсе имеют одно и то же имя и, похоже, работают одинаково, они совершенно разные.

Сравните эти два случая, например:

  1. В public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)случае, мы звоним IEnumerable.Average. Параметр этого метода представляет собой анонимный метод , то есть метод в классе с генерируемым компилятором, который принимает параметр и возвращает значение, умноженное на 2. Метод фактически вызывает этот метод для каждого элемента в списке:double GetAverage(IEnumerable<int> items) { return items.Average(i => i * 2); }

    IQueryable<T>
  2. В public static double Average<TSource>(this IQueryable<TSource> source, Expression<Func<TSource,int>> selector)случае, мы звоним 2. Параметр этого метода представляет собой дерево выражений , то есть объект, содержащий информацию, которую мы хотим умножить на параметр 2. Он буквально содержит двоичное выражение типа, Multiplyкоторое ссылается на два других выражения (a ParameterExpressionи a ConstantExpression). Этот объект не выполняет никаких вычислений при передаче IQueryable.Average:

    double GetAverage(IQueryable<int> items)
    {
        return items.Average(i => i * 2);
    }
C #, родовые, контейнеры,
Похожие вопросы