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! = Nullsitokensn'est pasnull,SelectManyne renverra jamaisnull code > mais énumérable videPeut-être qu'un élément de
tokensestnull.x.Categorieslè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.Containsa jeté parce quecategoriesest vide. Ou peut-être quex.Categoriescontenait en fait des entréesnullqui se sont retrouvées dans la variablecategories.categories! = nullvérifie lescatégoriesénumérables, pas son contenu. S'il y a une chance quex.Categoriescontienne des valeurs nulles, vous devez inclure unWhere (category => category! = Null)Quel type est votre selectmany de retour?
@DmitryBychenko Si
tokens == null,SelectManydoit 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écutionet 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 ().