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

С#: Как реализовать IOrderedEnumerable <T>

Я хочу реализовать некоторые различные алгоритмы для практики, просто чтобы понять, насколько я плох, и чтобы стать лучше: 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 и начал перечислять до того, как этот метод был вызван?


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

4b9b3361

Ответ 1

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

По сути, 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: создает компаратор на основе проекции исходных элементов на ключи, делегируя другому компаратору для сравнения этих ключей.

Сравнители отлично подходят для создания цепочки.

Ответ 2

Предположительно, ваш класс будет иметь некоторую внутреннюю переменную хранения, которая реализует 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);
}