2009-03-06 9 views
19

J'ai la classe suivante:« Lecture seule » propriété accesseur en C#

class SampleClass 
{ 
    private ArrayList mMyList; 

    SampleClass() 
    { 
     // Initialize mMyList 
    } 

    public ArrayList MyList 
    { 
     get { return mMyList;} 
    } 
} 

Je veux que les utilisateurs soient en mesure d'obtenir mMyList qui est pourquoi j'exposais le « get » via une propriété mais je ne suis pas veulent les changements qu'ils apportent à l'objet (par exemple MyList.Add (new Class());) pour revenir dans ma classe.

Je suppose que je peux retourner une copie de l'objet mais cela peut être lent et je cherche un moyen qui fournira une erreur de compilation informant l'utilisateur qu'ils ne devraient pas s'attendre à pouvoir modifier le valeur renvoyée de la propriété.

Est-ce possible?

+0

question connexe: http://stackoverflow.com/questions/681287/how-to-make-a-reference-type-property-readonly –

+2

Est-ce que 'List : ReadOnlyCollection ' dans .NET 4.5 change cela? http://www.infoq.com/news/2011/10/ReadOnly-WInRT/ –

Répondre

25

Avec une ArrayList, vous êtes assez limité car il n'y a pas de classe de collection non générique en lecture seule dans la BCL. La solution rapide et sale est de retourner un type de IEnumerable.

public IEnumerable MyList 
    { 
     get { return mMyList;} 
    } 

Cela empêchera pas vraiment quelqu'un de jeter à ArrayList mais il ne permettra pas à des modifications par défaut soit

Vous pouvez retourner une liste en lecture seule efficace en appelant ArrayList.ReadOnly. Cependant, son type de retour est une ArrayList donc l'utilisateur pourrait encore compiler avec .Add mais cela produirait une erreur d'exécution.

1

Vous devez placer la valeur de retour dans une collection en lecture seule.

19

Utilisez la méthode ArrayList.ReadOnly() pour construire et renvoyer un wrapper en lecture seule autour de la liste. il ne va pas copier la liste, mais simplement en faire un wrapper en lecture seule. Pour obtenir une vérification à la compilation, vous souhaiterez probablement renvoyer l'encapsuleur en lecture seule comme IEnumerable comme suggéré par @Jared.

public IEnumerable MyList 
{ 
    get { return ArrayList.ReadOnly(mMyList); } 
} 

Une collection qui est en lecture seule est simplement une collection avec une enveloppe qui empêche la modification de la collection . Si des modifications sont apportées à la collection sous-jacente , la collection en lecture seule reflète ces modifications.

Cette méthode est une opération O (1).

Reference

+2

Je suis contre cette solution car elle transforme ce qui devrait être une erreur de compilation en une erreur d'exécution. – JaredPar

+0

-1, OP: "Je cherche un moyen qui fournira une erreur de ** compilation" –

+0

Sans copier à une ReadOnlyCollection - qu'il a également dit qu'il voulait éviter - je choisis d'appliquer la lecture seulement en faisant semblant d'être en lecture seule. Faire les deux est probablement mieux. – tvanfosson

-3

Assurez la propriété comme scellé (ou NotInheritable en VB.NET) et en lecture seule, comme ceci:

public sealed readonly ArrayList MyList 
    { 
     get { return mMyList;} 
    } 

Aussi ne pas oublier de rendre le champ de support en lecture seule :

private readonly ArrayList mMyList; 

Mais pour initialiser la valeur de mMyList, vous devez l'initialiser uniquement dans le constructeur, un s dans cet exemple:

public SampleClass() 
{ 
    // Initialize mMyList 
    mMyList = new ArrayList(); 
} 
10

Juste pour développer la réponse de JaredPar. Comme il l'a dit, le retour du champ de sauvegarde réel n'est pas complètement sûr, puisque l'utilisateur est encore capable de lancer dynamiquement le IEnumerable à ArrayList.

Je voudrais écrire quelque chose comme ça pour être sûr aucune modification à la liste initiale sont faites:

public IEnumerable MyList 
{ 
    get 
    { 
     foreach (object o in mMyList) 
      yield return o; 
    } 
} 

Là encore, je probabily également utiliser une liste générique (IEnumerable<T>) à taper en toute sécurité.

+0

* Ce * est le bon - souhaite que je puisse voter deux fois. Un problème avec ne pas appeler 'rupture de rendement;' après la boucle? – Jay

+0

@Jay: Il n'y a pas besoin de 'break break' explicite après la boucle. La fin de la méthode implique la fin du 'Enumerator' (et' MoveNext' retournera correctement 'false'). –

+0

Avec cette solution l'utilisateur ne perd-il pas la fonctionnalité de ArrayList telle que l'accès rapide et rapide et le compte rapide? –

4

Utilisez un ReadOnlyCollection.

return new ReadOnlyCollection<object>(mMyList).

Vous pouvez également créer un champ ReadOnlyCollection et refléter automatiquement les modifications dans la liste sous-jacente. C'est la solution la plus efficace.

Si vous savez que mMyList ne contient qu'un seul type d'objet (par exemple, il n'a que DateTimes), vous pouvez retourner un ReadOnlyCollection<DateTime> (ou un autre type) pour la sécurité de type supplémentaire. Si vous utilisez C# 3, vous pouvez simplement écrire

return new ReadOnlyCollection<DateTime>(mMyList.OfType<DateTime>().ToArray())

Cependant, cela met pas à jour automatiquement la liste sous-jacente, et est également moins efficace (il copie la liste complète) . La meilleure option est de faire mMyList un List<T> générique (par exemple, un List<DateTime>)

0

Disponible à partir v2.0 .NET

public ArrayList MyList { get; private set; } 
+0

Vous pouvez toujours utiliser Ajouter sur une liste existante. –

Questions connexes