C #: как реализовать IOrderedEnumerable <T> - PullRequest
13 голосов
/ 05 августа 2009

Я хочу реализовать на практике несколько различных алгоритмов, просто чтобы увидеть, насколько я на самом деле плох и поправиться: p

В любом случае, я подумал, что попытаюсь использовать IEnumerable<T> и IOrderedEnumerable<T> и другие типы коллекций .Net только для совместимости (так, чтобы то, что я пишу, можно было использовать позже).

Но я не могу найти способ вернуть экземпляр IOrderedEnumerable<T>, кроме использования методов расширения OrderBy и ThenBy. Поэтому я думаю, что мне нужно создать свой собственный класс, который реализует этот интерфейс. Но интерфейс не совсем имеет смысла, если честно. Возможно, но я не уверен.

Я создал пустой класс, добавил интерфейс и затем заставил ReSharper добавить для меня пустые реализации. Это выглядит так:

class MyOrderedEnumerable<T> : IOrderedEnumerable<T>
{
    /// <summary>
    /// Performs a subsequent ordering on the elements of an <see cref="T:System.Linq.IOrderedEnumerable`1"/> according to a key.
    /// </summary>
    /// <returns>
    /// An <see cref="T:System.Linq.IOrderedEnumerable`1"/> whose elements are sorted according to a key.
    /// </returns>
    /// <param name="keySelector">The <see cref="T:System.Func`2"/> used to extract the key for each element.</param><param name="comparer">The <see cref="T:System.Collections.Generic.IComparer`1"/> used to compare keys for placement in the returned sequence.</param><param name="descending">true to sort the elements in descending order; false to sort the elements in ascending order.</param><typeparam name="TKey">The type of the key produced by <paramref name="keySelector"/>.</typeparam><filterpriority>2</filterpriority>
    public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Returns an enumerator that iterates through the collection.
    /// </summary>
    /// <returns>
    /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
    /// </returns>
    /// <filterpriority>1</filterpriority>
    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Returns an enumerator that iterates through a collection.
    /// </summary>
    /// <returns>
    /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
    /// </returns>
    /// <filterpriority>2</filterpriority>
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Что я не понимаю, так это метод CreateOrderedEnumerable. Что именно это должно делать? Ну, я думаю, это, конечно, создаст перечисляемый порядок, но как? Должен ли сам алгоритм сортировки входить туда? И что это будет сортировать? В этот метод не входит коллекция элементов, так где же предполагается получить коллекцию на заказ? Как бы вы использовали класс? Он предназначен для реализации в качестве, например, частного вспомогательного класса внутри чего-то, что должно сортировать вещи?

Тогда вместо MyOrderedEnumerable<T> : IOrderedEnumerable<T> у вас может быть QuickSorter<T> : IOrderedEnumerable<T>, который берет коллекцию в своем конструкторе и сортирует ее при вызове этого метода CreateOrderedEnumerable ... но что тогда произойдет, если кто-то вызовет GetEnumerator и начал перечислять до того, как этот метод был вызван?


Ха-ха, только что обнаружил, что недавно спросил нечто подобное здесь . Но это было примерно, если бы можно было вернуть один. Так что я думаю, что этот вопрос является ответом на один ответ, который я получил там =)

Ответы [ 2 ]

12 голосов
/ 05 августа 2009

У меня есть пример реализации , на который вы можете посмотреть. Он не рассчитан на то, чтобы быть эффективным, но должен помочь вам начать работу.

Как правило, IOrderedEnumerable<T> просто необходимо иметь представление о его текущем порядке, чтобы он мог создать новый. Предполагая, что у вас уже есть IComparer<T>, вы создаете новый, говоря что-то вроде:

int Compare(T first, T second)
{
    if (baseComparer != null)
    {
        int baseResult = baseComparer.Compare(first, second);
        if (baseResult != 0)
        {
            return baseResult;
        }
    }
    TKey firstKey = keySelector(first);
    TKey secondKey = keySelector(second);

    return comparer.Compare(firstKey, secondKey);        
}

Таким образом, в основном вы создаете цепочку сравнения, начиная от «наименее значимого» до «наиболее значимого». Вам также нужно добавить туда нисходящий бит, но это легко:)

В приведенном выше примере три различных аспекта представлены в трех различных классах, уже представленных в MiscUtil :

  • ReverseComparer: отменяет существующие результаты IComparer<T>
  • LinkedComparer: создает один компаратор из двух, с одним ведущим и одним подчиненным
  • ProjectionComparer: создание компаратора на основе проекции исходных элементов на ключи, делегирование другому компаратору для сравнения этих ключей.

Сравнивающие устройства отлично подходят для такой цепочки.

0 голосов
/ 04 марта 2012

Предположительно, у вашего класса будет некоторая внутренняя переменная хранения, которая реализует IEnumerable (например, List<T>).Реализация этого метода проста в таком случае:

private List<T> data = new List<T>();

public IOrderedEnumerable<CalculationResult> CreateOrderedEnumerable<TKey>(Func<CalculationResult, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
  return descending ? 
      data.OrderByDescending(keySelector, comparer) 
    : data.OrderBy(keySelector, comparer);
}
...