C # manière élégante de vérifier si la propriété d’une propriété est nulle

En C #, dites que vous souhaitez supprimer une valeur de PropertyC dans cet exemple et que ObjectA, PropertyA et PropertyB peuvent tous être nuls.

ObjectA.PropertyA.PropertyB.PropertyC

Comment puis-je obtenir PropertyC en toute sécurité avec le moins de code possible?

En ce moment je vérifierais:

if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null) { // safely pull off the value int value = objectA.PropertyA.PropertyB.PropertyC; } 

Ce serait bien de faire quelque chose de plus comme ça (pseudo-code).

 int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal; 

Peut-être encore plus réduit avec un opérateur de coalescence nulle.

EDIT A l’ origine, j’ai dit que mon deuxième exemple était comme js, mais je l’ai changé en psuedo-code car il a été correctement souligné qu’il ne fonctionnerait pas avec js.

En C # 6, vous pouvez utiliser l’opérateur conditionnel Null. Le test original sera donc:

  int? value = objectA?.PropertyA?.PropertyB?.PropertyC; 

Méthode d’extension courte:

 public static TResult IfNotNull(this TInput o, Func evaluator) where TResult : class where TInput : class { if (o == null) return null; return evaluator(o); } 

En utilisant

 PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC); 

Cette méthode d’extension simple et bien plus, vous pouvez trouver sur http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/

MODIFIER:

Après l’avoir utilisé pour l’instant, je pense que le nom approprié pour cette méthode devrait être IfNotNull () au lieu de original avec ().

Pouvez-vous append une méthode à votre classe? Si non, avez-vous pensé à utiliser des méthodes d’extension? Vous pouvez créer une méthode d’extension pour votre type d’object appelé GetPropC() .

Exemple:

 public static class MyExtensions { public static int GetPropC(this MyObjectType obj, int defaltValue) { if (obj != null && obj.PropertyA != null & obj.PropertyA.PropertyB != null) return obj.PropertyA.PropertyB.PropertyC; return defaltValue; } } 

Usage:

 int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue) 

En passant, cela suppose que vous utilisez .NET 3 ou supérieur.

La façon dont vous le faites est correcte.

Vous pouvez utiliser une astuce comme celle décrite ici , en utilisant les expressions Linq:

 int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0); 

Mais c’est beaucoup plus lent que de vérifier manuellement chaque propriété …

Refactor pour observer la loi de Déméter

Vous êtes évidemment à la recherche du Nullable Monad :

 ssortingng result = new A().PropertyB.PropertyC.Value; 

devient

 ssortingng result = from a in new A() from b in a.PropertyB from c in b.PropertyC select c.Value; 

Cela renvoie null , si l’une des propriétés nullable est null; sinon, la valeur de la Value .

 class A { public B PropertyB { get; set; } } class B { public C PropertyC { get; set; } } class C { public ssortingng Value { get; set; } } 

Méthodes d’extension LINQ:

 public static class NullableExtensions { public static TResult SelectMany( this TOuter source, Func innerSelector, Func resultSelector) where TOuter : class where TInner : class where TResult : class { if (source == null) return null; TInner inner = innerSelector(source); if (inner == null) return null; return resultSelector(source, inner); } } 

Mise à jour 2014: C # 6 a un nouvel opérateur ?. divers appelé “navigation sûre” ou “propagation de null”

 parent?.child 

Lisez http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-call-the-safe-navigation-operator.aspx pour plus de détails.

Cela a longtemps été une demande extrêmement populaire

Ce code est “le moins de code”, mais pas la meilleure pratique:

 try { return ObjectA.PropertyA.PropertyB.PropertyC; } catch(NullReferenceException) { return null; } 

En supposant que vous avez des valeurs vides de types une approche serait la suivante:

 var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfSsortingng; 

Je suis un grand fan de C # mais une très bonne chose dans le nouveau Java (1.7?) Est le. opérateur:

  var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfSsortingng; 

Consultez ce billet de blog . Je pense que c’est une méthode très élégante pour les vérifications nulles en chaîne. Il y a beaucoup d’implémentations similaires de ceci, mais j’aime celle-ci parce qu’elle arrête l’évaluation dès qu’un null est trouvé dans la chaîne.

Tout le code source est sur github .

Quand j’ai besoin d’enchaîner des appels comme ça, je me fie à une méthode d’aide que j’ai créée, TryGet ():

  public static U TryGet(this T obj, Func func) { return obj.TryGet(func, default(U)); } public static U TryGet(this T obj, Func func, U whenNull) { return obj == null ? whenNull : func(obj); } 

Dans votre cas, vous l’utiliseriez ainsi:

  int value = ObjectA .TryGet(p => p.PropertyA) .TryGet(p => p.PropertyB) .TryGet(p => p.PropertyC, defaultVal); 

J’ai vu quelque chose dans le nouveau C # 6.0, c’est en utilisant ‘?’ au lieu de vérifier

par exemple au lieu d’utiliser

 if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null) { var city = person.contact.address.city; } 

vous utilisez simplement

 var city = person?.contact?.address?.city; 

J’espère que ça a aidé quelqu’un.


METTRE À JOUR:

