Quando você cria um método de extensão, é claro, pode chamá-lo null
. Mas, diferentemente de uma chamada de método de instância, chamá-lo como null não precisa gerar um NullReferenceException
-> você deve verificar e ativá-lo manualmente.
Para a implementação do método de extensão Linq, a Any()
Microsoft decidiu que eles deveriam lançar um ArgumentNullException
( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ).
Me irrita ter que escrever if( myCollection != null && myCollection.Any() )
Como cliente deste código, estou errado em esperar que, por exemplo ((int[])null).Any()
, retorne false
?
c#
.net
linq
extension-method
esta extensão
fonte
fonte
null |> Seq.isEmpty
lançaSystem.ArgumentNullException: Value cannot be null
. A expectativa parece ser que você não passará valores indefinidos para algo que se espera que exista, portanto, ainda é uma exceção quando você tem um nulo. Se é uma questão de inicialização, eu começaria com uma sequência vazia em vez de anull
.null
ao lidar com coleções, mas usar uma coleção vazia nesses casos.Any
é apenas consistente.Respostas:
Eu tenho uma sacola com cinco batatas. Existem
.Any()
batatas na sacola?" Sim " , você diz.
<= true
Pego todas as batatas e as como. Existem
.Any()
batatas na sacola?" Não " , você diz.
<= false
Eu incinero completamente a bolsa no fogo. Existem
.Any()
batatas na bolsa agora?" Não há bolsa ."
<= ArgumentNullException
fonte
Primeiro, parece que esse código-fonte será lançado
ArgumentNullException
, nãoNullReferenceException
.Dito isto, em muitos casos, você já sabe que sua coleção não é nula, porque esse código é chamado apenas a partir de um código que sabe que a coleção já existe, portanto você não precisará inserir a verificação nula com muita frequência. Mas se você não sabe que ele existe, faz sentido verificar antes de chamá
Any()
-lo.Sim. A pergunta que
Any()
responde é "esta coleção contém algum elemento?" Se essa coleção não existir, a própria pergunta não fará sentido; não pode conter nem não conter nada, porque não existe.fonte
null
é equivalente a um conjunto contendo o conjunto vazio (e vice-versa)?{ null }
e{ {} }
deve ser equivalente. Eu acho isso fascinante; Não consigo me relacionar com isso..Add
umanull
coleção, de alguma forma, também devemos criar uma coleção para nós?Nulo significa falta de informação, não há elementos.
Você pode evitar um nulo mais amplo - por exemplo, use um dos enumeráveis vazios internos para representar uma coleção sem elementos em vez de nulo.
Se você retornar nulo em algumas circunstâncias, poderá alterar isso para retornar a coleção vazia. (Caso contrário, se você encontrar nulos retornados pelos métodos da biblioteca (não o seu), isso é lamentável, e eu os agruparia para normalizar.)
Consulte também https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty
fonte
null
não significa que nenhum elemento é uma questão de usar uma convenção sensata. Basta pensar sobre OLESTRINGS nativas, onde ele realmente faz dizer isso.null
não significa "falta de informação".null
não tem uma única interpretação significativa. Em vez disso, é uma questão de como você o trata.null
às vezes é usado para "informações ausentes", mas também para "nenhum elemento" e também para "um erro" ou "informações não inicializadas" ou "informações conflitantes" ou algo arbitrário e ad-hoc.null
é absolutamente usado com frequência para significar "sem dados". Às vezes faz sentido. Outras vezes, não.Além da sintaxe condicional nula , há outra técnica para aliviar esse problema: não deixe sua variável permanecer
null
.Considere uma função que aceita uma coleção como parâmetro. Se, para os fins da função,
null
e vazio forem equivalentes, você poderá garantir que ela nunca contenhanull
no início:Você pode fazer o mesmo ao buscar coleções de outro método:
(Observe que nos casos em que você tem controle sobre uma função como
GetWords
enull
é equivalente à coleção vazia, é preferível retornar a coleção vazia em primeiro lugar.)Agora você pode realizar qualquer operação que desejar na coleção. Isso é especialmente útil se você precisar executar muitas operações que falhariam quando a coleção
null
ocorrer e, nos casos em que você obtiver o mesmo resultado fazendo um loop ou consultando um enumerável vazio, permitirá eliminarif
completamente as condições.fonte
Sim, simplesmente porque você está em C # e esse comportamento é bem definido e documentado.
Se você estava criando sua própria biblioteca ou se estava usando um idioma diferente com diferentes culturas de exceção, seria mais razoável esperar falso.
Pessoalmente, sinto que o retorno falso é uma abordagem mais segura que torna seu programa mais robusto, mas é pelo menos discutível.
fonte
Se as verificações nulas repetidas o incomodarem, você poderá criar seu próprio método de extensão 'IsNullOrEmpty ()', para espelhar o método String com esse nome e agrupar a verificação nula e a chamada .Any () em uma única chamada.
Caso contrário, a solução, mencionada por 17 de 26 em um comentário da sua pergunta, também é mais curta que o método 'padrão' e razoavelmente clara para qualquer pessoa familiarizada com a nova sintaxe nulo-condicional.
fonte
?? false
vez de== true
. Isso me pareceria mais explícito (mais limpo), pois lida apenas com o caso denull
, e não é realmente mais detalhado. Além disso,==
verificações de booleanos geralmente acionam meu reflexo de vômito. =)myCollection?.Any()
suficiente?myCollection?.Any()
efetivamente retorna um booleano anulável em vez de um booleano comum (ou seja, booleano? Em vez de booleano). Não há conversão implícita do bool? para bool, e é um bool que precisamos neste exemplo em particular (para testar como parte daif
condição). Portanto, devemos comparar explicitamentetrue
ou fazer uso do operador de coalescência nula (??).?.
.==
realmente vai funcionar. Eu já sei o que acontece com um booleano intuitivamente quando só pode sertrue
oufalse
; Nem preciso pensar nisso. Mas joguenull
na mistura, e agora eu tenho que descobrir se isso==
é certo.??
em vez disso, apenas elimina onull
caso, reduzindo-o novamente paratrue
efalse
, que você já entende imediatamente. Quanto ao meu reflexo de vômito, quando o vejo pela primeira vez, meu instinto imediato é apenas excluí-lo, pois geralmente é inútil, o que você não quer que aconteça.Se você se pergunta sobre expectativas, precisa pensar em intenções.
null
significa algo muito diferente deEnumerable.Empty<T>
Como mencionado na resposta de Erik Eidt , há uma diferença de significado entre
null
e uma coleção vazia.Vamos primeiro dar uma olhada em como eles devem ser usados.
O livro Framework Design Guidelines: Convenções, expressões idiomáticas e padrões para bibliotecas .NET reutilizáveis, 2ª edição, escritas pelos arquitetos da Microsoft Krzysztof Cwalina e Brad Abrams, afirma as seguintes práticas recomendadas:
Considere chamar um método que está obtendo dados de um banco de dados: se você receber uma matriz vazia ou
Enumerable.Empty<T>
isso simplesmente significa que seu espaço de amostra está vazio, ou seja, seu resultado é um conjunto vazio . Recebernull
neste contexto, no entanto, significaria um estado de erro .Na mesma linha de pensamento da analogia da batata de Dan Wilson , faz sentido fazer perguntas sobre seus dados, mesmo que sejam um conjunto vazio . Mas faz muito menos sentido, se não houver um conjunto .
fonte
null
e vazio pode resultar no mesmo valor de retorno para a função, mas isso não significa quenull
e vazio significa exatamente a mesma coisa no escopo da chamada.Existem muitas respostas que explicam o porquê
null
e o vazio são diferentes e opiniões suficientes tentam explicar por que eles devem ser tratados de maneira diferente ou não. No entanto, você está perguntando:É uma expectativa perfeitamente razoável . Você está tão certo quanto alguém que defende o comportamento atual. Concordo com a filosofia de implementação atual, mas os fatores determinantes não se baseiam apenas em considerações fora do contexto.
Dado que
Any()
sem predicado é essencialmenteCount() > 0
, o que você espera desse snippet?Ou um genérico:
Suponho que você espera
NullReferenceException
.Any()
é um método de extensão, ele deve se integrar perfeitamente ao objeto estendido e, em seguida, lançar uma exceção é a coisa menos surpreendente.Enumerable.Any(null)
e lá definitivamente esperaArgumentNullException
. É o mesmo método e deve ser consistente com - possivelmente - quase TUDO no Framework.null
objeto é um erro de programação , a estrutura não deve impor nulo como valor mágico . Se você usá-lo dessa maneira, é sua responsabilidade lidar com isso.Count()
parece uma decisão fácil,Where()
não é. Que talMax()
? Ele lança uma exceção para uma lista VAZIA, não deveria lançar também para umanull
?O que os designers de bibliotecas fizeram antes do LINQ foi introduzir métodos explícitos quando
null
é um valor válido (por exemploString.IsNullOrEmpty()
), então eles tinham que ser consistentes com a filosofia de design existente . Dito isto, mesmo se bastante trivial para escrever, dois métodosEmptyIfNull()
eEmptyOrNull()
pode ser útil.fonte
Jim deve deixar batatas em cada saco. Caso contrário, eu vou matá-lo.
Jim tem uma sacola com cinco batatas. Existem
.Any()
batatas na sacola?"Sim", você diz. <= true
Ok, então Jim vive desta vez.
Jim pega todas as batatas e as come. Existem. Alguma () batata na sacola?
"Não", você diz. <= false
Hora de matar Jim.
Jim incinera completamente a bolsa no fogo. Existem. Alguma () batata na bolsa agora?
"Não há bolsa." <= ArgumentNullException
Jim deveria viver ou morrer? Bem, não esperávamos isso, então eu preciso de uma decisão. Deixar Jim se safar com isso é um bug ou não?
Você pode usar anotações para sinalizar que não está suportando nenhuma travessia nula dessa maneira.
Mas sua cadeia de ferramentas precisa apoiá-la. O que significa que você provavelmente ainda acabará escrevendo cheques.
fonte
Se isso te incomoda muito, sugiro um método de extensão simples.
Agora você pode fazer isso:
... e o sinalizador será definido como
false
.fonte
return
declaração como:return source ?? Enumerable.Empty<T>();
Esta é uma pergunta sobre os métodos de extensão do C # e sua filosofia de design. Portanto, acho que a melhor maneira de responder a essa pergunta é citar a documentação do MSDN com a finalidade dos métodos de extensão :
Para resumir, os métodos de extensão são projetados para adicionar métodos de instância a um tipo específico, mesmo quando os desenvolvedores não podem fazê-lo diretamente. E como os métodos de instância sempre substituem os métodos de extensão, se presentes (se chamados usando a sintaxe do método de instância), isso só deve ser feito se você não puder adicionar diretamente um método ou estender a classe. *
Em outras palavras, um método de extensão deve agir como um método de instância, porque pode acabar sendo um método de instância por algum cliente. E como um método de instância deve ser lançado se o objeto no qual está sendo chamado for
null
, o método de extensão também deve ser.* Como observação lateral, esta é exatamente a situação que os designers do LINQ enfrentaram: quando o C # 3.0 foi lançado, já havia milhões de clientes que estavam usando
System.Collections.IEnumerable
eSystem.Collections.Generic.IEnumerable<T>
, tanto em suas coleções quanto emforeach
loops. Essas classes retornouIEnumerator
objetos que só tinham os dois métodosCurrent
eMoveNext
, por isso, a adição de quaisquer métodos de instância necessárias adicionais, comoCount
,Any
, etc., seria quebrar esses milhões de clientes. Portanto, para fornecer essa funcionalidade (especialmente porque ela pode ser implementada em termos deCurrent
eMoveNext
com relativa facilidade), eles a lançaram como métodos de extensão, que podem ser aplicados a qualquer atualmente existenteIEnumerable
instância e também pode ser implementado por classes de maneiras mais eficientes. Se os designers do C # decidissem liberar o LINQ no primeiro dia, ele seria fornecido como método de instânciaIEnumerable
e provavelmente projetariam algum tipo de sistema para fornecer implementações de interface padrão desses métodos.fonte
Acho que o ponto mais importante aqui é que, retornando false, em vez de lançar uma exceção, você está ofuscando informações que podem ser relevantes para futuros leitores / modificadores do seu código.
Se for possível que a lista seja nula, sugiro ter um caminho lógico separado para isso; caso contrário, no futuro, alguém poderá adicionar alguma lógica baseada em lista (como uma adição) ao resto {} do seu if, resultando em um exceção indesejada que eles não tinham motivos para prever.
A legibilidade e a manutenção superam 'Eu tenho que escrever uma condição extra' toda vez.
fonte
Como regra geral, escrevo a maior parte do meu código para assumir que o chamador é responsável por não me fornecer dados nulos, principalmente pelos motivos descritos em Diga não ao nulo (e outras postagens semelhantes). O conceito de nulo geralmente não é bem compreendido e, por esse motivo, é aconselhável inicializar suas variáveis antes do tempo, tanto quanto possível. Supondo que você esteja trabalhando com uma API razoável, o ideal é que você nunca recupere um valor nulo, portanto nunca precisará procurar nulo. O ponto nulo, como outras respostas indicaram, é garantir que você tenha um objeto válido (por exemplo, uma "bolsa") para trabalhar antes de continuar. Este não é um recurso do
Any
, mas um recurso do idiomaem si. Você não pode executar a maioria das operações em um objeto nulo, porque ele não existe. É como se eu pedisse para você dirigir até a loja do meu carro, exceto que eu não tenho carro, então você não tem como usar o meu carro para ir até a loja. Não faz sentido executar uma operação em algo que literalmente não existe.Existem casos práticos para o uso de nulo, como quando você literalmente não tem as informações solicitadas disponíveis (por exemplo, se eu lhe perguntar qual é a minha idade, a opção mais provável para você responder nesse momento é "Eu não sei"). ", que é o valor nulo). No entanto, na maioria dos casos, você deve pelo menos saber se suas variáveis foram inicializadas ou não. E se não o fizer, recomendo que você reforce seu código. A primeira coisa que faço em qualquer programa ou função é inicializar um valor antes de usá-lo. É raro precisar verificar valores nulos, pois posso garantir que minhas variáveis não sejam nulas. É um bom hábito entrar, e quanto mais você se lembrar de inicializar suas variáveis, menos frequentemente você precisa verificar se há nulo.
fonte