signo interrogacion definicion cheques cheque anulados anulado c# .net-4.0 null .net-4.5

interrogacion - Manera más limpia de hacer un cheque nulo en C#?



cheques anulados (19)

¿Necesitas C # o solo quieres .NET ? Si puede mezclar otro lenguaje .NET, eche un vistazo a Oxygene . Es un lenguaje OO asombroso y muy moderno que se enfoca en .NET (y también en Java y Cocoa . Sí, todo de forma nativa, realmente es una cadena de herramientas increíble).

Oxygene tiene un operador de colon que hace exactamente lo que preguntas. Para citar de su página de características de idioma misceláneo :

El operador de Colon (":")

En Oxygene, como en muchos de los idiomas en los que fue influenciado, el "." operador se usa para llamar a miembros en una clase u objeto, como

var x := y.SomeProperty;

Esto "desreferencia" el objeto contenido en "y", llama (en este caso) al getter de propiedad y devuelve su valor. Si "y" no está asignado (es decir, "nulo"), se lanza una excepción.

El operador ":" funciona de forma muy parecida, pero en lugar de lanzar una excepción sobre un objeto no asignado, el resultado será simplemente nulo. Para los desarrolladores que provienen de Objective-C, esto será familiar, ya que así es como funcionan las llamadas al método Objective-C utilizando la sintaxis [].

... (recorte)

Donde ":" realmente brilla es cuando se accede a propiedades en una cadena, donde cualquier elemento puede ser nulo. Por ejemplo, el siguiente código:

var y := MyForm:OkButton:Caption:Length;

se ejecutará sin error y devolverá nulo si alguno de los objetos de la cadena es nulo: la forma, el botón o su título.

Esta pregunta ya tiene una respuesta aquí:

Supongamos que tengo esta interfaz

