Вопрос по .net, c# – Элегантный способ избежать NullReferenceException в C #

7

я хочу сделать это

var path = HttpContext.Current.Request.ApplicationPath;

Если какое-либо из свойств на этом пути является нулевым, я хочу, чтобы путь был нулевым или & quot; & quot; было бы лучше.

Есть ли элегантный способ сделать это без троих?

в идеале я хотел бы, чтобы это поведение (без ужасной производительности и безобразия) путь строки;

try
{
    path = HttpContext.Current.Request.ApplicationPath;
}
catch
{
    path = null;
}

Спасибо

я неправильно прочитал и удалил комментарий Carl Winder

Ваш Ответ

4   ответа
11
[РЕДАКТИРОВАТЬ

аняющим оператором?., что упростит ваше дело до:

var path = HttpContext?.Current?.Request?.ApplicationPath

По историческим причинам ответ на предыдущие языковые версии можно найти ниже.

Полагаю, вам нужен безопасный оператор разыменования Groovy?., и вы не первый. Из связанной темы мне лично больше всего нравится решениеэто (вон то выглядит тоже неплохо). Тогда вы можете просто сделать:

var path = HttpContext.IfNotNull(x => x.Current).IfNotNull(x => x.Request).IfNotNull(x => x.ApplicationPath);

Вы всегда можете немного сократить имя функции. Это возвратит нуль, если любой из объектов в выражении будет нулевым, ApplicationPath в противном случае. Для типов значений вам придется выполнить одну нулевую проверку в конце. В любом случае, пока нет другого пути, если только вы не хотите проверять ноль на каждом уровне.

Вот метод расширения, использованный выше:

    public static class Extensions
    {
    // safe null-check.
    public static TOut IfNotNull<TIn, TOut>(this TIn v, Func<TIn, TOut> f) 
            where TIn : class  
            where TOut: class 
            { 
                    if (v == null) return null; 
                    return f(v); 
            }       
    }
какой разумный способ переопределить оператор, чтобы сделать это? AK_
@ AK_ Нет, вы не можете просто определить собственного оператора своего бренда и переопределить ... Это было бы неприятно в этом случае :). Команда .NET знает о «запросах», сделанных для такого рода функциональности, но AFAIK это не будет поставляться в C # 5 ->Во а такжеВо Patryk Ćwiek
(Продолжение, ба, время ожидания для правок). Я не вижу способа переопределить эту функцию как оператора, учитывая, что она принимает лямбда-выражение - я не знаю, возможно ли это, но даже если бы это было так, это было бы некрасиво :). Более того, перегрузка существующего оператора для произвольного типа (если это вообще возможно) открыла бы совершенно новое поле для столкновений и неожиданных WTF. Patryk Ćwiek
Добавлен метод расширения, который вы использовали в своем примере. Отличное решение! Matt
Любить это! Также добавлен метод расширения в нашу библиотеку фреймворков. Timo Kosig
2

когда вы задали вопрос, я не увидел более простого решения, чем предложенная попытка ... поймать. Единственная альтернатива - проверка каждой части на ноль с помощью «если» или «?» выглядела ужаснее (но, возможно, немного быстрее).

Либо ты должен был написать:

path = HttpContext!=null 
    ? (HttpContext.Current!=null 
            ? (HttpContext.Current.Request!=null 
                    ?(HttpContext.Current.Request.ApplicationPath!=null 
                            ? HttpContext.Current.Request.ApplicationPath 
                            : null) 
                    : null) 
            : null) 
    : null;

или

if (HttpContext == null || HttpContext.Current == null 
    || HttpContext.Current.Request == null 
    || HttpContext.Current.Request.ApplicationPath  == null)
    path = null;
else
    path = HttpContext.Current.Request.ApplicationPath;

оба делают это без обработки исключений. Обратите внимание, что оба используют «горячие клавиши» для отмены проверки, если найдено какое-либо нулевое значение.

Обновление (декабрь 2017 года): Поскольку C # Версия 6 и выше, есть лучшее решение, так называемоеElvis -Оператор (также известный как нуль-коалесцирующий оператор?. а такжеx?[i] для массивов), который вы можете использовать. Пример выше

path = HttpContext!=null 
    ? (HttpContext.Current!=null 
            ? (HttpContext.Current.Request!=null 
                    ?(HttpContext.Current.Request.ApplicationPath!=null 
                            ? HttpContext.Current.Request.ApplicationPath 
                            : null) 
                    : null) 
            : null) 
    : null;

выглядит намного лучше:

path = HttpContext?.Current?.Request?.ApplicationPath;

которая делает то же самое, и это, ИМХО, гораздо больше, чем просто синтаксический сахар. В сочетании с дополнением?? value вы можете легко заменитьnull другим значением, например

path = (HttpContext?.Current?.Request?.ApplicationPath) ?? "";

Это делаетpath переменная пуста, если не может быть получено ненулевое значение.

4

string value = NullHelpers.GetValueOrNull(
    () => HttpContext.Current.Request.ApplicationPath);

Самый простой способ реализовать этоNullHelpers.GetValueOrNull, вероятно, что-то вроде этого:

public static T GetValueOrNull<T>(Func<T> valueProvider) 
    where T : class
{
    try
    {
        return valueProvider();
    }
    catch (NullReferenceException)
    {
        return null;
    }
}

Но самый крутой способ решить эту проблему - использовать деревья выражений:

public static T GetValueOrNull<T>(
    Expression<Func<T>> valueProvider) 
    where T : class
{
    var expression = (MemberExpression)
        ((MemberExpression)valueProvider.Body).Expression;

    var members = new List<MemberExpression>();

    while (expression != null)
    {
        members.Add(expression);

        expression = 
            (MemberExpression)expression.Expression;
    }

    members.Reverse();

    foreach (var member in members)
    {
        var func = Expression.Lambda<Func<object>>(member).Compile();

        if (func() == null)
        {
            return null;
        }
    }

    return valueProvider.Compile()();
}

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

Это все еще круто; -)

ОБНОВЛЕНИЕ (ноябрь 2014)

C # 6 идет, и это будет содержать то, что они называют Нулевой оператор распространения, что означает, что для этого существует языковая поддержка. Ваш пример может быть записан в C # 6 следующим образом:

var path = HttpContext?.Current?.Request?.ApplicationPath;

Если какая-либо часть содержит ноль, полное выражение вернет ноль.

Я не думаю, что второй пример работает .... разве вы не должны начинать с нижней части выражения? AK_
@ AK_: Вот чтоmembers.Reverse(); это для :-). Попробуйте сами. Steven
2

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

Пример

public void DoSomething()
{
  // Get the path, which may be null, using the extension method
  var contextPath = HttpContext.Current.RequestPath;
}


public static class HttpContextExtensions
{
  public static string RequestPath(this HttpContext context)
  {
    if (context == null || context.Request == null)
    {
      return default(string);
    }

    return context.Request.ApplicationPath;
  }
}
как это коротко? я сделал что-то очень похожее с тернариями ... AK_
Он короткий, потому что он включает в себя проверки в методе многократного использования; Это означает, что в любое время, когда вы хотите получить значение, это всего лишь один вызов метода. Это лучше, чем пытаться последовательно выполнять одни и те же проверки на ноль во многих местах, а также проще. STW

Похожие вопросы