SingleOrDefault: как изменить значения по умолчанию?

SingleOrDefault возвращает null, но что, если я хочу присвоить значения для представления объекта, который не был найден?


person zsharp    schedule 04.05.2009    source источник
comment
вы нашли какой-нибудь ответ? вы не можете изменить дефолт как промис методом. потому что SingleOrDefault должен быть SingleOrNull, но он может возвращать объект значения. кто может сказать вернуть 0 для int, это номер по умолчанию или предполагаемый? SingleOrDefault может возвращать 0, но это не должно быть значение по умолчанию. вернуть ноль или нет. это плохая парадигма для ООП.   -  person Nuri YILMAZ    schedule 15.09.2015


Ответы (4)


вы можете сделать что-то вроде

myStrings.DefaultIfEmpty("myDefaultString").Single()

проверьте здесь

person oscarkuo    schedule 04.05.2009

?? оператор. Если левый аргумент равен нулю, оцените и верните второй аргумент.

myCollection.SingleOrDefault() ?? new[]{new Item(...)}

Это будет работать только со ссылочными типами (или нулевыми значениями), но будет делать то, что вы ищете, очень просто.

person Joe White    schedule 04.05.2009
comment
Когда я использую нулевую коалесценцию, в моей голове раздаются аплодисменты. +1 - person EightyOne Unite; 07.03.2012
comment
@Joe Я думаю, что OP действительно хочет изменить значение по умолчанию, которое напрямую возвращается из SingleOrDefault(), например. если поиск объекта Person и ни один не существует, он может захотеть, чтобы SingleOrDefault() возвращал объект NullPerson. Я понимаю, что невозможно изменить тип возвращаемого значения, и проблема должна быть решена так, как вы это сделали, но опять же я думаю, что важно уточнить для OP, что возвращаемое значение по умолчанию SingleOrDefault() не может быть измененный. - person Matthew; 27.06.2017
comment
Не работает для коллекции типов значений. Например, stackoverflow.com/questions/5424968/ - person r2d2; 02.03.2020

Вы могли бы свернуть свой собственный.

public static T SingleOrDefault<T>(this IEnumerable<T> enumerable, T defaultValue) {
  if ( 1 != enumerable.Count() ) {
    return defaultValue;
  }
  return enumerable.Single();
}

Это может быть немного дорого, потому что Count() требует от вас обработки всей коллекции и может быть довольно дорогим для запуска. Было бы лучше либо вызвать Single, либо поймать InvalidOperationException, либо свернуть метод IsSingle.

public static bool IsSingle<T>(this IEnumerable<T> enumerable) {
  using ( var e = enumerable.GetEnumerator() ) {
    return e.MoveNext() && !e.MoveNext();
  }
}

public static T SingleOrDefault<T>(this IEnumerable<T> enumerable, T defaultValue) {
  if ( !enumerable.IsSingle() ) {
    if( enumerable.IsEmpty() ) { 
      return defaultValue;
    }
    throw new InvalidOperationException("More than one element");
  }
  return enumerable.Single();
}
person JaredPar    schedule 04.05.2009
comment
Это вернет значение по умолчанию, если перечисляемый объект содержит несколько объектов. Разве он не должен генерировать исключение, как это делает стандартная реализация? - person tvanfosson; 04.05.2009
comment
@tvanfosson да, так и должно быть. У меня сложилось впечатление, что SingleOrDefault вернет значение по умолчанию, если будет что-то кроме одного элемента. Но, по-видимому, это будет делаться только без элементов. Странно, буду обновлять. - person JaredPar; 04.05.2009

Вы можете создавать свои собственные методы расширения -- SingleOrNew.

public static class IEnumerableExtensions
{
    public static T SingleOrNew<T>( this IEnumerable<T> enumeration, T newValue )
    {
        T elem = enumeration.SingleOrDefault();
        if (elem == null)
        {
            return newValue;
        }
        return elem;
    }

    public static T SingleOrNew<T>( this IEnumerable<T> enumeration, Func<T,bool> predicate, T newValue )
    {
        T elem = enumeration.SingleOrDefault( predicate );
        if (elem == null)
        {
            return newValue;
        }
        return elem;
    }
}
person tvanfosson    schedule 04.05.2009
comment
Однако проблема с этим подходом заключается в том, что вы вернете параметр newValue для коллекции, которая содержит один элемент со значением null. Рассмотрим это выражение (new string[] { null }).SingleOrNew(foo);. Это неправильно вернет foo - person JaredPar; 04.05.2009
comment
@JaredPar - я готов с этим смириться. Как правило, я не помещаю нули в свои коллекции, и если бы я нашел их при использовании такого метода, я бы, вероятно, все равно захотел заменить его своим значением по умолчанию. - person tvanfosson; 04.05.2009
comment
@tvanfosson, это довольно существенное изменение исходного алгоритма, которое делает серьезное предположение о том, как люди используют null. - person JaredPar; 04.05.2009
comment
Вся идея заключается в том, что вы используете это, когда хотите гарантировать, что вы получите не нулевой объект, а тот, который заполнен подходящим значением по умолчанию. Я не вижу проблемы. Оберните ожидаемую функциональность в модульные тесты, чтобы пользователю было ясно, что ожидается. При необходимости добавьте документацию. Я не могу придумать ни одного варианта использования, который бы сломался для меня. - person tvanfosson; 04.05.2009
comment
@tvanfosson, при разработке API вы должны учитывать других пользователей перед собой. В этом случае вы предлагаете то, что составляет перегрузку для существующего типа запроса, но при этом коренным образом меняете способ поиска. Это может не сломить вас, но сломает кого-то в зависимости от такого поведения. Рассмотрим в качестве примера разницу между Hashtable и Dictionary. В v1 считалось нормальным возвращать значение null для Hashtable, поскольку считалось, что никто не будет использовать значение null. Оказалось, что это было очень плохое предположение до такой степени, что изменилось поведение словаря в версии 2. - person JaredPar; 05.05.2009
comment
Я полностью согласен с Джаредом. Вы делаете огромное предположение, которое может привести к очень трудно отслеживаемым ошибкам. Я бы вообще не рекомендовал такой подход. - person SolutionYogi; 05.04.2012
comment
@SolutionYogi, просто для ясности, я не предлагаю Microsoft сделать это, а скорее, что кто-то может сделать это для своего проекта. Для меня, который никогда не хранит нули в коллекциях, это было бы подходящим решением проблемы. Если ваши коллекции могут содержать пустые значения, вам нужно будет выполнить собственную проверку вместо того, чтобы полагаться на SingleOrDefault, и возвращать значение по умолчанию только тогда, когда ни один из элементов не соответствует условию. - person tvanfosson; 06.04.2012