En C #, dites que vous voulez extraire une valeur de PropertyC dans cet exemple et que ObjectA, PropertyA et PropertyB peuvent tous être null.
ObjectA.PropertyA.PropertyB.PropertyC
Comment 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 semblable à ceci (pseudo-code).
int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;
Peut-être même plus loin s'est effondré avec un opérateur nul coalescent.
EDIT À l'origine, je disais que mon deuxième exemple ressemblait à js, mais je le changeai en code psuedo, car il était correctement indiqué que cela ne fonctionnerait pas avec js.
En C # 6, vous pouvez utiliser le Opérateur Null Conditionnel . Donc, le test original sera:
int? value = objectA?.PropertyA?.PropertyB?.PropertyC;
Méthode d'extension courte:
public static TResult IfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> 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 encore, vous pouvez trouver sur http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/
MODIFIER:
Après l'avoir utilisé pendant un moment, je pense que le nom correct pour cette méthode devrait être IfNotNull () au lieu de l'original With ().
Pouvez-vous ajouter 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'objet appelée 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)
Soit dit en passant, cela suppose que vous utilisiez .NET 3 ou une version ultérieure.
La façon dont vous le faites est correcte.
Vous pourriez utiliser une astuce semblable à celle décrite ici , en utilisant les expressions Linq:
int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);
Mais il est beaucoup plus lent que de vérifier manuellement chaque propriété ...
Refactor pour observer le loi de Demeter
Mise à jour 2014: C # 6 a un nouvel opérateur ?.
divers appelés 'navigation sécurisée' ou 'propagation par zéro'
parent?.child
Lire http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe- navigation-operator.aspx pour plus de détails
Cela a longtemps été une demande très populaire https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code= 594c10a522f8e9bc987ee4a5e2c0b38d
Vous recherchez évidemment le Monad Nullable:
string result = new A().PropertyB.PropertyC.Value;
devient
string result = from a in new A()
from b in a.PropertyB
from c in b.PropertyC
select c.Value;
Ceci retourne null
, si l’une des propriétés nullable est null; sinon, la valeur de Value
.
class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }
Méthodes d'extension LINQ:
public static class NullableExtensions
{
public static TResult SelectMany<TOuter, TInner, TResult>(
this TOuter source,
Func<TOuter, TInner> innerSelector,
Func<TOuter, TInner, TResult> 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);
}
}
Ce code est "la moindre quantité de code", mais pas la meilleure pratique:
try
{
return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
return null;
}
En supposant que vous ayez des valeurs vides de types, une approche serait la suivante:
var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;
Je suis un grand fan de C # mais une très jolie chose dans le nouveau Java (1.7?) Est l'opérateur.?
var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;
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.
MISE À JOUR:
Tu pourrais 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;
Quand j'ai besoin d'enchaîner des appels comme ça, je m'appuie sur une méthode d'assistance que j'ai créée, TryGet ():
public static U TryGet<T, U>(this T obj, Func<T, U> func)
{
return obj.TryGet(func, default(U));
}
public static U TryGet<T, U>(this T obj, Func<T, U> func, U whenNull)
{
return obj == null ? whenNull : func(obj);
}
Dans votre cas, vous l'utiliseriez comme suit:
int value = ObjectA
.TryGet(p => p.PropertyA)
.TryGet(p => p.PropertyB)
.TryGet(p => p.PropertyC, defaultVal);
Vérifiez cet article de blog out. Je pense que c'est une méthode très élégante pour les contrôles nuls chaînés. Il y a beaucoup d'implémentations similaires, mais j'aime bien celle-ci car elle arrête l'évaluation dès qu'une valeur nulle est trouvée dans la chaîne.
Tout le code source est sur github .
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 mieux encore pourrait être ceci:
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, qui est une erreur.
if (ObjectA! = null && ObjectA.PropertyA ... fonctionne en raison d'un court-circuit, c'est-à-dire que ObjectA.PropertyA ne sera jamais vérifié si ObjectA est null.
La première façon que vous proposez est la meilleure et la plus claire avec l'intention. Si vous pouviez faire quelque chose, essayez de modifier la conception sans avoir à compter sur autant de valeurs nulles.
Propagation nulle prévue dans C # vNext, générée par Roslyn
vous pouvez utiliser l'extension suivante et je pense que c'est vraiment bien:
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<TF, TR>(TF t, Func<TF, TR> f)
where TF : class
{
return t != null ? f(t) : default(TR);
}
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
where T1 : class
where T2 : class
{
return Get(Get(p1, p2), p3);
}
/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
where T1 : class
where T2 : class
where T3 : class
{
return Get(Get(Get(p1, p2), p3), p4);
}
Et c'est utilisé comme ça:
int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC);
Je suis tombé par hasard sur ce post.
Il y a quelque temps, j'ai suggéré à Visual Studio Connect d'ajouter un nouveau ???
opérateur.
Cela demanderait un peu de travail de la part de l’équipe du framework, mais il n’aurait pas besoin de modifier le langage mais juste de faire un peu de magie pour le compilateur. L'idée était que le compilateur devrait changer ce code (syntaxe non autorisée atm)
string product_name = Order.OrderDetails[0].Product.Name ??? "no product defined";
dans ce code
Func<string> _get_default = () => "no product defined";
string 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 un contrôle nul cela pourrait ressembler à
bool isNull = (Order.OrderDetails[0].Product ??? null) == null;
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<TObject, TProperty>(this TObject model, Func<TObject, TProperty> valueFunc)
where TObject : class
{
try
{
return valueFunc.Invoke(model);
}
catch (NullReferenceException nex)
{
return default(TProperty);
}
}
Avec une utilisation 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
{
/// <summary>
/// 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");
/// </summary>
public static TSecond GetProperty<TFirst, TSecond>(this TFirst item1,
Func<TFirst, TSecond> getItem2, TSecond defaultValue = default(TSecond))
{
if (item1 == null)
{
return defaultValue;
}
return getItem2(item1);
}
}