Linq.SingleOrDefault - как настроить значение по умолчанию для пользовательского класса?
Я просмотрел некоторые вопросы и немного искал Google,
но я не мог найти ответ (это меня удовлетворяет).
В основном я понимаю, что SingleOrDefault
возвращает null или 0 (зависит от типа).
но как я могу заставить его вернуть что-то еще?
return myChannels.All.Where(_Channel => _Channel.Guid == this.ParentChannelGuid).SingleOrDefault(_SPECIFICCHANNEL);
поэтому я хочу, чтобы _SPECIFICCHANNEL
возвращался в случае, если он не является единственным.
это можно сделать?
Ответы
Ответ 1
Это может быть достигнуто довольно простым способом. Если вы создаете свой собственный метод расширения, который является более конкретным, чем универсальный SingleOrDefault
, то компилятор предпочтет более специфичную для типа версию. Вот пример, который показывает, как это сделать с помощью простого класса Person
(вы можете скопировать и вставить его в LINQPad, чтобы быстро увидеть результат):
public class Person
{
public string Name { get; set; }
public override string ToString()
{
return Name ?? "";
}
}
public static class PersonExtensionMethod
{
public static Person SingleOrDefault(this IEnumerable<Person> source)
{
var person = Enumerable.SingleOrDefault(source);
if (person == null)
return new Person { Name = "Unnamed" };
return person;
}
}
public static void Main()
{
var emptyCollection = new Person[0];
var nonEmptyCollection = new Person[] { new Person { Name = "Jack" } };
Debug.WriteLine("Empty collection: " + emptyCollection.SingleOrDefault());
Debug.WriteLine("Non-empty collection: " + nonEmptyCollection.SingleOrDefault());
}
В приведенном выше примере SingleOrDefault(IEnumerable<Person>)
имеет приоритет над SingleOrDefault<T>(IEnumerable<T>)
который является менее конкретным.
Ответ 2
Вам нужно будет сделать метод расширения:
public static T SingleOr<T>(this IEnumerable<T> list, T defaultValue) where T : class
{
return list.SingleOrDefault() ?? defaultValue;
}
Другого пути нет. Все классы по умолчанию имеют значение null.
Ответ 3
Не могли бы вы использовать DefaultIfEmpty() (код Psedo следует) -
return myChannels.All.Where(_Channel => _Channel.Guid == this.ParentChannelGuid).DefaultIfEmpty(_SPECIFICCHANNEL).SingleOrDefault();
Ответ 4
но как я могу заставить его вернуть что-то еще?
Вы не можете. Вы можете сделать свой собственный метод — как показано Оскаром Кьеллином; для возврата someting else, но SingleOrDefault
всегда будет вести себя как запрограммировано, что означает возврат значения по умолчанию (null
, 0) для элемента.
Ответ 5
Почему бы просто не использовать "??" оператора и скажем
return myChannels.SingleOrDefault(_Channel => _Channel.Guid == this.ParentChannelGuid) ??_SPECIFICCHANNEL;
Ответ 6
Вы не можете определить значение по умолчанию для типа. Он всегда определяется как null для ссылочных типов. Для structs это экземпляр структуры, где все поля-члены в свою очередь устанавливаются в значения по умолчанию. Для перечислений всегда 0 (что может быть или не быть определенным значением рассматриваемого типа перечисления)
Ответ 7
Расширяя ответ Оскара о методе расширения, вы можете сделать его более общим, чтобы оно охватывало значения, а также ссылочные типы примерно так:
public static T SingleOrSpecifiedDefault<T>(this IEnumerable<T> enumerable, Expression<Func<T, bool>> singleOrDefault, T defaultValue) where T : IComparable
{
T singleValue = enumerable.SingleOrDefault(singleOrDefault.Compile());
if (singleValue == null || singleValue.CompareTo(default(T)) == 0)
{
return defaultValue;
}
return singleValue;
}
Это позволяет вам указать то же выражение LINQ, которое вы будете использовать с SingleOrDefault
а также defaultValue
(которое будет использоваться, если совпадение не найдено).