Nota: Esta pergunta foi feita antes da introdução do .?
operador em C # 6 / Visual Studio 2015 .
Todos nós já estivemos lá, temos algumas propriedades profundas, como cake.frosting.berries.loader, que precisamos verificar se é nulo, para que não haja exceção. A maneira de fazer é usar uma instrução if de curto-circuito
if (cake != null && cake.frosting != null && cake.frosting.berries != null) ...
Isso não é exatamente elegante e talvez deva haver uma maneira mais fácil de verificar toda a cadeia e verificar se ela se depara com uma variável / propriedade nula.
É possível usar algum método de extensão ou seria um recurso de linguagem ou é apenas uma má idéia?
Respostas:
Consideramos adicionar uma nova operação "?." para o idioma que possui a semântica desejada. (E foi adicionado agora; veja abaixo.) Ou seja, você diria
e o compilador geraria todas as verificações de curto-circuito para você.
Não chegou ao C # 4. Talvez para uma versão futura hipotética da linguagem.
Atualização (2014): O
?.
operador agora está planejado para a próxima versão do compilador Roslyn. Observe que ainda há algum debate sobre a análise sintática e semântica exata do operador.Atualização (julho de 2015): o Visual Studio 2015 foi lançado e é fornecido com um compilador C # que suporta os operadores
?.
e condicionais nulos?[]
.fonte
Eu me inspirei nessa pergunta para tentar descobrir como esse tipo de verificação nula profunda pode ser feita com uma sintaxe mais fácil / mais bonita usando árvores de expressão. Embora eu concorde com as respostas afirmando que pode ser um design ruim se você precisar acessar instâncias profundas na hierarquia, também acho que, em alguns casos, como a apresentação de dados, pode ser muito útil.
Então, eu criei um método de extensão, que permitirá que você escreva:
Isso retornará os Berries se nenhuma parte da expressão for nula. Se nulo for encontrado, nulo será retornado. No entanto, existem algumas ressalvas: na versão atual, ele funciona apenas com acesso simples a membros e funciona no .NET Framework 4, porque usa o método MemberExpression.Update, que é novo na v4. Este é o código para o método de extensão IfNotNull:
Ele funciona examinando a árvore de expressões que representa sua expressão e avaliando as partes uma após a outra; sempre que verificar se o resultado não é nulo.
Estou certo de que isso pode ser estendido para que outras expressões que não sejam MemberExpression sejam suportadas. Considere isso como um código de prova de conceito e lembre-se de que haverá uma penalidade de desempenho ao usá-lo (o que provavelmente não será importante em muitos casos, mas não o use em um circuito fechado :-))
fonte
DynamicInvoke
lá. I religiosamente evitar que :)Eu achei essa extensão bastante útil para cenários de aninhamento profundo.
É uma ideia que tirei do operador coalescente nulo em C # e T-SQL. O bom é que o tipo de retorno é sempre o tipo de retorno da propriedade interna.
Dessa forma, você pode fazer isso:
... ou uma ligeira variação do anterior:
Não é a melhor sintaxe que conheço, mas funciona.
fonte
Além de violar a Lei de Demeter, como Mehrdad Afshari já apontou, parece-me que você precisa de "verificação nula profunda" para a lógica de decisão.
Geralmente, esse é o caso em que você deseja substituir objetos vazios por valores padrão. Nesse caso, você deve considerar a implementação do Padrão de Objeto Nulo . Ele atua como substituto de um objeto real, fornecendo valores padrão e métodos de "não ação".
fonte
Atualização: a partir do Visual Studio 2015, o compilador C # (versão 6 do idioma) agora reconhece o
?.
operador, o que facilita a "verificação nula profunda". Veja esta resposta para detalhes.Além de redesenhar seu código, como sugeriu esta resposta excluída , outra opção (ainda que terrível) seria usar um
try…catch
bloco para verificar se issoNullReferenceException
ocorre em algum momento durante essa pesquisa profunda de propriedade.Eu pessoalmente não faria isso pelos seguintes motivos:
NullReferenceException
Provavelmente, nunca deve ser capturado explicitamente. (Veja esta pergunta .)Isso seria quase certamente tem que ser um recurso de linguagem (que está disponível em C # 6 na forma do
.?
e?[]
operadores), a menos que C # já teve avaliação preguiçosa mais sofisticado, ou menos que você queira usar a reflexão (que provavelmente também não é uma boa ideia por razões de desempenho e segurança do tipo).Como não há como simplesmente passar
cake.frosting.berries.loader
para uma função (ela seria avaliada e lançaria uma exceção de referência nula), você teria que implementar um método de pesquisa geral da seguinte maneira: Ele pega objetos e nomes de propriedades para olho para cima:(Nota: código editado.)
Você vê rapidamente vários problemas com essa abordagem. Primeiro, você não obtém nenhum tipo de segurança e possível boxe de valores de propriedades de um tipo simples. Segundo, você pode retornar
null
se algo der errado e terá que verificar isso em sua função de chamada ou lançar uma exceção e voltar ao ponto em que começou. Terceiro, pode ser lento. Quarto, parece mais feio do que você começou.Eu ficaria com:
ou vá com a resposta acima de Mehrdad Afshari.
PS: Quando escrevi esta resposta, obviamente não considerava árvores de expressão para funções lambda; veja, por exemplo, a resposta do @driis 'para uma solução nessa direção. Também é baseado em um tipo de reflexão e, portanto, pode não ter um desempenho tão bom quanto em uma solução mais simples (
if (… != null & … != null) …
), mas pode ser considerado melhor do ponto de vista da sintaxe.fonte
Embora a resposta dos driis seja interessante, acho que é um desempenho um pouco caro demais. Em vez de compilar muitos delegados, prefiro compilar um lambda por caminho de propriedade, armazená-lo em cache e reinvocá-lo de vários tipos.
NullCoalesce abaixo faz exatamente isso, ele retorna uma nova expressão lambda com verificações nulas e um retorno de padrão (TResult) no caso de qualquer caminho ser nulo.
Exemplo:
Retornará uma expressão
Código:
fonte
Uma opção é usar o Null Object Patten; portanto, em vez de ter nulo quando você não tem um bolo, você tem um NullCake que retorna um NullFosting etc. Desculpe. Não sou muito bom em explicar isso, mas outras pessoas são.
fonte
Eu também frequentemente desejei uma sintaxe mais simples! Fica especialmente feio quando você tem valores de retorno de método que podem ser nulos, porque você precisa de variáveis extras (por exemplo
cake.frosting.flavors.FirstOrDefault().loader
:)No entanto, aqui está uma alternativa bastante decente que eu uso: crie um método auxiliar Null-Safe-Chain. Percebo que isso é bastante semelhante à resposta de @ John acima (com o
Coal
método de extensão), mas acho que é mais direto e menos digitado. Aqui está o que parece:Aqui está a implementação:
Também criei várias sobrecargas (com 2 a 6 parâmetros), bem como sobrecargas que permitem que a cadeia termine com um tipo de valor ou padrão. Isso funciona muito bem para mim!
fonte
Existe o projeto Talvez codeplex que Implementa Talvez ou IfNotNull usando lambdas para expressões profundas em C #
Exemplo de uso:
O link foi sugerido em uma pergunta semelhante. Como verificar nulos em uma expressão lambda profunda?
fonte
Como sugerido na John Leidegren 's resposta , uma abordagem para trabalho em torno isso é usar métodos de extensão e delegados. Usá-los pode ser algo como isto:
A implementação é confusa porque é necessário fazê-la funcionar para tipos de valor, tipos de referência e tipos de valor anuláveis. Você pode encontrar uma implementação completa em Timwi 's resposta para o que é a maneira correta para verificar se há valores nulos? .
fonte
Ou você pode usar a reflexão :)
Função de reflexão:
Uso:
My Case (retorne DBNull.Value em vez de nulo na função de reflexão):
fonte
Tente este código:
fonte
Postei isso ontem à noite e um amigo me indicou essa pergunta. Espero que ajude. Você pode fazer algo assim:
Leia o post completo do blog aqui .
O mesmo amigo também sugeriu que você assista a isso .
fonte
Expression
se você está indo apenas para compilar e pegar? Basta usar umFunc<T>
.Modifiquei levemente o código daqui para fazê-lo funcionar na pergunta:
E sim, essa provavelmente não é a solução ideal devido a implicações de desempenho de tentativa / captura, mas funciona:>
Uso:
fonte
Onde você precisa conseguir isso, faça o seguinte:
Uso
ou
Implementação da classe auxiliar
fonte
Eu gosto da abordagem adotada pelo Objective-C:
"A linguagem Objective-C adota outra abordagem para esse problema e não invoca métodos em zero, mas retorna nulo para todas essas invocações".
fonte