web-dev-qa-db-fra.com

Une liste const en C #

Je voudrais créer une liste en C # qui après sa création je ne pourrai pas en ajouter ou en supprimer. Par exemple, je vais créer la liste;

List<int> lst = a;

(a est une liste existante), mais après je ne pourrai pas écrire le code (il le marquera comme une erreur):

lst.Add(2);
32
Ofer Magen

.NET prend en charge les collections véritablement immuables, les vues en lecture seule des collections mutables et les interfaces en lecture seule implémentées par les collections mutables.


Une telle collection immuable est ImmutableArray<> que vous pouvez créer sous la forme a.ToImmutableArray() dans votre exemple. Assurez-vous de jeter un œil aux autres options des listes MSDN car vous pouvez être mieux servi par une autre collection immuable. Si vous voulez faire des copies de la séquence originale avec de légères modifications, ImmutableList<> pourrait être plus rapide, par exemple (le tableau est moins cher à créer et à accéder, cependant). Notez que a.Add(...); est valide, mais renvoie une nouvelle collection plutôt que de modifier a. Si vous avez resharper, cela vous avertira si vous ignorez la valeur de retour d'une méthode pure comme Add (et il peut y avoir une extension roslyn pour faire quelque chose de similaire que je ne connais pas). Si vous suivez cette voie - considérez sautez List<> Entièrement et passez directement aux collections immuables.

Les vues en lecture seule des collections modifiables sont un peu moins sûres mais prises en charge sur les anciennes versions de .NET. Le type d'habillage est appelé ReadOnlyCollection<> , que dans votre exemple vous pourriez construire comme a.AsReadOnly() . Cette collection ne pas garantit l'immuabilité; il ne garantit que vous ne peut pas le changer. Un autre morceau de code qui partage une référence au List<> Sous-jacent peut toujours le changer. En outre, ReadOnlyCollection impose également une surcharge supplémentaire; il est donc possible que vous ne gagniez pas beaucoup en évitant les collections immuables pour des raisons de performances (A FAIRE: comparer cette affirmation) Vous pouvez utiliser un wrapper en lecture seule comme celui-ci même dans une API publique en toute sécurité - il n'y a aucun moyen (non réfléchi) d'obtenir la liste sous-jacente. Cependant, comme il n'est souvent pas plus rapide que les collections immuables et qu'il n'est pas non plus entièrement sûr, je recommande de éviterReadOnlyCollection<> - I jamais utiliser cela plus personnellement.

