web-dev-qa-db-fra.com

Une façon plus propre de faire une vérification nulle en C #?

Supposons que j'ai cette interface,

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 (Ceci vérifie si City est null ou non.)

Toutefois, cette vérification échoue si Adresse ou Contact ou Personne elle-même est null.

Actuellement, une solution à laquelle je pouvais penser était la suivante:

if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)

{ 
    // Do some stuff here..
}

Y a-t-il une manière plus propre de faire ceci?

Je n'aime vraiment pas la vérification null effectuée en tant que (something == null). Y a-t-il une autre façon agréable de faire quelque chose comme la méthode something.IsNull()?

De manière générique, vous pouvez utiliser un arbre d'expression et vérifier avec une méthode d'extension:

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

Code complet:

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);
    }
}
236
Toto

Votre code peut avoir des problèmes plus importants que la nécessité de vérifier les références nulles. Dans l'état actuel des choses, vous enfreignez probablement la loi de Demeter .

La loi de Demeter est une de ces heuristiques, comme Ne te répète pas, qui t'aide à écrire du code facilement maintenable. Il dit aux programmeurs de ne pas accéder à quelque chose de trop éloigné de la portée immédiate. Par exemple, supposons que j'ai ce code:

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

La méthode DoAnAction viole la loi de Demeter. Dans une fonction, il accède à une BusinessCalcualtor, à un BusinessData et à un decimal. Cela signifie que si l'une des modifications suivantes est effectuée, la ligne devra être refactorisée:

  • Le type de retour de BusinessCalculator.CalculateMoney() change.
  • Le type de BusinessData.Money change

Compte tenu de la situation actuelle, ces changements sont plutôt susceptibles de se produire. Si un code comme celui-ci est écrit dans la base de code, ces modifications pourraient devenir très coûteuses. De plus, cela signifie que votre BusinessController est couplée aux types BusinessCalculator et BusinessData.

Une façon d'éviter cette situation est de réécrire le code comme ceci:

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

Désormais, si vous apportez l'une des modifications ci-dessus, il vous suffira de refactoriser un élément de code supplémentaire, la méthode BusinessCalculator.CalculateCents(). Vous avez également éliminé la dépendance de BusinessController sur BusinessData.


Votre code souffre d'un problème similaire:

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 l'une des modifications suivantes est effectuée, vous devrez refactoriser la méthode principale que j'ai écrite ou la vérification de null que vous avez écrite:

  • Le type de IPerson.contact change
  • Le type de IContact.address change
  • Le type de IAddress.city change

Je pense que vous devriez envisager une refactorisation plus profonde de votre code que la simple réécriture d'un contrôle nul.


Cela dit, j'estime qu'il est parfois inapproprié de respecter la loi de Demeter. (C'est, après tout, une heuristique, pas une règle absolue, même si elle s'appelle une "loi".)

En particulier, je pense que si:

  1. Certaines classes représentent des enregistrements stockés dans la couche de persistance de votre programme, ET
  2. Vous êtes extrêmement confiant que vous n’aurez pas besoin de refactoriser ces classes à l’avenir,

ignorer la loi de Demeter est acceptable s’agissant spécifiquement de ces classes. En effet, elles représentent les données avec lesquelles votre application fonctionne. Par conséquent, passer d'un objet de données à un autre est un moyen d'explorer les informations de votre programme. Dans mon exemple ci-dessus, le couplage causé par la violation de la loi de Demeter était beaucoup plus grave: je parcourais tout le chemin depuis un contrôleur situé au sommet de ma pile via une calculatrice de logique commerciale située au milieu de la pile dans une classe de données dans la couche de persistance.

J'apporte cette exception potentielle à la loi de Demeter, car avec des noms tels que Person, Contact et Address, vos classes semblent être des POCO de couche de données. Si tel est le cas et que vous êtes extrêmement confiant que vous n'aurez jamais besoin de les reformuler à l'avenir, vous pourrez peut-être ignorer la loi de Demeter dans votre situation spécifique.

