Подтвердить что ты не робот

Безопасное разыменование вызова FirstOrDefault в Linq С#

Для краткости в моем коде я хотел бы иметь возможность сделать следующее: имея коллекцию, найдите первый элемент, соответствующий выражению лямбда; если он существует, верните значение свойства или функции. Если он не существует, верните null.

Обновленные примеры w. классы

Пусть есть набор материалов

class Stuff
{
    public int Id { get; set; }
    public string Value { get; set; }
    public DateTime? ExecutionTime { get; set; }
}

То, на что я нацеливаюсь, - это способ хорошо вернуться при вызове этого

var list = new Stuff[] { new Stuff() { Id = 1, Value = "label", ExecutionTime = DateTime.Now } };

// would return the value of ExecutionTime for the element in the list
var ExistingTime = list.FirstOrDefault(s => s.Value.Contains("ab")).ExecutionTime;

// would return null
var NotExistingTime = list.FirstOrDefault(s => s.Value.Contains("zzz")).ExecutionTime; 

Возможно ли это с некоторым linq-синтаксисом-fu или мне нужно явно проверить возвращаемое значение?

Исходный пример w. строки

var stuff = {"I", "am", "many", "strings", "obviously"};

// would return "OBVIOUSLY"
var UpperValueOfAString = stuff.FirstOrDefault(s => s.contains("bvi")).ToUpper();

// would return null
var UpperValueOfAStringWannabe = stuff.FirstOrDefault(s => s.contains("unknown token")).ToUpper();

Комментарий: Я не должен был использовать строки в моем исходном примере, так как он слегка искажает вопрос, центрируя его по методу ToUpper и классу строк. Пожалуйста, рассмотрите обновленный пример

4b9b3361

Ответ 1

Почему бы просто не сделать:

stuff.Where(s => s.contains("bvi"))
     .Select(s => s.ToUpper())
     .FirstOrDefault()

Если у вас есть "не по умолчанию по умолчанию", вы можете сделать:

stuff.Where(s => s.contains("bvi"))
     .Select(s => s.ToUpper())
     .DefaultIfEmpty("Something Else")
     .First()

Ответ 2

Мне нравится это как метод расширения:

public static U SelectMaybe<T, U>(this T input, Func<T,U> func)
{
    if (input != null) return func(input);
    else return default(U);
}

И использование:

var UpperValueOfAString = stuff.FirstOrDefault(s => s.Contains("bvi")).SelectMaybe(x => x.ToUpper());
var UpperValueOfAStringWannabe = stuff.FirstOrDefault(s => s.Contains("unknown token")).SelectMaybe(x => x.ToUpper());

Это приведет к возврату значения по умолчанию (null в этом случае, но как правильно для этого типа), или вызовите соответствующую функцию и верните это.

Ответ 3

Update:

На основе выяснения вопроса вам не нужен какой-либо дополнительный код для достижения того, что вы хотите. Просто используйте предложение where и предложение выбора прогона:

var theString = stuff
    .Where(s => s.contains("unknown token"))
    .Select(s => s.ToUpper())
    .FirstOrDefault();

<ч/" > Старый ответ

Как было предложено в комментариях (и сделано общее в другом ответе), заверните вызов ToUpper в метод расширения. Методы расширения сводятся к синтаксическому сахару вокруг вызовов статических методов, поэтому они могут обрабатывать нули только отлично.

static class StringExtensions
{
    public static string PossiblyToUpper(this string s)
    {
        if (s != null)
            return s.ToUpper();

        return null;
    }
}

Ваш вызов станет следующим:

var upperValueOfAStringWannabe = stuff
    .FirstOrDefault(s => s.contains("unknown token"))
    .PossiblyToUpper();

Теперь просто обсуждается, является ли метод расширения просто поддерживать однострочный код стиля более выразительным, чем несколько строк - в конце дня выражение намерения кода более важно, чем то, что выглядит код как.

Лично я думаю, что методы расширения, которые обрабатывают нули, с первого взгляда запутывают, так как они затягиваются, чтобы выглядеть как обычные методы.