А почему у нас разное поведение MaxItem в зависимости от типа (ref/value) TValue.
В текущей реализации если source-коллекция пустая, то если TValue — value type — выкидываем исключение, а если TValue — reference type — возвращаем null.
Или я что-то упускаю?
Я бы в случае, если source пустая, выкидывал исключение, но дополнительно сделал бы метод
MaxItemOrDefault(..., Func<TSource> defaultElementIfSourceIsEmpty /* Func<> for lazy creation */)
Здравствуйте, MozgC, Вы писали:
MC>Если source-коллекция пустая, то если TValue — value type — выкидываем исключение, а если TValue — reference type — возвращаем null.
Это соответствует поведению обычных Max/Min из фреймворка. Мотивация, видимо, в том что для реф типов есть выделенное значение null, означающее отсутствие элементов, а вот для value типов такого значения нет.
Возможно имеет смысл для value типов вертать nullable, но что важнее — консистентность между собой или консистентность с фреймворком я ответить однозначно не готов.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
Что то я подумал, что вот так тупо переносить с типа элемента на тип контейнера логику бессмысленно. Поэтому теперь все вариации при отсутствии элементов возвращают default(TSource).
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Возможно имеет смысл для value типов вертать nullable, но что важнее — консистентность между собой или консистентность с фреймворком я ответить однозначно не готов.
Я за фреймворк. Чтобы не переделывать код, если в FW добавят похожий вариант, а просто его использовать.
Re[3]: Разное поведение MaxItem в зависимости от типа (ref/value) TValue
Здравствуйте, AndrewVK, Вы писали:
AVK>Что то я подумал, что вот так тупо переносить с типа элемента на тип контейнера логику бессмысленно. Поэтому теперь все вариации при отсутствии элементов возвращают default(TSource).
Таким образом может быть невозможно понять, нашли ли мы элемент с максимальным значением или нет.
Представим, что у нас есть IEnumerable<Point> и надо найти точку с максимальным значением по оси Y. В коде может быть неизвестно, есть/были ли в source коллекции элементы или нет:
var maxYpoint = data.Where(d => d.SomeProperty == xxx).Select(d => d.Point).MaxItem(p => p.Y);
// maxYpoint.X & Y == 0. Значит ли это что мы нашли точку с максимальным значением Y или у нас после фильтрации вообще элементов не было?
Re[3]: Разное поведение MaxItem в зависимости от типа (ref/value) T
Здравствуйте, MozgC, Вы писали:
MC>Таким образом может быть невозможно понять, нашли ли мы элемент с максимальным значением или нет.
Можно предварительно позвать Enumerable.Empty если контейнер структура с валидным дефолтным значением и нам важно отследить отсутствие в ней элементов. В минусе только двойной запрос первого элемента.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Можно предварительно позвать Enumerable.Empty если контейнер структура с валидным дефолтным значением и нам важно отследить отсутствие в ней элементов.
Не распарсил.
AVK>В минусе только двойной запрос первого элемента.
Это может быть очень жирный минус. Первый элемент, удовлетворящий условию фильтрации, может находится ближе к концу последовательности. Или итерация может быть дорогой операцией. Вот у меня сейчас реальная задача по обработке терабайта данных. Во время этой обработки нужно тысячи раз проанализировать последовательности из миллионов элементов. Требования к производительности и памяти жёсткие.
AVK>Можно предварительно позвать Enumerable.Empty если контейнер структура с валидным дефолтным значением и нам важно отследить отсутствие в ней элементов. В минусе только двойной запрос первого элемента.
Я бы просто добавил MaxItemOrDefault(). Ну и переименовать в MaxBy()/MaxByOrDefault(), чтобы совпадало с Rx/Ix.
Re: Разное поведение MaxItem в зависимости от типа (ref/value) T
Здравствуйте, MozgC, Вы писали:
AVK>>Можно предварительно позвать Enumerable.Empty если контейнер структура с валидным дефолтным значением и нам важно отследить отсутствие в ней элементов. MC>Не распарсил.
var result = sequence.Empty() ? ... : sequence.MaxItem(...);
AVK>>В минусе только двойной запрос первого элемента. MC>Это может быть очень жирный минус.
Но точно так же этот минус будет, если нам не важно знать что в коллекции нет элеметнов, а метод выбрасывает исключение.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
Можно ничего не менять и надеяться, что люди будут знать о таком поведении (null для ref и исключение для value type) и в случае ref types будут проверять возвращаемое значение на null, а в случае структур будут использовать Cast<Point?> или DefaultIfEmpty(new Point(double.MinValue, double.MinValue)).
И такой подход конечно имеет право на жизнь.
А можно добавить методы Max/MinByOrDefault() и тогда некоторые люди, типа меня, просто сразу увидят в списке метод XxxOrDefault() и выберут его, и будут знать чего от него ожидать (т.к. имя метода сразу намекает, что в случае пустой коллекции будет возвращено дефолтное значение). И можно было бы сделать 2 перегрузки для XxxOrDefault(): одну без параметров, возвращающую default(TSource), другую — указанное пользователем значение (тут надо будет подумать в каком виде указывать это значение — Func<TSource> для ленивости, или не усложнять и просто TSource).
Re[5]: Разное поведение MaxItem в зависимости от типа (ref/value) T
Здравствуйте, AndrewVK, Вы писали:
AVK>А есть какой нибудь реальный юзкейс где это было бы полезно?
Последовательность структур. Возвращаемся к тому, что вернется default(Point), и я не пойму, то ли это реально точка с максимальной координатой, то ли source последовательность была пустая.
Re[9]: Разное поведение MaxItem в зависимости от типа (ref/value) T
Здравствуйте, MozgC, Вы писали:
MC>Последовательность структур. Возвращаемся к тому, что вернется default(Point), и я не пойму, то ли это реально точка с максимальной координатой, то ли source последовательность была пустая.
И что ты вернешь в случае наличия нужного параметра?
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>