62
Kevin

dans votre cas, vous pouvez créer une propriété pour personne

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

mais vous devez toujours vérifier si la personne est nulle

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

}

à votre autre question, pour les chaînes, vous pouvez également vérifier si null ou vide de cette façon:

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
}
48
Koryu

Une option totalement différente (que je pense être sous-utilisée) est le modèle d'objet nul . Il est difficile de dire si cela a du sens dans votre situation particulière, mais cela vaut peut-être la peine d'essayer. En bref, vous aurez une implémentation NullContact, une implémentation NullAddress et ainsi de suite que vous utiliserez à la place de null. De cette façon, vous pouvez vous débarrasser de la plupart des contrôles nuls, bien sûr, au détriment de la réflexion que vous avez à faire dans la conception de ces implémentations.

Comme Adam l'a souligné dans son commentaire, cela vous permet d'écrire

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

dans les cas où cela est vraiment nécessaire. Bien sûr, cela n'a de sens que si la ville est vraiment un objet non trivial ...

Alternativement, l'objet null peut être implémenté en tant que singleton (par exemple, look here pour des instructions pratiques concernant l'utilisation du modèle d'objet null et here pour des instructions concernant des singletons en C # ) qui vous permet d’utiliser la comparaison classique.

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

Personnellement, je préfère cette approche car je pense qu'il est plus facile à lire pour les personnes qui ne sont pas familières avec le motif.

37
bigge

Mise à jour du 28/04/2014: ne propagation nulle est prévue pour C # vNext


Il y a des problèmes plus importants que la propagation des contrôles nuls. Visez le code lisible qui peut être compris par un autre développeur, et bien que ce soit verbeux - votre exemple va bien.

S'il s'agit d'une vérification fréquemment effectuée, envisagez de l'encapsuler dans la classe Person en tant qu'appel de propriété ou de méthode.


Cela dit, gratuit Func et génériques!

Je ne ferais jamais cela, mais voici une autre alternative:

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

Appelé:

    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();
    }
26
Adam Houldsworth

La deuxième question,

Je n'aime vraiment pas que le contrôle nul soit effectué comme (quelque chose == nul). Y a-t-il une autre façon agréable de faire quelque chose comme la méthode quelque chose.IsNull ()?

pourrait être résolu en utilisant une méthode d'extension:

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

Si, pour une raison quelconque, cela ne vous dérange pas de choisir l'une des solutions les plus complexes, vous pouvez consulter la solution décrite dans mon article de blog . Il utilise l'arborescence des expressions pour déterminer si la valeur est nulle avant d'évaluer l'expression. Mais pour que les performances restent acceptables, il crée et met en cache du code IL.

La solution vous permet d’écrire ceci:

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

Tu peux écrire:

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

puis:

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

}

Parfois, cela a du sens. Mais personnellement, j'éviterais de telles choses ... car il n'est pas clair pourquoi vous pouvez appeler une méthode de l'objet qui est en réalité nulle.

7
Vladimir Gondarev

Faites-le dans un method séparé comme:

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

Où votre IsNullmethod est

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

Avez-vous besoin de C # ou voulez-vous seulement . NET ? Si vous pouvez mélanger un autre langage .NET, jetez un œil à Oxygene . C'est un langage OO étonnant et très moderne qui cible .NET (ainsi que Java et Cocoa . Eh oui. Tout en fait, c'est tout à fait une incroyable chaîne d'outils.)

Oxygene a un opérateur de colon qui fait exactement ce que vous demandez. Pour citer leurs page de fonctionnalités diverses en langues :

Le colon (":") opérateur

Dans Oxygene, comme dans beaucoup de langues, il a été influencé par le "." opérateur est utilisé pour appeler des membres sur une classe ou un objet, tel que

var x := y.SomeProperty;

