C # manera elegante de verificar si la propiedad de una propiedad es nula

En C #, diga que desea extraer un valor de PropertyC en este ejemplo y ObjectA, PropertyA y PropertyB pueden ser nulos.

ObjectA.PropertyA.PropertyB.PropertyC

¿Cómo puedo obtener PropertyC de forma segura con la menor cantidad de código?

Ahora mismo verificaría:

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

Sería bueno hacer algo más así (pseudo-código).

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

Posiblemente aún más colapsado con un operador de fusión nula.

EDITAR Originalmente dije que mi segundo ejemplo era como js, ​​pero lo cambié a código de pseudo porque se señaló correctamente que no funcionaría en js.

En C # 6 puede usar el Operador condicional nulo. Entonces la prueba original será:

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

Método de extensión corta:

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

Utilizando

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

Este sencillo método de extensión y mucho más se puede encontrar en http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/

EDITAR:

Después de usarlo por un momento, creo que el nombre correcto para este método debería ser IfNotNull () en lugar del original With ().

¿Puedes agregar un método a tu clase? Si no, ¿has pensado en usar métodos de extensión? Puede crear un método de extensión para su tipo de objeto llamado GetPropC() .

Ejemplo:

 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; } } 

Uso:

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

Por cierto, esto supone que estás usando .NET 3 o superior.

La forma en que lo haces es correcta.

Puede usar un truco como el que se describe aquí , usando expresiones Linq:

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

Pero es mucho más lento que verificando manualmente cada propiedad …

Refactor para observar la Ley de Demeter

Obviamente estás buscando la mónada Nullable :

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

se convierte

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

Esto devuelve null , si alguna de las propiedades que aceptan nulos es nula; de lo contrario, el valor de Value .

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

Métodos de extensión 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); } } 

Actualización 2014: C # 6 tiene un nuevo operador ?. varios llamados ‘navegación segura’ o ‘propagación nula’

 parent?.child 

Lea http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-lastc-c-isgetget-sometimes-called-the-safe-navigation-operator.aspx para obtener detalles

Esto ha sido durante mucho tiempo una petición muy popular https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code=594c10a522f8e9bc987ee4a5e2c0b38d

Este código es “la menor cantidad de código”, pero no la mejor práctica:

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

Suponiendo que tiene valores vacíos de tipos, un enfoque sería este:

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

Soy un gran fan de C #, pero una cosa muy buena en el nuevo Java (¿1.7?) Es el. operador:

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

Mira esta publicación de blog . Creo que es un método muy elegante para los controles nulos encadenados. Hay muchas implementaciones similares de esto, pero me gusta esta porque deja de evaluar tan pronto como se encuentra un valor nulo en la cadena.

Todo el código fuente está en github .

Cuando necesito encadenar llamadas así, confío en un método de ayuda que creé, 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); } 

En tu caso, lo usarías así:

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

Vi algo en el nuevo C # 6.0, esto es mediante el uso de ‘?’ en lugar de verificar

por ejemplo, en lugar de usar

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

simplemente usa

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

Espero que haya ayudado a alguien.


ACTUALIZAR:

Podrías hacer esto así ahora

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

Podrías hacer esto:

 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; ... } 

O incluso mejor podría ser esto:

 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); 

No es posible. ObjectA.PropertyA.PropertyB fallará si ObjectA es nulo debido a la desreferenciación nula, que es un error.

if (ObjectA! = null && ObjectA.PropertyA … funciona debido a un cortocircuito, es decir, ObjectA.PropertyA nunca se comprobará si ObjectA es nulo.

La primera forma en que propones es la mejor y más clara con intención. Si algo puede intentar rediseñar sin tener que depender de tantos nulos.

Acabo de tropezar con este post.

Hace algún tiempo hice una sugerencia sobre Visual Studio Connect sobre agregar un nuevo ??? operador.

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

Esto requeriría algo de trabajo del equipo de framework, pero no es necesario alterar el lenguaje, sino simplemente hacer algo de comstackción mágica. La idea era que el comstackdor debería cambiar este código (syntax no permitida atm)

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

en este código

 Func _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() 

Para un cheque nulo esto podría parecerse

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

puedes usar la siguiente extensión y creo que es realmente buena:

 ///  /// 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); } 

Y se usa así:

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

Propagación nula planificada en C # vNext, con tecnología de Roslyn


No es una respuesta, sino más una actualización. Parece que UserVoice lo ha manejado, junto con otras cosas nuevas, desde Roslyn y aparentemente es una adición planeada:

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

Escribiría su propio método en el tipo de PropertyA (o un método de extensión si no es su tipo) utilizando el patrón similar al tipo Nullable.

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

Este enfoque es bastante directo una vez que superas el 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); } } 

Con un uso que podría verse así:

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

Escribí un método que acepta un valor predeterminado, aquí se muestra cómo usarlo:

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

Aquí está el código:

 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; 

Los ?? operador significa que si el primer argumento es nulo, regrese el segundo en su lugar.