web-dev-qa-db-fra.com

Est-il préférable d'utiliser Enumerable.Empty <T> () par opposition à new List <T> () pour initialiser un IEnumerable <T>?

Supposons que vous ayez une personne de classe:

public class Person
{
   public string Name { get; set;}
   public IEnumerable<Role> Roles {get; set;}
}

Je devrais évidemment instancier les rôles dans le constructeur. Maintenant, je le faisais avec une liste comme celle-ci:

public Person()
{
   Roles = new List<Role>();
}

Mais j'ai découvert cette méthode statique dans l'espace de noms System.Linq

IEnumerable<T> Enumerable.Empty<T>();

De MSDN :

La méthode Empty(TResult)() met en cache une séquence vide de type TResult. Lorsque l'objet qu'il renvoie est énuméré, il ne donne aucun élément.

Dans certains cas, cette méthode est utile pour passer une séquence vide à une méthode définie par l'utilisateur qui prend une IEnumerable(T). Il peut également être utilisé pour générer un élément neutre pour des méthodes telles que Union. Voir la section Exemple pour un exemple de cette utilisation de

Alors vaut-il mieux écrire le constructeur comme ça? L'utilisez-vous? Pourquoi? ou sinon, pourquoi pas?

public Person()
{
   Roles = Enumerable.Empty<Role>();
}
125
Stéphane

Je pense que la plupart des messages ont manqué le point principal. Même si vous utilisez un tableau vide ou une liste vide, ce sont des objets et ils sont stockés en mémoire. Than Garbage Collector doit s'en occuper. Si vous avez affaire à une application à haut débit, cela pourrait avoir un impact notable.

Enumerable.Empty ne crée pas d'objet par appel mettant ainsi moins de charge sur GC.

Si le code se trouve dans un emplacement à faible débit, il se résume cependant à des considérations esthétiques.

142
Vadim Chekan

Je pense Enumerable.Empty<T> c'est mieux car c'est plus explicite: votre code indique clairement vos intentions. Il pourrait également être un peu plus efficace, mais ce n'est qu'un avantage secondaire.

84
Tommy Carlier

Sur le plan des performances, voyons comment Enumerable.Empty<T> est implémenté.

Il renvoie EmptyEnumerable<T>.Instance, qui est défini comme:

internal class EmptyEnumerable<T>
{
    public static readonly T[] Instance = new T[0];
}

Les champs statiques sur les types génériques sont alloués par paramètre de type générique. Cela signifie que l'environnement d'exécution peut paresseusement créer ces tableaux vides uniquement pour les types de code utilisateur requis et réutiliser les instances autant de fois que nécessaire sans ajouter de pression sur le garbage collector.

En être témoin:

Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>()));
21
Drew Noakes

En supposant que vous vouliez réellement remplir la propriété Roles d'une manière ou d'une autre, puis encapsulez cela en rendant son setter privé et en l'initialisant dans une nouvelle liste dans le constructeur:

public class Person
{
    public string Name { get; set; }
    public IList<Role> Roles { get; private set; }

    public Person()
    {
        Roles = new List<Role>();
    }
}

Si vous voulez vraiment vraiment avoir le setter public, laissez Roles avec une valeur de null et évitez l'allocation d'objet.

12
Neil Barnwell

Le problème avec votre approche est que vous ne pouvez pas ajouter d'éléments à la collection - j'aurais une structure privée comme une liste, puis exposerais les éléments en tant qu'énumérables:

public class Person
{
    private IList<Role> _roles;

    public Person()
    {
        this._roles = new List<Role>();
    }

    public string Name { get; set; }

    public void AddRole(Role role)
    {
        //implementation
    }

    public IEnumerable<Role> Roles
    {
        get { return this._roles.AsEnumerable(); }
    }
}

Si vous avez l'intention qu'une autre classe crée la liste des rôles (que je ne recommanderais pas), je n'initialiserais pas du tout l'énumérable dans Person.

7
Lee

Le problème typique avec l'exposition de la liste privée en tant qu'IEnumerable est que le client de votre classe peut jouer avec elle en transtypant. Ce code fonctionnerait:

  var p = new Person();
  List<Role> roles = p.Roles as List<Role>;
  roles.Add(Role.Admin);

Vous pouvez éviter cela en implémentant un itérateur:

public IEnumerable<Role> Roles {
  get {
    foreach (var role in mRoles)
      yield return role;
  }
}
6
Hans Passant

Le plus gros problème ici serait d'exposer Roles comme un champ public .

ce qui suit ressemble mieux:

public class Person
{
   public string Name { get; set; }  

   private List<Role> _roles = null;
   public IEnumerable<Role> Roles 
   {
      get { 
        if (_roles != null) 
          return _roles;
        else
          return Enumerable.Empty<Role>();
        }
   }
}

Et peut-être devriez-vous jeter un coup d'œil au retour en tant que ReadonlyCollection , selon la façon dont vous souhaitez l'utiliser.

Et Enumerable.Empty n'est pas better ici, juste un peu plus efficace lorsque les rôles restent généralement vides.

5
Henk Holterman