Ceci "déréférence" l'objet contenu dans "y", appelle (dans ce cas) la propriété getter et retourne sa valeur. Si "y" se trouve être non affecté (c'est-à-dire "nil"), une exception est levée.

L'opérateur ":" fonctionne à peu près de la même façon, mais au lieu de lever une exception sur un objet non affecté, le résultat sera simplement nil. Pour les développeurs venant d'Objective-C, cela vous sera familier, car c'est ainsi que fonctionnent les appels de méthode Objective-C à l'aide de la syntaxe [].

... (snip)

Où ":" brille vraiment lorsque vous accédez à des propriétés dans une chaîne, où tout élément peut être nul. Par exemple, le code suivant:

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

s'exécutera sans erreur et renverra nil si l'un des objets de la chaîne est vide - le formulaire, le bouton ou sa légende.

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

Ne le faites pas réellement. Faites les vérifications nulles et déterminez quel format vous pouvez le mieux vivre.

3
jwg

Une telle chaîne de référence peut se produire, par exemple, si vous utilisez un outil ORM et souhaitez conserver vos classes aussi pures que possible. Dans ce scénario, je pense que cela ne peut pas être évité gentiment.

J'ai la méthode d'extension suivante "family", qui vérifie si l'objet sur lequel il s'appelle est null, et si ce n'est pas le cas, renvoie l'une des propriétés demandées ou exécute des méthodes avec. Cela ne fonctionne bien sûr que pour les types de référence, c'est pourquoi j'ai la contrainte générique correspondante.

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

Ces méthodes n’ajoutent presque pas de surcharge par rapport à la solution manuelle (pas de réflexion, pas d’arbres d’expression), et vous pouvez obtenir une syntaxe plus agréable avec elles (IMO).

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

Ou avec des méthodes:

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

Cependant, on pourrait vraiment discuter que la longueur du code n'a pas trop changé.

3
Zoltán Tamási

J'ai une extension qui pourrait être utile pour cela; ValueOrDefault (). Il accepte une instruction lambda et l'évalue en renvoyant la valeur évaluée ou une valeur par défaut si des exceptions attendues (NRE ou IOE) sont levées.

    /// <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 surcharge ne prenant pas une valeur par défaut spécifique retournera null pour tout type de référence. Cela devrait fonctionner dans votre scénario:

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
        }
    }
}
3
KeithS

À mon avis, l'opérateur d'égalité n'est pas un moyen plus sûr et meilleur pour l'égalité de référence.

Il est toujours préférable d'utiliser ReferenceEquals(obj, null). Cela fonctionnera toujours. D'autre part, l'opérateur d'égalité (==) pourrait être surchargé et vérifier si les valeurs sont égales au lieu des références. Je dirai donc que ReferenceEquals() est un moyen plus sûr et meilleur.

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

Référence: article MSDN méthode Object.ReferenceEquals.

Mais aussi voici mes pensées pour les valeurs nulles

  • Généralement, renvoyer des valeurs nulles est la meilleure idée si quelqu'un essaie d'indiquer qu'il n'y a pas de données.

  • Si l'objet n'est pas null, mais vide, cela signifie que des données ont été renvoyées, alors que renvoyer null indique clairement que rien n'a été renvoyé.

  • De plus, si vous retournez NULL, cela entraînera une exception NULL si vous essayez d'accéder aux membres de l'objet, ce qui peut être utile pour mettre en évidence le code erroné.

En C #, il existe deux types d'égalité:

  • égalité de référence et
  • égalité de valeur.

Lorsqu'un type est immuable, l'opérateur de surcharge == pour comparer l'égalité de valeur au lieu d'égalité de référence peut être utile.

Remplacer l'opérateur == dans les types non immuables n'est pas recommandé.