interface IContact { IAddress address { get; set; } } interface IAddress { string city { get; set; } } class Person : IPerson { public IContact contact { get; set; } } class test { private test() { var person = new Person(); if (person.contact.address.city != null) { //this will never work if contact is itself null? } } }

Person.Contact.Address.City != null (Esto funciona para verificar si City es nula o no).

Sin embargo, esta comprobación falla si Dirección o Contacto o Persona en sí es nulo.

Actualmente, una solución en la que podía pensar era esta:

if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null) { // Do some stuff here.. }

¿Hay una forma más limpia de hacer esto?

Realmente no me gusta que se realice la comprobación null como (something == null) . En su lugar, ¿hay otra buena manera de hacer algo como el método something.IsNull() ?


De forma genérica, puede usar un árbol de expresiones y verificar con un método de extensión:

if (!person.IsNull(p => p.contact.address.city)) { //Nothing is null }

Código completo:

public class IsNullVisitor : ExpressionVisitor { public bool IsNull { get; private set; } public object CurrentObject { get; set; } protected override Expression VisitMember(MemberExpression node) { base.VisitMember(node); if (CheckNull()) { return node; } var member = (PropertyInfo)node.Member; CurrentObject = member.GetValue(CurrentObject,null); CheckNull(); return node; } private bool CheckNull() { if (CurrentObject == null) { IsNull = true; } return IsNull; } } public static class Helper { public static bool IsNull<T>(this T root,Expression<Func<T, object>> getter) { var visitor = new IsNullVisitor(); visitor.CurrentObject = root; visitor.Visit(getter); return visitor.IsNull; } } class Program { static void Main(string[] args) { Person nullPerson = null; var isNull_0 = nullPerson.IsNull(p => p.contact.address.city); var isNull_1 = new Person().IsNull(p => p.contact.address.city); var isNull_2 = new Person { contact = new Contact() }.IsNull(p => p.contact.address.city); var isNull_3 = new Person { contact = new Contact { address = new Address() } }.IsNull(p => p.contact.address.city); var notnull = new Person { contact = new Contact { address = new Address { city = "LONDON" } } }.IsNull(p => p.contact.address.city); } }


Dicha cadena de referencia puede ocurrir, por ejemplo, si usa una herramienta ORM y desea mantener sus clases lo más puramente posible. En este escenario, creo que no se puede evitar muy bien.

Tengo el siguiente método de extensión "familia", que comprueba si el objeto al que se llama es nulo, y si no, devuelve una de sus propiedades solicitadas o ejecuta algunos métodos con ella. Esto funciona, por supuesto, solo para los tipos de referencia, por eso tengo la restricción genérica correspondiente.

public static TRet NullOr<T, TRet>(this T obj, Func<T, TRet> getter) where T : class { return obj != null ? getter(obj) : default(TRet); } public static void NullOrDo<T>(this T obj, Action<T> action) where T : class { if (obj != null) action(obj); }

Estos métodos casi no agregan gastos generales en comparación con la solución manual (sin reflexión, sin árboles de expresión), y puede lograr una sintaxis más agradable con ellos (IMO).

var city = person.NullOr(e => e.Contact).NullOr(e => e.Address).NullOr(e => e.City); if (city != null) // do something...

O con métodos:

person.NullOrDo(p => p.GoToWork());

Sin embargo, uno podría definitivamente argumentar que la longitud del código no cambió demasiado.


En mi opinión, el operador de igualdad no es una forma más segura y mejor para la igualdad de referencia.

Siempre es mejor usar ReferenceEquals(obj, null) . Esto siempre funcionará Por otro lado, el operador de igualdad (==) podría estar sobrecargado y podría estar comprobando si los valores son iguales en lugar de las referencias, entonces diré que ReferenceEquals() es una forma más segura y mejor.

class MyClass { static void Main() { object o = null; object p = null; object q = new Object(); Console.WriteLine(Object.ReferenceEquals(o, p)); p = q; Console.WriteLine(Object.ReferenceEquals(p, q)); Console.WriteLine(Object.ReferenceEquals(o, p)); } }

Referencia: artículo de MSDN Método Object.ReferenceEquals .

Pero también aquí están mis pensamientos sobre los valores nulos

  • En general, devolver valores nulos es la mejor idea si alguien intenta indicar que no hay datos.

  • Si el objeto no es nulo, pero está vacío, implica que se han devuelto datos, mientras que devolver nulo indica claramente que no se ha devuelto nada.

  • También IMO, si devuelve null, dará como resultado una excepción nula si intenta acceder a miembros en el objeto, lo que puede ser útil para resaltar el código defectuoso.

En C #, hay dos tipos diferentes de igualdad:

  • igualdad de referencia y
  • igualdad de valores

Cuando un tipo es inmutable, puede ser útil sobrecargar al operador == para comparar la igualdad de valores en lugar de la igualdad de referencia.

No se recomienda el operador de anulación == en tipos no inmutables.

Consulte el artículo de MSDN Pautas para la sobrecarga equivalentes () y Operador == (Guía de programación C #) para obtener más detalles.


Hazlo en un method separado como:

private test() { var person = new Person(); if (!IsNull(person)) { // Proceed ........

Donde su method IsNull es

public bool IsNull(Person person) { if(Person != null && Person.Contact != null && Person.Contact.Address != null && Person.Contact.Address.City != null) return false; return true; }


La segunda pregunta

Realmente no me gusta que se realice la comprobación nula como (algo == nulo). En su lugar, ¿hay otra buena manera de hacer algo como el método something.IsNull ()?

podría resolverse usando un método de extensión:

public static class Extensions { public static bool IsNull<T>(this T source) where T : class { return source == null; } }


Por mucho que me guste C #, esta es una cosa agradable acerca de C ++ cuando se trabaja directamente con instancias de objetos; algunas declaraciones simplemente no pueden ser nulas, por lo que no es necesario comprobar si son nulas.

La mejor manera de obtener una porción de este pastel en C # (que podría ser un exceso de rediseño de su parte, en cuyo caso, elija las otras respuestas) es con struct ''s. Si bien puede encontrarse en una situación en la que una estructura tiene valores "predeterminados" (es decir, 0, 0.0, cadena nula), nunca es necesario marcar "if (myStruct == null)".

No me gustaría cambiar a ellos sin entender su uso, por supuesto. Tienden a ser utilizados para tipos de valor, y no para grandes bloques de datos: cada vez que asigna una estructura de una variable a otra, tiende a copiar los datos, esencialmente creando una copia de cada uno de los valores ( puede evitar esto con la palabra clave ref : de nuevo, lea en ella en lugar de solo usarla). Aún así, puede encajar en cosas como StreetAddress: ciertamente no lo usaría perezosamente en algo que no quisiera anular.


Puedes escribir:

public static class Extensions { public static bool IsNull(this object obj) { return obj == null; } }

y entonces:

string s = null; if(s.IsNull()) { }

A veces esto tiene sentido. Pero personalmente evitaría tales cosas ... porque no está claro por qué se puede llamar a un método del objeto que en realidad es nulo.


Si por alguna razón no te importa ir con una de las soluciones más "exageradas", es posible que desees consultar la solución que se describe en la publicación de mi blog . Utiliza el árbol de expresiones para averiguar si el valor es nulo antes de evaluar la expresión. Pero para mantener el rendimiento aceptable, crea y almacena el código IL.

La solución te permite escribir esto:

string city = person.NullSafeGet(n => n.Contact.Address.City);


Su código puede tener problemas mayores que la necesidad de verificar referencias nulas. Tal como está, probablemente estés violando la Ley de Demeter .

La Ley de Demeter es una de esas herramientas heurísticas, como Do not Repeat Yourself, que te ayuda a escribir código fácil de mantener. Les dice a los programadores que no accedan a nada demasiado alejado del alcance inmediato. Por ejemplo, supongamos que tengo este código:

public interface BusinessData { public decimal Money { get; set; } } public class BusinessCalculator : ICalculator { public BusinessData CalculateMoney() { // snip } } public BusinessController : IController { public void DoAnAction() { var businessDA = new BusinessCalculator().CalculateMoney(); Console.WriteLine(businessDA.Money * 100d); } }

El método DoAnAction viola la Ley de Demeter. En una función, accede a un BusinessCalcualtor , BusinessData y un decimal . Esto significa que si se realiza alguno de los siguientes cambios, la línea tendrá que ser refactorizada:

  • El tipo de devolución de BusinessCalculator.CalculateMoney() cambia.
  • El tipo de cambios de BusinessData.Money

Teniendo en cuenta la situación en had, es probable que estos cambios sucedan. Si se escribe un código como este en la base del código, hacer estos cambios podría ser muy costoso. Además de eso, significa que su BusinessController está acoplado a los tipos BusinessCalculator y BusinessData .

Una forma de evitar esta situación es reescribiendo el código de esta manera:

public class BusinessCalculator : ICalculator { private BusinessData CalculateMoney() { // snip } public decimal CalculateCents() { return CalculateMoney().Money * 100d; } } public BusinessController : IController { public void DoAnAction() { Console.WriteLine(new BusinessCalculator().CalculateCents()); } }

Ahora, si realiza alguno de los cambios anteriores, solo tiene que refactorizar una pieza más de código, el método BusinessCalculator.CalculateCents() . También eliminó la dependencia de BusinessData en BusinessData .

Su código sufre un problema similar:

interface IContact { IAddress address { get; set; } } interface IAddress { string city { get; set; } } class Person : IPerson { public IContact contact { get; set; } } class Test { public void Main() { var contact = new Person().contact; var address = contact.address; var city = address.city; Console.WriteLine(city); } }

Si se realiza alguno de los siguientes cambios, deberá refactorizar el método principal que escribí o el cheque nulo que escribió:

  • El tipo de IPerson.contact cambios
  • El tipo de IContact.address cambia
  • El tipo de cambios de IAddress.city

Creo que debería considerar una refacturación más profunda de su código que simplemente reescribir un cheque nulo.

Dicho esto, creo que hay ocasiones en las que seguir la Ley de Demeter es inapropiado. (Después de todo, es una regla heurística, no dura, aunque se llame "ley").

En particular, creo que si:

  1. Tiene algunas clases que representan registros almacenados en la capa de persistencia de su programa, Y
  2. Estás muy seguro de que no necesitarás refactorizar esas clases en el futuro,

ignorar la Ley de Demeter es aceptable cuando se trata específicamente de esas clases. Esto se debe a que representan los datos con los que funciona su aplicación, por lo que, al pasar de un objeto de datos a otro, se puede explorar la información de su programa. En mi ejemplo anterior, el acoplamiento causado por la violación de la Ley de Demeter fue mucho más severo: estaba llegando desde un controlador cerca de la parte superior de mi pila a través de una calculadora de lógica de negocios en el medio de la pila en una clase de datos probable en la capa de persistencia

Traigo esta posible excepción a la Ley de Demeter porque con nombres como Person , Contact y Address , parece que sus clases podrían ser POCO de capa de datos. Si ese es el caso, y usted está extremadamente seguro de que nunca tendrá que refactorizarlos en el futuro, es posible que pueda salirse con la suya haciendo caso omiso de la Ley de Demeter en su situación específica.


Tengo una extensión que podría ser útil para esto; ValueOrDefault (). Acepta una declaración lambda y la evalúa, devolviendo el valor evaluado o un valor predeterminado si se lanzan las excepciones esperadas (NRE o IOE).

/// <summary> /// Provides a null-safe member accessor that will return either the result of the lambda or the specified default value. /// </summary> /// <typeparam name="TIn">The type of the in.</typeparam> /// <typeparam name="TOut">The type of the out.</typeparam> /// <param name="input">The input.</param> /// <param name="projection">A lambda specifying the value to produce.</param> /// <param name="defaultValue">The default value to use if the projection or any parent is null.</param> /// <returns>the result of the lambda, or the specified default value if any reference in the lambda is null.</returns> public static TOut ValueOrDefault<TIn, TOut>(this TIn input, Func<TIn, TOut> projection, TOut defaultValue) { try { var result = projection(input); if (result == null) result = defaultValue; return result; } catch (NullReferenceException) //most reference types throw this on a null instance { return defaultValue; } catch (InvalidOperationException) //Nullable<T> throws this when accessing Value { return defaultValue; } } /// <summary> /// Provides a null-safe member accessor that will return either the result of the lambda or the default value for the type. /// </summary> /// <typeparam name="TIn">The type of the in.</typeparam> /// <typeparam name="TOut">The type of the out.</typeparam> /// <param name="input">The input.</param> /// <param name="projection">A lambda specifying the value to produce.</param> /// <returns>the result of the lambda, or default(TOut) if any reference in the lambda is null.</returns> public static TOut ValueOrDefault<TIn, TOut>(this TIn input, Func<TIn, TOut> projection) { return input.ValueOrDefault(projection, default(TOut)); }

La sobrecarga que no toma un valor predeterminado específico devolverá nulo para cualquier tipo de referencia. Esto debería funcionar en su escenario:

class test { private test() { var person = new Person(); if (person.ValueOrDefault(p=>p.contact.address.city) != null) { //the above will return null without exception if any member in the chain is null } } }


Una opción totalmente diferente (que creo que está infrautilizada) es el patrón de objeto nulo . Es difícil saber si tiene sentido en su situación particular, pero podría valer la pena intentarlo. En resumen, tendrá una implementación de NullContact , una implementación de NullAddress , etc., que use en lugar de null . De esta forma, puede deshacerse de la mayoría de las comprobaciones nulas, por supuesto a expensas de algún pensamiento que tenga que poner en el diseño de estas implementaciones.

Como Adam señaló en su comentario, esto te permite escribir

if (person.Contact.Address.City is NullCity)

en los casos en que es realmente necesario. Por supuesto, esto solo tiene sentido si la ciudad realmente es un objeto no trivial ...

Alternativamente, el objeto nulo puede implementarse como un singleton (por ejemplo, busque here algunas instrucciones prácticas sobre el uso del patrón de objeto nulo y here instrucciones relativas a singletons en C #) que le permiten usar la comparación clásica.

if (person.Contact.Address.City == NullCity.Instance)

Personalmente, prefiero este enfoque porque creo que es más fácil de leer para las personas que no están familiarizadas con el patrón.


en su caso, podría crear una propiedad para una persona

public bool HasCity { get { return (this.Contact!=null && this.Contact.Address!= null && this.Contact.Address.City != null); } }

pero aún tienes que verificar si la persona es nula

if (person != null && person.HasCity) { }

Para su otra pregunta, para cadenas también puede verificar si está vacía o no:

string s = string.Empty; if (!string.IsNullOrEmpty(s)) { // string is not null and not empty } if (!string.IsNullOrWhiteSpace(s)) { // string is not null, not empty and not contains only white spaces }


Actualización 28/04/2014: la propagación nula está planificada para C # vSiguiente

Hay problemas mayores que la propagación de verificaciones nulas. Intente obtener un código legible que otro desarrollador pueda comprender y, aunque es prolijo, su ejemplo está bien.

Si se trata de una comprobación que se realiza con frecuencia, considere encapsularla dentro de la clase Person como una propiedad o llamada a un método.

Dicho esto, Func gratuito y genéricos!

Yo nunca haría esto, pero aquí hay otra alternativa:

class NullHelper { public static bool ChainNotNull<TFirst, TSecond, TThird, TFourth>(TFirst item1, Func<TFirst, TSecond> getItem2, Func<TSecond, TThird> getItem3, Func<TThird, TFourth> getItem4) { if (item1 == null) return false; var item2 = getItem2(item1); if (item2 == null) return false; var item3 = getItem3(item2); if (item3 == null) return false; var item4 = getItem4(item3); if (item4 == null) return false; return true; } }

Llamado:

static void Main(string[] args) { Person person = new Person { Address = new Address { PostCode = new Postcode { Value = "" } } }; if (NullHelper.ChainNotNull(person, p => p.Address, a => a.PostCode, p => p.Value)) { Console.WriteLine("Not null"); } else { Console.WriteLine("null"); } Console.ReadLine(); }


Depending on what the purpose of using the "city" variable is, a cleaner way could be to separate the null checks into different classes. That way you also wouldn''t be violating the Law of Demeter. So instead of:

if (person != null && person.contact != null && person.contact.address != null && person.contact.address.city != null) { // do some stuff here.. }

You''d have:

class test { private test() { var person = new Person(); if (person != null) { person.doSomething(); } } } ... /* Person class */ doSomething() { if (contact != null) { contact.doSomething(); } } ... /* Contact class */ doSomething() { if (address != null) { address.doSomething(); } } ... /* Address class */ doSomething() { if (city != null) { // do something with city } }

Again, it depends on the purpose of the program.


In what circumstances can those things be null? If nulls would indicate a bug in the code then you could use code contracts. They will pick it up if you get nulls during testing, then will go away in the production version. Something like this:

using System.Diagnostics.Contracts; [ContractClass(typeof(IContactContract))] interface IContact { IAddress address { get; set; } } [ContractClassFor(typeof(IContact))] internal abstract class IContactContract: IContact { IAddress address { get { Contract.Ensures(Contract.Result<IAddress>() != null); return default(IAddress); // dummy return } } } [ContractClass(typeof(IAddressContract))] interface IAddress { string city { get; set; } } [ContractClassFor(typeof(IAddress))] internal abstract class IAddressContract: IAddress { string city { get { Contract.Ensures(Contract.Result<string>() != null); return default(string); // dummy return } } } class Person { [ContractInvariantMethod] protected void ObjectInvariant() { Contract.Invariant(contact != null); } public IContact contact { get; set; } } class test { private test() { var person = new Person(); Contract.Assert(person != null); if (person.contact.address.city != null) { // If you get here, person cannot be null, person.contact cannot be null // person.contact.address cannot be null and person.contact.address.city cannot be null. } } }

Of course, if the possible nulls are coming from somewhere else then you''ll need to have already conditioned the data. And if any of the nulls are valid then you shouldn''t make non-null a part of the contract, you need to test for them and handle them appropriately.


One way to remove null checks in methods is to encapsulate their functionality elsewhere. One way to do this is through getters and setters. For instance, instead of doing this:

class Person : IPerson { public IContact contact { get; set; } }

Do this:

class Person : IPerson { public IContact contact { get { // This initializes the property if it is null. // That way, anytime you access the property "contact" in your code, // it will check to see if it is null and initialize if needed. if(_contact == null) { _contact = new Contact(); } return _contact; } set { _contact = value; } } private IContact _contact; }

Then, whenever you call "person.contact", the code in the "get" method will run, thus initializing the value if it is null.

You could apply this exact same methodology to all of the properties that could be null across all of your types. The benefits to this approach are that it 1) prevents you from having to do null checks in-line and it 2) makes your code more readable and less prone to copy-paste errors.

It should be noted, however, that if you find yourself in a situation where you need to perform some action if one of the properties is null (ie does a Person with a null Contact actually mean something in your domain?), then this approach will be a hindrance rather than a help. However, if the properties in question should never be null, then this approach will give you a very clean way of representing that fact.

--jtlovetteiii


You could use reflection, to avoid forcing implementation of interfaces and extra code in every class. Simply a Helper class with static method(s). This might not be the most efficient way, be gentle with me, I''m a virgin (read, noob)..

public class Helper { public static bool IsNull(object o, params string[] prop) { if (o == null) return true; var v = o; foreach (string s in prop) { PropertyInfo pi = v.GetType().GetProperty(s); //Set flags if not only public props v = (pi != null)? pi.GetValue(v, null) : null; if (v == null) return true; } return false; } } //In use isNull = Helper.IsNull(p, "ContactPerson", "TheCity");

Offcourse if you have a typo in the propnames, the result will be wrong (most likely)..


try { // do some stuff here } catch (NullReferenceException e) { }

No hagas esto. Realice las comprobaciones nulas y descubra con qué formato puede vivir mejor.