Const liste en C#
je voudrais créer une liste en C# qui après sa création, je ne vais pas être en mesure d'ajouter ou de supprimer des éléments. 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);
8 réponses
.NET prend en charge les collections vraiment immuables, les vues en lecture seule des collections mutables et les interfaces en lecture seule implémentées par des collections mutables.
un tel immuable collectionImmutableArray<>
que vous pouvez créer comme a.ToImmutableArray()
dans ton exemple. Assurez-vous de jeter un oeil aux autres options listes MSDN parce que vous pouvez être mieux servi par une collection immuable différente. 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 retourne une nouvelle collection plutôt que de changer a
. Si vous avez resharper, que vous avertira si vous ignorez la valeur de retour d'une méthode pure comme Add
(et il peut y avoir une extension de roslyn pour faire quelque chose de semblable que je ne suis pas au courant). Si vous êtes aller dans cette voie - considérer sauter List<>
entièrement et directement immuable collections.
Lecture seule vue de mutable collections sont un peu moins sûrs mais pris en charge sur les versions plus anciennes de .NET. Le type d'emballage s'appelle ReadOnlyCollection<>
, qui, dans votre exemple, vous pourriez construire a.AsReadOnly()
. Cette collection fait garantit l'immuabilité; il ne garantit que des garanties vous vous ne pouvez pas le changer. Un autre morceau de code qui partage un référence au sous-jacent List<>
peut encore changer. En outre, ReadOnlyCollection impose également certains frais généraux supplémentaires, de sorte que vous ne pouvez pas gagner beaucoup en évitant les collections immuables pour des raisons de performance (TODO: benchmark cette revendication). Vous utilisez 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-reflection) d'obtenir la liste sous-jacente. Cependant, comme il n'est souvent pas plus rapide que des collections immuables, et il n'est pas non plus entièrement sûr, je vous recommande d' éviterReadOnlyCollection<>
- I jamais utilisez encore ceci, personnellement.
interfaces en lecture seule implémentées par des collections mutables sont encore plus bas de l'échelle de sécurité, mais rapide. Vous pouvez tout simplement jeté List<>
IReadOnlyList<>
, qui vous pourriez faire dans votre exemple comme IReadOnlyList<int> lst = a
. C'est mes préférences pour les interne code-vous obtenez toujours statique la sécurité de type, vous n'êtes tout simplement pas protégé contre le code malveillant ou le code qui utilise des contrôles de type et jette imprudemment (mais ceux-ci sont évitables via codes-revues dans mon expérience). Je n'ai jamais été mordu par ce choix, mais il moins sûr que les deux options. D'un autre côté, il n'engage aucune allocation et est plus rapide. Si vous le faites souvent, vous pouvez définir une méthode d'extension pour faire l'upcast pour vous (casts peuvent être dangereux en C# parce qu'ils ne font pas seulement des upcasts sûrs, mais il est possible que vous ne réussissiez pas les downcasts, et les conversions définies par l'utilisateur-c'est donc une bonne idée d'éviter les casts 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 les objets plus compliqués peuvent ou ne peuvent pas l'être).
TL;DR:
- sécurité: Utiliser
a.ToImmutableArray()
pour créer immuable copie dans unImmutableArray<int>
. - performances: Utiliser
IReadOnlyList<int>
pour aider à prévenir la mutation accidentelle dans le code interne avec des performances aériennes minimales. Soyez conscient que quelqu'un peut le renvoyer àList<>
(ne faites pas cela), ce qui rend la chose moins "sécuritaire" pour une api publique. - Éviter
a.AsReadOnly()
ce qui crée unReadOnlyCollection<int>
sauf si vous travaillez sur une base de code legacy cela ne supporte pas 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).
Vous pouvez utiliser ImmutableList<T>
/ImmutableArray<T>
System.Collections.Immutable
NuGet:
var immutable = ImmutableList<int>.Create(1, 2, 3);
Ou ToImmutableList
méthode d'extension:
var immutable = mutableList.ToImmutableList();
au cas où Add
est invoqué, *une nouvelle copie * est retournée et ne modifie pas la liste originale. Ce ne sera pas provoquer une erreur de compilation.
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
pourquoi ne pas simplement utiliser un IEnumerable?
IEnumerable<string> myList = new List<string> { "value1", "value2" };
je recommande d'utiliser un System.Collections.Immutable.ImmutableList<T>
exemple mais référencé par une variable ou une propriété de type System.Collections.Generic.IReadOnlyList<T>
. Si vous utilisez juste une liste immuable nue, vous n'obtiendrez pas d'erreurs pour l'ajouter, comme vous le désirez.
System.Collections.Generic.IReadOnlyList<int> list = a.ToImmutableList();
comme alternative aux réponses déjà postées, vous pouvez envelopper un readonly
List<T>
dans un objet qui expose 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
}
Votre meilleur pari ici est d'utiliser IReadOnlyList<int>
.
l'avantage d'utiliser IReadOnlyList<int>
par rapport à List.AsReadOnly()
est un ReadOnlyCollection<T>
peut être assigné à un IList<T>
, qui peut alors être consulté via un indexeur en écriture.
Exemple pour 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.
en utilisant un IReadOnlyList<int>
évite la possibilité de accidentellement passer la liste en lecture seule à une méthode qui accepte un IList<>
et qui tente de modifier un élément, qui entraînerait une exécution exception.
IReadOnlyList<int>
, e.g.
IReadOnlyList<int> lst = a;
Donc la liste initiale (a
mutablelst
. Souvent nous utilisons IReadOnlyList<T>
public propriétés et IList<T>
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;
}
}
}