Безопасное разыменование вызова 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 и классу строк. Пожалуйста, рассмотрите обновленный пример
Ответы
Ответ 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();
Теперь просто обсуждается, является ли метод расширения просто поддерживать однострочный код стиля более выразительным, чем несколько строк - в конце дня выражение намерения кода более важно, чем то, что выглядит код как.
Лично я думаю, что методы расширения, которые обрабатывают нули, с первого взгляда запутывают, так как они затягиваются, чтобы выглядеть как обычные методы.