Reportez-vous à l'article MSDN Instructions pour la surcharge égal à () et à l'opérateur == (Guide de programmation C #) pour plus de détails.

2
Microtechie

Autant que j'aime C #, c'est une chose qui plaît beaucoup au C++ quand on travaille directement avec des instances d'objet; certaines déclarations simplement ne peut pas être nul, il n’est donc pas nécessaire de rechercher null.

La meilleure façon d'obtenir une part de cette tarte en C # (ce qui pourrait être un peu trop remodelé de votre part - dans ce cas, choisissez parmi les autres réponses) est avec struct. Bien que vous puissiez vous retrouver dans une situation où une structure a des valeurs "par défaut" non instanciées (c'est-à-dire 0, 0.0, chaîne nulle), il n'est jamais nécessaire de vérifier "if (myStruct == null)".

Je ne voudrais pas passer à eux sans comprendre leur utilisation, bien sûr. Elles ont tendance à être utilisées pour les types de valeur, et pas vraiment pour les grands blocs de données - chaque fois que vous affectez une structure d'une variable à une autre, vous avez tendance à copier les données, en créant essentiellement une copie de chacune des valeurs de l'original ( vous pouvez éviter cela avec le mot clé ref - lisez à nouveau dessus plutôt que de simplement l'utiliser. Néanmoins, cela peut convenir à des choses comme StreetAddress - je ne l’utiliserais certainement pas paresseusement pour tout ce que je ne voulais pas annuler.

1
Katana314

Selon l'objectif de l'utilisation de la variable "ville", une méthode plus simple pourrait consister à séparer les contrôles nuls en différentes classes. De cette façon, vous ne violeriez pas non plus la loi de Demeter. Donc au lieu de:

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

Vous auriez:

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

Encore une fois, cela dépend de l'objectif du programme.

1
Thomas

Dans quelles circonstances ces choses peuvent-elles être nulles? Si null indique un bogue dans le code, vous pouvez utiliser des contrats de code. Ils le prendront si vous obtenez des valeurs nulles lors des tests, puis disparaîtront dans la version de production. Quelque chose comme ça:

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

Bien sûr, si les valeurs Null possibles proviennent d’autres sources, vous devez déjà avoir conditionné les données. Et si l'une des valeurs NULL est valide, vous ne devez pas inclure non-null dans le contrat, vous devez les tester et les gérer correctement.

1
digitig

Vous pouvez utiliser la réflexion pour éviter de forcer l'implémentation d'interfaces et de code supplémentaire dans chaque classe. Simplement une classe Helper avec méthode (s) statique (s). Ce n'est peut-être pas le moyen le plus efficace, soyez gentil avec moi, je suis vierge (lisez, 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");

Bien entendu, si vous avez une faute de frappe dans les noms de propriétés, le résultat sera faux (très probablement).

0
TDull

Une façon de supprimer les contrôles NULL dans les méthodes consiste à encapsuler leurs fonctionnalités ailleurs. Une façon de faire est d'utiliser des accesseurs et des passeurs. Par exemple, au lieu de faire ceci:

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

Faire ceci:

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

Ensuite, chaque fois que vous appelez "personne.contact", le code de la méthode "get" sera exécuté, initialisant ainsi la valeur si elle est nulle.

Vous pouvez appliquer exactement cette même méthodologie à toutes les propriétés pouvant être nulles pour tous vos types. Les avantages de cette approche sont qu’elle 1) vous évite d’avoir à effectuer des contrôles nuls en ligne et 2) rend votre code plus lisible et moins sujet aux erreurs de copier-coller.

Il convient toutefois de noter que si vous vous trouvez dans une situation où vous devez exécuter une action si l’une des propriétés est null (c’est-à-dire une personne avec un contact nul signifie-t-elle réellement quelque chose dans votre domaine?), alors cette approche sera un obstacle plutôt qu’une aide. Cependant, si les propriétés en question doivent jamais être nulles, cette approche vous donnera un moyen très clair de représenter ce fait.

--jtlovetteiii

0
jtlovetteiii