Лучший синтаксис для a = (x == null)? null: x.func()
Основной вопрос здесь - у меня много строк кода, которые выглядят примерно так:
var a = (long_expression == null) ? null : long_expression.Method();
Подобные линии многократно повторяют эту функцию. long_expression
отличается каждый раз. Я пытаюсь найти способ избежать повторения long_expression
, но сохраняя этот компактный. Что-то вроде противоположности operator ??
. На данный момент я рассматриваю возможность сдачи и ввода нескольких строк, например:
var temp = long_expression;
var a = (temp == null) ? null : temp.Method();
Но мне было любопытно, есть ли какой-нибудь умный синтаксис, о котором я не знаю, что сделает его более кратким.
Ответы
Ответ 1
Ну, вы можете использовать метод расширения следующим образом:
public static TResult NullOr<TSource, TResult>(this TSource source,
Func<TSource, TResult> func) where TSource : class where TResult : class
{
return source == null ? null : func(source);
}
Тогда:
var a = some_long_expression.NullOr(x => x.Method());
Или (в зависимости от вашей версии С#)
var a = some_long_expression.NullOr(Foo.Method);
где Foo
- тип some_long_expression
.
Я не думаю, что я это сделаю. Я бы просто использовал две строки. Это проще и менее умным - и, хотя "умный" - это удовольствие для Stack Overflow, обычно это не хорошая идея для реального кода.
Ответ 2
Я нашел этот ответ проницательным.
Выполняя это назначение, вы распространяете нуль глубже в систему. Вам нужно будет снова написать свое условие обработки null (и снова и снова) для обработки этих распространений.
Вместо того, чтобы разрешить выполнение продолжить с нулями, замените нуль лучшим представлением (указанным по ссылке)
- Если нуль представляет пустую коллекцию, используйте пустую коллекцию.
- Если нуль представляет исключительный случай, выведите исключение.
- Если значение null представляет собой случайно неинициализированное значение, явно инициализируйте его.
- Если значение null представляет собой законное значение, проверьте его - или даже лучше используйте NullObject, который выполняет нулевой оператор.
В частности, замена ссылки на null-сборку пустой коллекцией спасла мне много нулевых тестов.
Ответ 3
var x = "";
/* try null instead */
string long_expression = "foo";
var a = ((x = long_expression) == null) ? null : x.ToUpper();
/* writes "FOO" (or nothing) followed by newline */
Console.WriteLine(a);
Тип значения инициализации x
должен быть совместим с типом long_expression
(здесь: string
). Работает с Mono компилятор С#, версия 2.10.8.1 (из пакета Debian mono-gmcs = 2.10.8.1-4).
Ответ 4
Я думаю, что язык С# действительно может использовать новый оператор для такого рода логики - он довольно распространен, и оператор упростит бесчисленные строки кода там.
Нам нужно что-то в строю "??" или "if null then", но работает как специальный. точечного оператора с условием "если не null". Во-первых '?' это как "if" часть "?:" if-then, а вторая "?" представляет "null" как для типов с нулевым значением. Я думаю, нам нужно ".?" оператор, который работает так же, как ".", за исключением того, что он просто вычисляет значение null, если левое выражение является нулевым, а не выбрасывает исключение. Я предполагаю, что это будет оператор "точка не нуль" (ОК, так что, возможно, ".!?" Было бы более логичным, но не пускай туда).
Итак, ваш пример oh-so-common может потерять избыточное имя символа, например:
var a = long_expression.?Method();
Существует тонна кода С# с вложенными нулевыми проверками, которые будут очень полезны. Просто подумайте, как хорошо, если это:
if (localObject != null)
{
if (localObject.ChildObject != null)
{
if (localObject.ChildObject.ChildObject != null)
localObject.ChildObject.ChildObject.DoSomething();
}
}
может стать просто:
localObject.?ChildObject.?ChildObject.?DoSomething();
Существует также тонна кода, где нулевые проверки, которые должны быть там, отсутствуют, что приводит к случайным ошибкам во время выполнения. Итак, на самом деле, используя новый '.?' оператор по умолчанию устранит такие проблемы... Может быть, к сожалению, мы не можем изменить поведение ".". а также '.?' на данный момент, так что только новый ".?" выдает исключение, если левая сторона имеет нулевое значение - это будет более чистый и логичный путь, но нарушение изменений очень плохое. Хотя, можно утверждать, что это конкретное изменение, скорее всего, устранит множество скрытых проблем и вряд ли что-нибудь сломает (только код, ожидающий исключения null ref ref). О, ну... всегда можно мечтать...
Единственный недостаток проверки нулевого значения - это, пожалуй, небольшое повышение производительности, что, несомненно, является причиной того, почему '.' не проверяет значение null. Но я действительно думаю, что можно просто использовать ".?" когда вы заботитесь о производительности (и знаете, что левая сторона никогда не будет равна нулю), было бы лучшим способом.
Аналогичным образом было бы гораздо приятнее иметь "foreach" проверку и игнорирование нулевой коллекции. Больше не нужно обертывать большинство "foreach" операторов "if (collection!= Null)" или, что еще хуже, развивать обычную привычку всегда использовать пустые коллекции вместо нуля... что в некоторых случаях прекрасно, но еще хуже чем нулевая проверка, когда это делается в сложных деревьях объектов, где большая часть коллекций пуста (что я видел много). Я думаю, что хорошее намерение не иметь проверку "foreach" для получения нулевого значения, чтобы обеспечить лучшую производительность, в большинстве случаев отрицательно сказалось.
Андерс, никогда не поздно делать такие изменения, и добавить для них компилятор!
Ответ 5
Вы можете написать метод расширения для любого типа long_expression:
public static object DoMethod(this MyType pLongExpression)
{
return pLongExpression == null ? null : pLongExpression.Method();
}
Это будет доступно для любой ссылки MyType
, даже если эта ссылка null
.
Ответ 6
Вы можете поместить long_expression == null
в функцию с коротким именем и просто вызвать эту функцию каждый раз.
Ответ 7
if (! String.IsNullOrEmpty(x)) x.func()