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 (которое будет использоваться, если совпадение не найдено).