Les interfaces en lecture seule implémentées par des collections mutables sont encore plus bas dans l'échelle de sécurité, mais rapides. Vous pouvez simplement convertir List<> En IReadOnlyList<> , ce que vous pourriez faire dans votre exemple comme IReadOnlyList<int> lst = a. Ceci est mes préférences pour internal code - vous bénéficiez toujours d'une sécurité de type statique, vous n'êtes tout simplement pas protégé contre le code malveillant ou le code qui utilise les vérifications de type et les transtypages imprudemment (mais ceux-ci sont évitables via le code -avis sur mon expérience). Je n'ai jamais été mordu par ce choix, mais il est moins sûr que les deux options ci-dessus. À la hausse, il n'encourt aucune allocation et est plus rapide. Si vous le faites couramment, vous souhaiterez peut-être définir une méthode d'extension pour effectuer la conversion ascendante pour vous (les conversions peuvent être dangereuses en C # car non seulement elles effectuent des montées sécurisées, mais peuvent également échouer lors des descentes et des conversions définies par l'utilisateur - c'est donc un bon idée pour éviter les transtypages explicites partout où vous le pouvez).

Notez que dans tous les cas, seule la séquence elle-même est en lecture seule. Les objets sous-jacents ne sont pas affectés (par exemple, un int ou string sont immuables, mais des objets plus compliqués peuvent l'être ou non).


TL; DR:

  • Pour sécurité : utilisez a.ToImmutableArray() pour créer une copie immuable dans un ImmutableArray<int> .
  • Pour les performances : utilisez IReadOnlyList<int> pour éviter une mutation accidentelle dans le code interne avec un minimum de performances. Sachez que quelqu'un peut le restituer à List<> (Ne faites pas cela), ce qui rend cela moins "sûr" pour une API publique.
  • Évitez a.AsReadOnly() qui crée un ReadOnlyCollection<int> sauf vous travaillez sur une base de code héritée qui ne prend pas en charge les nouvelles alternatives, ou si vous savez vraiment ce que vous faites et avez des besoins spéciaux (par exemple, vous voulez vraiment muter la liste ailleurs et avoir une vue en lecture seule) .
44
Eamon Nerbonne

Vous pouvez utiliser ImmutableList<T> / ImmutableArray<T> de System.Collections.Immutable NuGet :

var immutable = ImmutableList<int>.Create(1, 2, 3);

Ou en utilisant la méthode d'extension ToImmutableList:

var immutable = mutableList.ToImmutableList();

Dans le cas où Add est invoqué, * une nouvelle copie * est retournée et ne modifie pas la liste d'origine. Cependant, cela ne provoquera pas d'erreur de compilation.

10
Yuval Itzchakov

Vous avez besoin d'un ReadonlyCollection. Vous pouvez en créer un à partir d'une liste en appelant List.AsReadOnly()

Référence: https://msdn.Microsoft.com/en-us/library/ms132474.aspx

8
Patrick Allwood

Pourquoi ne pas simplement utiliser un IEnumerable?

IEnumerable<string> myList = new List<string> { "value1", "value2" };
6
Yves Larouche

Votre meilleur pari ici est d'utiliser un IReadOnlyList<int>.

L'avantage d'utiliser IReadOnlyList<int> Par rapport à List.AsReadOnly() est qu'un ReadOnlyCollection<T> Peut être affecté à un IList<T>, Auquel on peut ensuite accéder via un indexeur accessible en écriture.

Exemple à clarifier:

var original = new List<int> { 1, 2, 3 };
IReadOnlyList<int> readOnlyList = original;

Console.WriteLine(readOnlyList[0]); // Compiles.

readOnlyList[0] = 0; // Does not compile.

var readOnlyCollection = original.AsReadOnly();

readOnlyCollection[0] = 1; // Does not compile.

IList<int> collection = readOnlyCollection; // Compiles.

collection[0] = 1; // Compiles, but throws runtime exception. 

L'utilisation d'un IReadOnlyList<int> Évite la possibilité de transmettre accidentellement la liste en lecture seule à une méthode qui accepte un IList<> Et qui essaie ensuite de modifier un élément - ce qui entraînerait une exception d'exécution.

5
Matthew Watson

Je recommande d'utiliser un System.Collections.Immutable.ImmutableList<T> instance mais référencée par une variable ou une propriété de type System.Collections.Generic.IReadOnlyList<T>. Si vous utilisez simplement une liste immuable nue, vous n'aurez pas d'erreurs pour l'ajouter, comme vous le souhaitez.

System.Collections.Generic.IReadOnlyList<int> list = a.ToImmutableList();
5
Aluan Haddad

Comme alternative aux réponses déjà publiées, vous pouvez envelopper un readonly régulier List<T> dans un objet qui l'expose en tant que IReadOnlyList.

class ROList<T>
{
    public ROList(IEnumerable<T> argEnumerable)
    {
        m_list = new List<T>(argEnumerable);
    }

    private readonly List<T> m_list;
    public IReadOnlyList<T> List { get { return m_list; } }
}

void Main()
{
    var list = new  List<int> {1, 2, 3};
    var rolist = new ROList<int>(list);

    foreach(var i in rolist.List)
        Console.WriteLine(i);

    //rolist.List.Add(4); // Uncomment this and it won't compile: Add() is not allowed
}
4
elnigno

Il pourrait être IReadOnlyList<int>, par exemple.

  IReadOnlyList<int> lst = a;

Ainsi, la liste initiale (a) est mutable tandis que lst est not. Nous utilisons souvent IReadOnlyList<T> pour public propriétés et IList<T> pour privé ceux, par exemple.

  public class MyClass {
    // class itself can modify m_MyList 
    private IList<int> m_MyList = new List{1, 2, 3};

    ...
    // ... while code outside can't  
    public IReadOnlyList<int> MyList {
      get {
        return m_MyList; 
      }
    }  
  }
4
Dmitry Bychenko