Pytanie w sprawie syntax, .net, web, c# – Elegancki sposób na uniknięcie NullReferenceException w C #

7

chce to zrobić

var path = HttpContext.Current.Request.ApplicationPath;

Jeśli któraś z właściwości po drodze ma wartość NULL, chcę, aby ścieżka była null lub „” byłaby lepsza.

Czy jest elegancki sposób na zrobienie tego bez Ternaries?

idealnie chciałbym to zachowanie (bez okropnej wydajności i brzydoty) ścieżki smyczkowej;

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

Dziękuję Ci

źle odczytałem i usunąłem komentarz Carl Winder

Twoja odpowiedź

4   odpowiedź
2

W maju 2012 r., Kiedy zadałeś pytanie, nie widziałem prostszego rozwiązania niż próba ... złapania, którą podałeś. Jedyna alternatywa - sprawdzanie każdej części przed wartością null za pomocą „if” lub „?” wyglądał brzydko (ale może trochę szybciej).

Albo musiałeś napisać:

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

lub:

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

obie robią to bez obsługi wyjątków. Zauważ, że oba używają „skrótów”, aby przerwać sprawdzanie, jeśli zostanie znaleziona jakaś wartość null.

Aktualizacja (grudzień 2017): OdWersja C # 6 i wyższa, istnieje lepsze rozwiązanie, tzwElvis-Operator (znany również jako operator koalescencji zerowej)?. ix?[i] dla tablic), których można użyć. Powyższy przykład

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

w ten sposób wygląda o wiele ładniej:

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

który robi dokładnie to samo i jest IMHO znacznie bardziej niż „tylko” cukrem syntaktycznym. W połączeniu z dołączonym?? value możesz łatwo wymienićnull o jakąś inną wartość, np.

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

To sprawia, żepath zmienna pusta, jeśli nie można uzyskać wartości innej niż null.

4

Możesz napisać coś takiego:

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

Najprostszy sposób wdrożenia tegoNullHelpers.GetValueOrNull jest prawdopodobnie coś takiego:

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

Ale zdecydowanie najfajniejszym sposobem rozwiązania tego problemu jest użycie drzew Expression:

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()();
}

Jest to również najwolniejszy sposób wykonywania różnych czynności, ponieważ każde wywołanie wykonuje jedno lub wiele wywołań kompilatora JIT, ale ...

Nadal jest fajnie ;-)

AKTUALIZACJA (listopad 2014)

Nadchodzi C # 6 i będzie zawierać coś, co nazywająNull Propagation Operator, co oznacza, że ​​istnieje wsparcie językowe. Twój przykład można zapisać w C # 6 w następujący sposób:

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

Jeśli którakolwiek z części zawiera wartość null, pełne wyrażenie zwróci wartość null.

Nie sądzę, aby drugi przykład działał ... nie powinien zaczynać od dołu wyrażenia? AK_
@AK_: Właśnie takmembers.Reverse(); jest dla :-). Sprawdź to sam. Steven
2

Najkrótsza i najbardziej skuteczna ścieżka polega na wykonaniu testu zerowego na każdym poziomie. Do ponownego użycia możesz zawinąć ten kod do funkcji pomocniczej lub metody rozszerzenia. Pozwoli to na bezpieczny dostęp do niego za pośrednictwem tej funkcji, ale nadal konsekwentnie wykonuje kontrolę zerową.

Przykład:

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;
  }
}
Jest krótki, ponieważ zawiera kontrole w metodzie wielokrotnego użytku; Oznacza to, że za każdym razem, gdy chcesz uzyskać wartość, jest to tylko jedno wywołanie metody. Lepiej niż konsekwentnie wykonywać te same kontrole zerowe w wielu lokalizacjach, a także prostsze. STW
jak to jest krótkie? Zrobiłem coś bardzo podobnego do Ternaries ... AK_
11
[EDYTOWAĆ]

C # 6 został wydany jakiś czas temu i został dostarczony z operatorem propagującym zero?., co uprościłoby Twoją sprawę do:

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

Ze względów historycznych odpowiedź na poprzednie wersje językowe znajduje się poniżej.

Myślę, że szukasz bezpiecznego operatora dereferencji Groovy'ego?., i Ty jesteśnie pierwszy. W powiązanym temacie rozwiązaniem, które osobiście najbardziej lubię, jestten (ten też wygląda całkiem nieźle). Wtedy możesz po prostu zrobić:

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

Zawsze możesz skrócić nazwę funkcji. Spowoduje to zwrócenie wartości null, jeśli którykolwiek z obiektów w wyrażeniu ma wartość NULL, w przeciwnym wypadku ApplicationPath. W przypadku typów wartości należy wykonać jeden test zerowy na końcu. W każdym razie nie ma innej drogi, chyba że chcesz sprawdzić przeciwko null na każdym poziomie.

Oto metoda rozszerzenia zastosowana powyżej:

    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); 
            }       
    }
jakikolwiek rozsądny sposób na zastąpienie operatora, aby to zrobić? AK_
(Ciąg dalszy, bah, limit czasu na edycjach) Nie widzę sposobu na zastąpienie tej funkcji jako operator, biorąc pod uwagę, że akceptuje wyrażenie lambda - nie wiem, czy to możliwe, ale nawet gdyby było, byłoby brzydkie :). Co więcej, przeciążenie istniejącego operatora dla dowolnego typu (jeśli byłoby to możliwe) ujawniłoby zupełnie nowe pole dla kolizji i nieoczekiwanych WTF. Patryk Ćwiek
@AK_ Nie, nie możesz po prostu zdefiniować własnego operatora marki i nadpisać ... To byłoby paskudne w tym przypadku :) Zespół .NET jest świadomy „żądań” wykonanych dla tego rodzaju funkcjonalności, ale AFAIK wygrał wysyłamy w C # 5 ->tutaj itutaj Patryk Ćwiek
Dodano metodę rozszerzenia, której użyłeś w swoim przykładzie. Miłe rozwiązanie! Matt
Kocham to! Dodano również metodę rozszerzenia do naszej biblioteki ramowej. Timo Kosig

Powiązane pytania