Jetez un œil à ce code:
var categories = tokens.SelectMany(x => x.Categories); if (categories != null) { if (categories.Contains("interp")) //null ref exception { return null; } }
J'obtiens une exception de référence nulle quand j'essaye de trouver la chaîne "interp" dans les catégories. Il semble donc que "categories! = Null" ne fonctionne pas.
J'ai trouvé quelques suggestions (ici Comment pour vérifier si IEnumerable est nul ou vide? ) mais ils impliquent d'utiliser .Any (). Mais cela ne rend l'exception que plus tôt (en utilisant .Any ()). Même? .Any () lève l'exception.
Des idées?
4 Réponses :
Lorsque vous travaillez avec des collections et IEnumerable
, évitez d'utiliser null
; si vous n'avez rien à renvoyer, renvoyez une collection vide (et non null
).
Dans votre cas particulier, SelectMany
ne renverra jamais null
, mais collection vide, c'est pourquoi la vérification de categories! = null
est inutile ,
et vous devez vérifier jetons
à la place
// if tokens is null change it for an empty collection tokens = tokens ?? new MyToken[0]; ... if (tokens .Where(x => x != null && x.Categories != null) .SelectMany(x => x.Categories) .Contains("interp")) return null;
Cependant, la vérification constante de null
rend le code illisible , c'est pourquoi essayez de vérifier null
once:
if (null != tokens) // Where(x => x != null) - to be on the safe side if x == null or x.Categories == null if (tokens .Where(x => x != null && x.Categories != null) .SelectMany(x => x.Categories) .Contains("interp")) return null;
var categories = tokens.SelectMany (x => x.Categories) .ToList ();
ajoutez .ToList () et vous devriez en savoir plus sur l'emplacement de l'erreur avec ces informations que nous avons dans le post, nous ne pouvons que deviner
ToList ();
- essayez d'éviter la matérialisation lorsque vous n'avez pas à le faire; imagerie que tokens.SelectMany
renvoie généralement millions d'éléments (quelle énorme liste que nous créons pour rien puisque Contains
ne le veut pas)
Ce code lancera un NRE dans categories.Contains
uniquement si la propriété Categories est nulle.
Le code suivant lancera:
class Token { string[] _categories=new string[0]; public string[] Categories{ get => _categories; set => _categories = value??new string[0]; } }
Mais il en irait de même
var categories = tokens.Where(x=>x.Categories!=null).SelectMany(x => x.Categories);
La chose qui lance réellement est le itérateur dans SelectMany , pas ToArray ()
ou Contains
. La trace de pile pour cette exception est:
at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext() at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value, IEqualityComparer`1 comparer) at UserQuery.Main()
SelectMany
essaiera d'itérer sur chaque entrée Categories
, trouver que le La propriété est en fait nulle et rejetée.
La solution rapide consiste à ajouter un Where
avant SelectMany
pour éliminer les catégories nulles:
tokens.SelectMany(x => x.Categories).ToArray();
La vraie solution est de s'assurer que Catégories
n'est jamais vide - il devrait être initialisé à un tableau, une liste vide, quoi que ce soit lors de la construction. Lorsqu'il est réaffecté, il ne doit jamais être défini sur null.
Cet exemple définit le champ _categories
sur nouvelle chaîne [0]
même si un appelant passe null aux catégories
class Token { public string[] Categories{get;set;} } var tokens=new []{new Token()}; var categories = tokens.SelectMany(x => x.Categories); if (categories != null) { if (categories.Contains("interp")) { Console.WriteLine("Found"); } }
Avec cela, Where (x => x.Categories! = null)
n'est plus nécessaire
C'est la seule réponse qui semble avoir raison. Les jetons
ne peuvent jamais être nuls si SelectMany
ne lève pas d'exception. Upvote!
Peut utiliser la clause where
et en faire une liste, puis vérifier simplement s'il y a un élément dans la liste
var categories = list.Where(x => x.Categories.Contains("interp")).ToList(); if (categories.Count() == 0) { return null; }
Vérifiez
tokens! = Null
sitokens
n'est pasnull
,SelectMany
ne renverra jamaisnull code > mais énumérable vide
Peut-être qu'un élément de
tokens
estnull
.x.Categories
lèverait alors une exception lorsque vous appelezContains
.Les vérifications nulles fonctionnent. Si vous obtenez un NRE, cela signifie que quelque chose d'autre a échoué. Publiez le texte d'exception complet , y compris la pile d'appels. C'est peut-être une ligne différente qui a jeté, ou
Enumerable.Contains
a jeté parce quecategories
est vide. Ou peut-être quex.Categories
contenait en fait des entréesnull
qui se sont retrouvées dans la variablecategories
.categories! = null
vérifie lescatégories
énumérables, pas son contenu. S'il y a une chance quex.Categories
contienne des valeurs nulles, vous devez inclure unWhere (category => category! = Null)
Quel type est votre selectmany de retour?
@DmitryBychenko Si
tokens == null
,SelectMany
doit cependant lancer uneArgumentNullException
. Est-ce que je manque quelque chose?Ce serait génial si vous pouviez fournir un exemple reproductible minimal . Quel est le type de
catégories
?Exécutez cette ligne de code
if (categories.Contains ("interp"))
. Lorsque vous cliquez dessus, veuillez accéder à laFenêtre Exécution
et saisir les? Catégories
. Qu'est-ce qui est retourné? Et pour? Categories.Count ()
? Qu'en est-il des? Categories.GetType ()
?@ Peter82 cette question doit être fermée soit comme peu claire, soit comme un double de Qu'est-ce qu'une NullReferenceException et comment la corriger? . Il n'y a aucun moyen de reproduire ce que la question prétend - un Enumerable emtpy ne lance pas. La comparaison avec null n'est pas cassée, des millions de développeurs l'auraient remarqué si c'était le cas. Vous devez publier un exemple reproductible et le texte de l'exception complet , y compris la pile d'appels. Vous pouvez l'obtenir facilement avec une simple
Exception.ToString ()
.