Використання лямбда-виразу замість аргументу IComparer


78

Чи можливо за допомогою C # передавати лямбда-вираз як аргумент IComparer у виклику методу?

наприклад щось подібне

var x = someIEnumerable.OrderBy(aClass e => e.someProperty, 
(aClass x, aClass y) => 
  x.someProperty > y.SomeProperty ?  1 : x.someProperty < y.SomeProperty ?  -1 : 0);

Я не можу змусити це скомпілювати, тому я здогадуюсь, що ні, але, здається, така очевидна синергія між лямбдами та анонімними делегатами, що я відчуваю, що, мабуть, роблю щось дурне неправильно.

ТІА


1
Тут можлива відповідь: stackoverflow.com/questions/9824435/…
Кріс Мантл,

Відповіді:


66

Як зазначає Джеппе, якщо ви використовуєте .NET 4.5, ви можете використовувати статичний метод Comparer<T>.Create.

Якщо ні, це реалізація, яка повинна бути еквівалентною:

public class FunctionalComparer<T> : IComparer<T>
{
    private Func<T, T, int> comparer;
    public FunctionalComparer(Func<T, T, int> comparer)
    {
        this.comparer = comparer;
    }
    public static IComparer<T> Create(Func<T, T, int> comparer)
    {
        return new FunctionalComparer<T>(comparer);
    }
    public int Compare(T x, T y)
    {
        return comparer(x, y);
    }
}

1
Можливо, хочете дати цьому класу іншу назву, щоб уникнути конфліктів з класом бібліотеки.
Серві

Синтаксична деталь: Конструктор загального класу не повинен включати <T>частину імені класу.
Jeppe Stig Nielsen

94

Якщо ви використовуєте .NET 4.5, ви можете використовувати статичний метод Comparer<aClass>.Create.

Документація: Comparer<T>.CreateМетод .

Приклад:

var x = someIEnumerable.OrderBy(e => e.someProperty, 
    Comparer<aClass>.Create((x, y) => x.someProperty > y.SomeProperty ?  1 : x.someProperty < y.SomeProperty ?  -1 : 0)
    );

1
На жаль, ми томимося в землі .Net 3.5! Не можу дозволити собі
мегаклин,

3
@haughtonomous, якщо це єдине, що вас стримує, чи роздумували ви про демпінг TFS на користь чогось іншого?
Артуро Ернандес

Чи знаєте ви основну теорію (не таку, як "оскільки він вимагає іншого типу, ніж лямбда") про те, чому ми не можемо помістити лямбду безпосередньо туди, а нам потрібна обгортка?
jw_

@jw_ Я не впевнений, скільки теорії стоїть за цим. Автори .OrderBy(Linq) вирішили не мати перевантаження, яке прийняло делегата для порівняння (як Comparison<TKey>делегат). Ви можете створити власний метод розширення, якщо хочете.
Jeppe Stig Nielsen

Теорія полягає в тому, що Інтерфейс має 2+ методи.
jw_

3

Якщо ви постійно хочете порівнювати прогнозовані ключі (наприклад, одну властивість), ви можете визначити клас, який інкапсулює всю логіку порівняння ключів для вас, включаючи нульові перевірки, вилучення ключів для обох об’єктів та порівняння ключів за допомогою вказаного або типового внутрішнього порівняльник:

public class KeyComparer<TSource, TKey> : Comparer<TSource>
{
    private readonly Func<TSource, TKey> _keySelector;
    private readonly IComparer<TKey> _innerComparer;

    public KeyComparer(
        Func<TSource, TKey> keySelector, 
        IComparer<TKey> innerComparer = null)
    {
        _keySelector = keySelector;
        _innerComparer = innerComparer ?? Comparer<TKey>.Default;
    }

    public override int Compare(TSource x, TSource y)
    {
        if (object.ReferenceEquals(x, y))
            return 0;
        if (x == null)
            return -1;
        if (y == null)
            return 1;

        TKey xKey = _keySelector(x);
        TKey yKey = _keySelector(y);
        return _innerComparer.Compare(xKey, yKey);
    }
}

Для зручності застосовується заводський метод:

public static class KeyComparer
{
    public static KeyComparer<TSource, TKey> Create<TSource, TKey>(
        Func<TSource, TKey> keySelector, 
        IComparer<TKey> innerComparer = null)
    {
        return new KeyComparer<TSource, TKey>(keySelector, innerComparer);
    }
}

Потім ви можете використовувати це так:

var sortedSet = new SortedSet<MyClass>(KeyComparer.Create((MyClass o) => o.MyProperty));

Ви можете звернутися до мого допису в блозі для розширеного обговорення цієї реалізації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.