Vous pourriez faire comme ça maintenant

  var city = (Person != null)? ((Person.Contact!=null)? ((Person.Contact.Address!= null)? ((Person.Contact.Address.City!=null)? Person.Contact.Address.City : null ) :null) :null) : null; 

Vous pourriez faire ceci:

 class ObjectAType { public int PropertyC { get { if (PropertyA == null) return 0; if (PropertyA.PropertyB == null) return 0; return PropertyA.PropertyB.PropertyC; } } } if (ObjectA != null) { int value = ObjectA.PropertyC; ... } 

Ou encore mieux:

 private static int GetPropertyC(ObjectAType objectA) { if (objectA == null) return 0; if (objectA.PropertyA == null) return 0; if (objectA.PropertyA.PropertyB == null) return 0; return objectA.PropertyA.PropertyB.PropertyC; } int value = GetPropertyC(ObjectA); 

Ce n’est pas possible. ObjectA.PropertyA.PropertyB échouera si ObjectA est null en raison d’un déréférencement nul, ce qui est une erreur.

if (ObjectA! = null && ObjectA.PropertyA … fonctionne en raison d’un court-circuit, c.-à-d. que ObjectA.PropertyA ne sera jamais vérifié si ObjectA est nul.

La première manière que vous proposez est la meilleure et la plus claire avec l’intention. Si quoi que ce soit, vous pourriez essayer de repenser sans avoir à compter sur autant de valeurs NULL.

Je suis tombé sur ce post.

Il y a quelque temps j’ai fait une suggestion sur Visual Studio Connect concernant l’ajout d’un nouveau ??? opérateur.

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4104392-add-as-an-recursive-null-reference-check-opera

Cela nécessiterait un peu de travail de la part de l’équipe Framework, mais vous n’avez pas besoin de modifier le langage, mais juste de faire de la magie avec le compilateur. L’idée était que le compilateur devrait changer ce code (syntaxe non autorisée atm)

 ssortingng product_name = Order.OrderDetails[0].Product.Name ??? "no product defined"; 

dans ce code

 Func _get_default = () => "no product defined"; ssortingng product_name = Order == null ? _get_default.Invoke() : Order.OrderDetails[0] == null ? _get_default.Invoke() : Order.OrderDetails[0].Product == null ? _get_default.Invoke() : Order.OrderDetails[0].Product.Name ?? _get_default.Invoke() 

Pour une vérification nulle, cela pourrait ressembler à

 bool isNull = (Order.OrderDetails[0].Product ??? null) == null; 

vous pouvez utiliser l’extension suivante et je pense que c’est vraiment bien:

 ///  /// Simplifies null checking ///  public static TR Get(TF t, Func f) where TF : class { return t != null ? f(t) : default(TR); } ///  /// Simplifies null checking ///  public static TR Get(T1 p1, Func p2, Func p3) where T1 : class where T2 : class { return Get(Get(p1, p2), p3); } ///  /// Simplifies null checking ///  public static TR Get(T1 p1, Func p2, Func p3, Func p4) where T1 : class where T2 : class where T3 : class { return Get(Get(Get(p1, p2), p3), p4); } 

Et il est utilisé comme ceci:

 int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC); 

Propagation nulle prévue dans C # vNext, propulsée par Roslyn


Pas une réponse, mais plus d’une mise à jour. Il semble que UserVoice ait piloté cela, avec d’autres nouveautés, depuis Roslyn et qu’il s’agit apparemment d’un ajout prévu:

https://roslyn.codeplex.com/discussions/540883

J’écrirais votre propre méthode dans le type de PropertyA (ou une méthode d’extension si ce n’est pas votre type) en utilisant le modèle similaire au type Nullable.

 class PropertyAType { public PropertyBType PropertyB {get; set; } public PropertyBType GetPropertyBOrDefault() { return PropertyB != null ? PropertyB : defaultValue; } } 

Cette approche est assez simple une fois que vous avez dépassé le lambda gobbly-gook:

 public static TProperty GetPropertyOrDefault(this TObject model, Func valueFunc) where TObject : class { try { return valueFunc.Invoke(model); } catch (NullReferenceException nex) { return default(TProperty); } } 

Avec un usage qui pourrait ressembler à:

 ObjectA objectA = null; Assert.AreEqual(0,objectA.GetPropertyOrDefault(prop=>prop.ObjectB.ObjectB.ObjectC.ID)); Assert.IsNull(objectA.GetPropertyOrDefault(prop => prop.ObjectB)); 

J’ai écrit une méthode qui accepte une valeur par défaut, voici comment l’utiliser:

 var teacher = new Teacher(); return teacher.GetProperty(t => t.Name); return teacher.GetProperty(t => t.Name, "Default name"); 

Voici le code:

 public static class Helper { ///  /// Gets a property if the object is not null. /// var teacher = new Teacher(); /// return teacher.GetProperty(t => t.Name); /// return teacher.GetProperty(t => t.Name, "Default name"); ///  public static TSecond GetProperty(this TFirst item1, Func getItem2, TSecond defaultValue = default(TSecond)) { if (item1 == null) { return defaultValue; } return getItem2(item1); } } 
 var result = nullableproperty ?? defaultvalue; 

Le ?? L’opérateur signifie que si le premier argument est nul, renvoyez le second à la place.