Pergunta sobre syntax, .net, c#, web – Maneira elegante de evitar NullReferenceException em c #

7

Eu quero fazer isso

var path = HttpContext.Current.Request.ApplicationPath;

Se alguma das propriedades ao longo do caminho for nula, quero que o caminho seja nulo ou "" seria melhor.

Existe uma maneira elegante de fazer isso sem ternários?

Idealmente, eu gostaria que esse comportamento (sem o horrível desempenho e fealdade) caminho string;

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

Obrigado

Eu interpretei mal e excluí o comentário Carl Winder

Sua resposta

4   a resposta
11
[EDITAR]

opagação de nulo?., o que simplificaria seu caso para:

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

Por razões históricas, a resposta para versões anteriores do idioma pode ser encontrada abaixo.

Eu acho que você está procurando o operador de desreferenciamento seguro do Groovy?., e você énão o primeiro. Do tópico vinculado, a solução que eu pessoalmente gosto mais éeste (aquele parece muito bom também). Então você pode apenas fazer:

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

Você sempre pode encurtar o nome da função um pouco. Isso retornará null se qualquer um dos objetos na expressão for nulo, caso contrário, o ApplicationPath. Para tipos de valor, você teria que realizar uma verificação nula no final. De qualquer forma, não há outro jeito até agora, a menos que você queira checar nulo em todos os níveis.

Aqui está o método de extensão usado acima:

    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); 
            }       
    }
(Continuação, bah, timeout em edições) Eu não consigo ver uma maneira de substituir essa funcionalidade como um operador, considerando que aceita uma expressão lambda - eu não sei se isso é possível, mas mesmo se fosse, seria feio :) Além disso, sobrecarregar um operador existente para um tipo arbitrário (se é que isso era possível) revelaria um novo campo para colisões e WTFs inesperadas. Patryk Ćwiek
alguma maneira sensata de substituir um operador para fazer isso? AK_
Adicionado o método de extensão que você usou no seu exemplo. Boa solução! Matt
@AK_ Não, você não pode simplesmente definir sua própria operadora de marca e ignorar ... Isso seria desagradável nesse caso :) A equipe do .NET está ciente das "solicitações" feitas para esse tipo de funcionalidade, mas a AFAIK ganhou é enviado em C # 5 ->Aqui eAqui Patryk Ćwiek
Adoro! Adicionado o método de extensão à nossa biblioteca de framework também. Timo Kosig
2

quando você fez a pergunta, eu não vi uma solução mais simples do que o try ... catch you provided. A única alternativa - verificar cada parte contra nulo com "se" ou "?" parecia mais feio (mas é possivelmente um pouco mais rápido).

Ou você teve que escrever:

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

ou:

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

ambos estão fazendo isso sem manipulação de exceção. Note que ambos estão usando "atalhos" para anular a verificação se algum valor nulo for encontrado.

Atualização (dezembro de 2017): Desde aC # versão 6 e superior, existe uma solução melhor disponível, os chamadosElvis-Operador (também conhecido como operador de coalescência de nulos?. ex?[i] para matrizes), que você pode usar. O exemplo acima

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

parece muito melhor assim:

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

que faz exatamente o mesmo e é IMHO muito mais do que "apenas" açúcar sintático. Combinado com um anexo?? value você pode facilmente substituirnull por algum outro valor, por ex.

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

Isso faz com que opath variável vazia se nenhum valor não nulo puder ser obtido.

2

todos os níveis. Para reutilização, você pode incluir esse código em uma função auxiliar ou talvez em um método de extensão. Isso permitirá que você o acesse com segurança por meio dessa função, mas ainda execute consistentemente a verificação nula.

Exemplo:

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;
  }
}
como isso é curto? Eu fiz algo muito parecido com os ternários ... AK_
É curto porque encapsula as verificações em um método reutilizável; o que significa que sempre que você quiser obter o valor, é apenas uma única chamada de método. É melhor do que tentar executar consistentemente as mesmas verificações de nulos em muitos locais e também mais simples. STW
4

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

A maneira mais simples de implementar issoNullHelpers.GetValueOrNull é provavelmente algo assim:

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

Mas, de longe, a maneira mais legal de resolver isso é usando árvores de expressão:

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

Esta é também a maneira mais lenta de fazer as coisas, já que cada chamada fará uma ou mais invocações do compilador JIT, mas ...

Ainda é legal ;-)

ATUALIZAÇÃO (novembro de 2014)

C # 6 está chegando e ele irá conter algo que eles chamam deOperador de Propagação Nula, o que significa que há suporte de idioma para isso. Seu exemplo pode ser escrito da seguinte maneira em C # 6:

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

Se alguma das partes contiver nulo, a expressão completa retornará nulo.

Eu não acho que o segundo exemplo funciona ... você não deveria começar do fundo da expressão? AK_
@AK_: Isso é o quemembers.Reverse(); é para :-). Teste você mesmo. Steven

Perguntas relacionadas