Обновление: Начиная с Visual Studio 2015, компилятор C # (языковая версия 6) теперь распознает оператор ?.
, что упрощает "глубокую проверку нуля". Подробнее см. этот ответ .
Помимо перепроектирования вашего кода, например
этот удаленный ответ предложил,
другой (хотя и ужасный) вариант - использовать блок try…catch
, чтобы посмотреть, не произойдет ли NullReferenceException
во время этого глубокого поиска свойств.
try
{
var x = cake.frosting.berries.loader;
...
}
catch (NullReferenceException ex)
{
// either one of cake, frosting, or berries was null
...
}
Лично я бы не стал этого делать по следующим причинам:
- Это не выглядит красиво.
- Он использует обработку исключений, которая должна быть нацелена на исключительные ситуации, а не на то, что вы ожидаете часто случать во время обычного хода работы.
NullReferenceException
s, вероятно, никогда не должны быть явно перехвачены. (См. этот вопрос .)
Так возможно ли использовать какой-либо метод расширения или это будет языковая функция, [...]
Это почти наверняка должна быть языковая функция (которая доступна в C # 6 в виде операторов .?
и ?[]
), если только в C # не было более сложной ленивой оценки или если вы не хотите использовать отражение (что, вероятно, также не очень хорошая идея по соображениям производительности и безопасности типов).
Поскольку нет способа просто передать cake.frosting.berries.loader
в функцию (она будет оценена и сгенерирована исключительная ситуация с нулевой ссылкой), вам придется реализовать метод общего поиска следующим образом: он принимает объекты и имена свойств для поиска:
static object LookupProperty( object startingPoint, params string[] lookupChain )
{
// 1. if 'startingPoint' is null, return null, or throw an exception.
// 2. recursively look up one property/field after the other from 'lookupChain',
// using reflection.
// 3. if one lookup is not possible, return null, or throw an exception.
// 3. return the last property/field's value.
}
...
var x = LookupProperty( cake, "frosting", "berries", "loader" );
(Примечание: код отредактирован.)
Вы быстро видите несколько проблем с таким подходом. Во-первых, вы не получаете никакой безопасности типов и возможной упаковки значений свойств простого типа. Во-вторых, вы можете либо вернуть null
, если что-то пойдет не так, и вам придется проверить это в вызывающей функции, либо вы выбросите исключение, и вы вернетесь к тому, с чего начали. В-третьих, это может быть медленно. В-четвертых, это выглядит хуже, чем то, с чего вы начали.
[...], или это просто плохая идея?
Я бы тоже остался с:
if (cake != null && cake.frosting != null && ...) ...
или воспользуйтесь приведенным выше ответом Мехрдада Афшари.
P.S .:: 1062 * Когда я писал этот ответ, я явно не рассматривал деревья выражений для лямбда-функций; см. например Ответ @driis для решения в этом направлении. Он также основан на некотором отражении и, таким образом, может работать не так хорошо, как более простое решение (if (… != null & … != null) …
), но его можно оценить лучше с точки зрения синтаксиса.