Довольно часто приходится передавать IComparer<> в разные методы.
Для пользовательских классов приходится создавать класс, наследовать и т.п., да еще и хранить ссылку на экземпляр отдельно
чтобы то и дело не было создания.
Когда намного проще было бы написать лямбду "на месте".
Написал такой класс:
public class CustomComparer<T> : IComparer<T>
{
private readonly Func<T, T, int> comparer;
private static CustomComparer<T> instance;
public int Compare(T x, T y)
{
return comparer(x, y);
}
public CustomComparer(Func<T,T,int> comparer)
{
this.comparer = comparer;
}
static CustomComparer<T> Create(Func<T,T,int> comparer)
{
return instance ?? (instance = new CustomComparer<T>(comparer));
}
}
юзать типа так:
CustomComparer<Lalala>.Create((l1, l2) => ...);
Думаю все понятно без комментариев.
Теперь вопрос: тыкните меня в стандартное решение из BCL, свой велосипед конечно самый крутой всегда, но все же я предпочитаю использовать стандартное апи. Или каждый сaм этот лесопед пишет?
28.06.11 19:52: Перенесено модератором из '.NET' — TK
Здравствуйте, G-Host, Вы писали:
GH>Теперь вопрос: тыкните меня в стандартное решение из BCL, свой велосипед конечно самый крутой всегда, но все же я предпочитаю использовать стандартное апи. Или каждый сaм этот лесопед пишет?
Стандартное решение в BCL для C# — создать тип-компаратор, реализующий интерфейс IComparator<T>.
И это нормально. Нормально для ОО-языка. Это не "лесопед".
Лямбдой вы лишь восполняете отсутствие в C# анонимных определений классов / object expression из F#.
Здравствуйте, Пельмешко, Вы писали:
П>Стандартное решение в BCL для C# — создать тип-компаратор, реализующий интерфейс IComparator<T>. П>И это нормально. Нормально для ОО-языка. Это не "лесопед".
Логически компаратор это именно функция, т.к. никакого состояния у него нет.
П>Лямбдой вы лишь восполняете отсутствие в C# анонимных определений классов / object expression из F#.
Лямбда это оптимальная форма представления компаратора, полностью соотвествующая его логической сути. А класс компаратора хоть в явном, хоть в анонимном виде это лишний слой абстракции, там где он совершенно не нужен.
Здравствуйте, Undying, Вы писали:
П>>Лямбдой вы лишь восполняете отсутствие в C# анонимных определений классов / object expression из F#.
U>Лямбда это оптимальная форма представления компаратора, полностью соотвествующая его логической сути. А класс компаратора хоть в явном, хоть в анонимном виде это лишний слой абстракции, там где он совершенно не нужен.
Знаете, если уж на то пошло, то логическая суть IEnumerator<T>/IObserver<T> тоже прекрасно изображается в виде единственной лямбды, возвращающей/передающей Option<T>, и чего с этого?
Я прекрасно понимаю и согласен с вашей мыслью, лишь хотел подчеркнуть топикстартеру, что .NET в первую очередь всё же объектно-ориентированный framework, а значит определить тип и реализовать интерфейс — нормальная практика, а значит частенько лямбда-рюшечки для старых частей framework'а приходится писать самому
Здравствуйте, Пельмешко, Вы писали:
П>Я прекрасно понимаю и согласен с вашей мыслью, лишь хотел подчеркнуть топикстартеру, что .NET в первую очередь всё же объектно-ориентированный framework, а значит определить тип и реализовать интерфейс — нормальная практика, а значит частенько лямбда-рюшечки для старых частей framework'а приходится писать самому
Я не знаю исторических причин создания интерфейса IComparer, но думаю, что он родился по аналогии с IEqualityComparer (где нужна связка двух методов Equals и GetHashCode).
В .NET 2.0 внесли и Comparison<T> и Predicate<T> (казалось бы, ничто не мешало ранее иметь обобщенные Comparison и Predicate с параметрами типа object), но удалять IComparer было уже поздно.
А в .NET 3.5 внесли Func<>-и, и стало поздно удалять Comparison<T> и Predicate<T>. Жаль что они несовместимы, и жаль что рюшечки с адаптерами приходится писать самому.
Здравствуйте, samius, Вы писали:
S>Жаль что они несовместимы, и жаль что рюшечки с адаптерами приходится писать самому.
Afair, эти адаптеры (обычно называются GenericComparer<T> или как-то так), параметризуемые лямбдами, реализованы в каждой второй библиотеке на правах NIH-велосипеда :) Так что нужно просто покопаться в недрах используемых вами third-party-библиотек где-нибудь в подпространствах Utils/Helpers. Вот, например, в библиотеке C5 подобный компаратор можно найти под названием DelegateComparer<T>.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, samius, Вы писали:
S>>Жаль что они несовместимы, и жаль что рюшечки с адаптерами приходится писать самому.
Q>Afair, эти адаптеры (обычно называются GenericComparer<T> или как-то так), параметризуемые лямбдами, реализованы в каждой второй библиотеке на правах NIH-велосипеда Так что нужно просто покопаться в недрах используемых вами third-party-библиотек где-нибудь в подпространствах Utils/Helpers. Вот, например, в библиотеке C5 подобный компаратор можно найти под названием DelegateComparer<T>.
Обычно я не копаюсь даже в своих исходниках дальше текущего проекта. Дешевле написать влет. Что-нибудь вроде Flatten с неагрессивным использованием стека — уже можно поискать.
Здравствуйте, Пельмешко, Вы писали:
П>Я прекрасно понимаю и согласен с вашей мыслью, лишь хотел подчеркнуть топикстартеру, что .NET в первую очередь всё же объектно-ориентированный framework,
Нет никакого противопоставления между объектами и функциями. Соответственно представление всего объектами это такая же глупость, как представление всего функциями.
Здравствуйте, G-Host, Вы писали: GH>Довольно часто приходится передавать IComparer<> в разные методы. GH>Для пользовательских классов приходится создавать класс, наследовать и т.п., да еще и хранить ссылку на экземпляр отдельно GH>чтобы то и дело не было создания. GH>Когда намного проще было бы написать лямбду "на месте".
Был такой замечательный советский фильм про конструкторов ракет (начинавших ещё в довоенные годы). Так там вот один такой "молодой" конструктор приходит к двум уже "заматеревшим" и показывает свои чертежи. А те смотрят и спрашивают: "Шестой вариант уже поди?" — "а откуда вы знаете" — "А вот когда тридцатый (тут точную цифру забыл я) сделаешь, сам поймёшь"
Далее сначала добавляется ещё один (Convert) "самый полезный метод":
public static partial class Comparers
{
public static Comparer<T> Create<T>(Comparison<T> comparison) {
Argument.NotNull(comparison, "comparison");
return new ComparisonComparer<T>(comparison);
}
public static Comparer<TOutput> Convert<TInput, TOutput>(this IComparer<TInput> comparer, Converter<TOutput, TInput> converter) {
Argument.NotNull(comparer, "comparer");
Argument.NotNull(converter, "converter");
Comparison<TOutput> comparison = (x, y) => comparer.Compare(converter(x), converter(y));
return Create(comparison);
}
[Serializable]
private sealed class ComparisonComparer<T> : Comparer<T>
{
public ComparisonComparer(Comparison<T> comparison) {
Argument.NotNull(comparison, "comparison");
Comparison = comparison;
}
private Comparison<T> Comparison { get; set; }
[ContractInvariantMethod]
private void Invariant() {
Contract.Invariant(Comparison != null);
}
public override int Compare(T x, T y) {
return Comparison(x, y);
}
}
}
Потом другой:
public static partial class Comparers
{
public static Comparer<T> Reverse<T>(this IComparer<T> comparer) {
Argument.NotNull(comparer, "comparer");
return new ReverseComparer<T>(comparer);
}
public static Comparer<T> DefaultReverse<T>() {
return ReverseComparer<T>.Default;
}
[Serializable]
private sealed class ReverseComparer<T> : Comparer<T>
{
private static readonly ReverseComparer<T> @default = new ReverseComparer<T>(Comparer<T>.Default);
public ReverseComparer(IComparer<T> comparer) {
Argument.NotNull(comparer, "comparer");
Original = comparer;
}
public static new ReverseComparer<T> Default {
[DebuggerStepThrough]
get { return @default; }
}
private IComparer<T> Original { get; set; }
[ContractInvariantMethod]
private void Invariant() {
Contract.Invariant(Original != null);
}
public override int Compare(T x, T y) {
return Original.Compare(y, x);
}
}
}
Третий…
public static partial class Comparers
{
public static Comparer<T> ThenBy<T>(this IComparer<T> first, IComparer<T> second) {
Argument.NotNull(first, "first");
Argument.NotNull(second, "second");
return new ThenByComparer<T>(first, second);
}
[Serializable]
private sealed class ThenByComparer<T> : Comparer<T>
{
public ThenByComparer(IComparer<T> first, IComparer<T> second) {
Argument.NotNull(first, "first");
Argument.NotNull(second, "second");
First = first;
Second = second;
}
private IComparer<T> First { get; set; }
private IComparer<T> Second { get; set; }
[ContractInvariantMethod]
private void Invariant() {
Contract.Invariant(First != null);
Contract.Invariant(Second != null);
}
public override int Compare(T x, T y) {
var value = First.Compare(x, y);
if(value == 0) {
return Second.Compare(x, y);
}//ifreturn value;
}
}
}
Ну и до кучи
public static partial class Comparers
{
public static bool Equals<T>(this IComparer<T> comparer, T left, T right) {
Argument.NotNull(comparer, "comparer");
return comparer.Compare(left, right) == 0;
}
public static bool NotEquals<T>(this IComparer<T> comparer, T left, T right) {
Argument.NotNull(comparer, "comparer");
return comparer.Compare(left, right) != 0;
}
public static bool LessThan<T>(this IComparer<T> comparer, T left, T right) {
Argument.NotNull(comparer, "comparer");
return comparer.Compare(left, right) < 0;
}
public static bool LessThanOrEqual<T>(this IComparer<T> comparer, T left, T right) {
Argument.NotNull(comparer, "comparer");
return comparer.Compare(left, right) <= 0;
}
public static bool GreaterThan<T>(this IComparer<T> comparer, T left, T right) {
Argument.NotNull(comparer, "comparer");
return comparer.Compare(left, right) > 0;
}
public static bool GreaterThanOrEqual<T>(this IComparer<T> comparer, T left, T right) {
Argument.NotNull(comparer, "comparer");
return comparer.Compare(left, right) >= 0;
}
}
А остальное уж совсем экзотика
Тут полезно что: наружу выдаётся не IComparer<>, а Comaprer<>, качественное отличие которого в том, что он так же реализует и не-дженерик IComparer и его можно использовать в больше количестве сценариев и даётся это совершенно забесплатно.
Метод Convert позволяет отсортировать, например, коллекцию бизнес-объектов по какому-либо полю, а ThenBy по совокупности полей.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Аlexey, Вы писали:
А>Немного покритикую реализацию. А>Кеширование экземпляра в статическом поле кажется очень подозрительным... А>Сегодня вы написали код: А>
А>И в результате вы начинаете искать почему же сравнение выполняется не так как ожидалось
Да, я знаю про такой вариант. Потому есть public CustomComparer(Func<T,T,int> comparer)
и я использую статический экземпляр только когда наверняка уверен что он нужен в одном месте
Да и, улучшить же можно сопоставлением функция-объект.
Здравствуйте, Tissot, Вы писали:
U>>Логически компаратор это именно функция, т.к. никакого состояния у него нет.
T>А кто вам сказал, что у функции обязательно нет состояния?
Здравствуйте, Tissot, Вы писали:
U>>Логически компаратор это именно функция, т.к. никакого состояния у него нет. T>А кто вам сказал, что у функции обязательно нет состояния?
Ты умеешь использовать функцию для хранения состояния? Можно код продемонстрировать, в котором функция это делает?
Здравствуйте, Undying, Вы писали:
U>>>Логически компаратор это именно функция, т.к. никакого состояния у него нет. T>>А кто вам сказал, что у функции обязательно нет состояния?
U>Ты умеешь использовать функцию для хранения состояния? Можно код продемонстрировать, в котором функция это делает?
Нет, я не использую функцию для хранения состояния, я написал, что она может его иметь. Погугли по memoize, найдешь пример функции с "состоянием".
Здравствуйте, Qbit86, Вы писали:
U>>Ты умеешь использовать функцию для хранения состояния? Можно код продемонстрировать, в котором функция это делает? Q>Замыкание?
Там же не в функции состояние хранится, а в переменной объявленной вне функции.
Здравствуйте, Undying, Вы писали:
U>>>Ты умеешь использовать функцию для хранения состояния? Можно код продемонстрировать, в котором функция это делает? Q>>Замыкание?
U>Там же не в функции состояние хранится, а в переменной объявленной вне функции.
Офигеть аргументация. Тогда и у объектов состояния нет, в состояние же не в них хранится, а в полях.
Здравствуйте, Nikolay_P_I, Вы писали: N_P>А можно поделиться окончательным результатом для использования сторонними разработчиками не разжигая аппетит ? Пожалуйста
А там уже ничего интересного и не осталось. Create/Convert для IEqualityComparer<>: