web-dev-qa-db-fra.com

Quels sont les avantages de marquer un champ comme `readonly` en C #?

Quels sont les avantages d'avoir une variable membre déclarée en lecture seule? S'agit-il uniquement d'une protection contre le changement de personne pendant le cycle de vie de la classe ou existe-t-il une amélioration de la vitesse du compilateur grâce à ce mot clé

237
leora

Le mot clé readonly est utilisé pour déclarer une variable membre constante, mais permet de calculer la valeur au moment de l'exécution. Cela diffère d'une constante déclarée avec le modificateur const, qui doit avoir sa valeur définie au moment de la compilation. À l'aide de readonly, vous pouvez définir la valeur du champ dans la déclaration ou dans le constructeur de l'objet dont le champ est membre.

Utilisez-le également si vous ne souhaitez pas recompiler des DLL externes faisant référence à la constante (car elle est remplacée à la compilation).

131
Bill the Lizard

Je ne pense pas que l’utilisation d’un champ en lecture seule permette de générer des gains de performances. C'est simplement une vérification pour s'assurer qu'une fois que l'objet est entièrement construit, ce champ ne peut pas être dirigé vers une nouvelle valeur. 

Cependant, "readonly" est très différent des autres types de sémantique en lecture seule car il est appliqué à l'exécution par le CLR. Le mot clé readonly est compilé jusqu'à .initièrement, ce qui est vérifiable par le CLR. 

L'avantage réel de ce mot clé est de générer des structures de données immuables. Les structures de données immuables, par définition, ne peuvent pas être modifiées une fois construites. Il est donc très facile de raisonner sur le comportement d’une structure au moment de l’exécution. Par exemple, il n'y a pas de danger de passer d'une structure immuable à une autre partie aléatoire du code. Ils ne peuvent jamais le changer, vous pouvez donc programmer de manière fiable contre cette structure.

Voici une bonne entrée sur l'un des avantages de l'immutabilité: Threading

166
JaredPar

L'utilisation de readonly ne présente aucun avantage en termes de performances, du moins aucun de ceux que j'ai vus ne soit mentionné nulle part. C'est juste pour faire exactement ce que vous suggérez, pour empêcher la modification une fois qu'elle a été initialisée.

Il est donc avantageux de pouvoir écrire un code plus robuste et plus lisible. Le véritable avantage de ce type de choses vient lorsque vous travaillez en équipe ou pour la maintenance. Déclarer quelque chose comme étant readonly revient à placer un contrat d'utilisation de cette variable dans le code. Pensez-y comme si vous ajoutiez de la documentation de la même manière que d’autres mots-clés tels que internal ou private, vous dites que "cette variable ne doit pas être modifiée après l’initialisation", et que vous la respectez.

Ainsi, si vous créez une classe et marquez certaines variables membres readonly de par leur conception, vous empêchez vous-même ou un autre membre de l'équipe de commettre une erreur ultérieurement lors de l'extension ou de la modification de votre classe. À mon avis, c'est un avantage qui en vaut la peine (à la petite dépense de complexité linguistique supplémentaire, comme le fait remarquer Doofledorfer dans les commentaires).

59
Xiaofu

Pour le dire en termes très pratiques:

Si vous utilisez un const dans les références dll A et dll B, la valeur de ce const sera compilée dans dll B. Si vous redéployez dll A avec une nouvelle valeur pour ce const, dll B utilisera toujours la valeur d'origine.

Si vous utilisez une référence en lecture seule dans les DLL A et les DLL B en lecture seule, cette recherche en lecture seule sera toujours effectuée au moment de l'exécution. Cela signifie que si vous redéployez la DLL A avec une nouvelle valeur en lecture seule, la DLL B utilisera cette nouvelle valeur. 

43
Daniel Auger

Il existe un cas potentiel où le compilateur peut optimiser les performances en fonction de la présence du mot clé readonly.

Ceci ne s'applique que si le champ en lecture seule est également marqué comme statique . Dans ce cas, le compilateur JIT peut supposer que ce champ statique ne changera jamais. Le compilateur JIT peut en tenir compte lors de la compilation des méthodes de la classe.

Exemple typique: votre classe pourrait avoir un champ statique readonly IsDebugLoggingEnabled initialisé dans le constructeur (basé sur un fichier de configuration, par exemple). Une fois que les méthodes réelles sont compilées JIT, le compilateur peut omettre des parties entières du code lorsque la journalisation du débogage n'est pas activée.

Je n'ai pas vérifié si cette optimisation est réellement implémentée dans la version actuelle du compilateur JIT, il ne s'agit donc que de spéculation.

12
Kristof Verbiest

N'oubliez pas que readonly ne s'applique qu'à la valeur elle-même. Par conséquent, si vous utilisez un type de référence, readonly ne protège que la référence. L'état de l'instance n'est pas protégé par readonly. 

6
Brian Rasmussen

N'oubliez pas qu'il existe une solution de contournement pour obtenir les champs readonly définis en dehors de tout constructeur à l'aide de out params. 

Un peu brouillon mais:

private readonly int _someNumber;
private readonly string _someText;

public MyClass(int someNumber) : this(data, null)
{ }

public MyClass(int someNumber, string someText)
{
    Initialise(out _someNumber, someNumber, out _someText, someText);
}

private void Initialise(out int _someNumber, int someNumber, out string _someText, string someText)
{
    //some logic
}

Plus de discussion ici: http://www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx

5
Adam Naylor

Si vous avez une valeur prédéfinie ou pré calculée qui doit rester la même tout au long du programme, vous devez utiliser constant, mais si vous avez une valeur à fournir au moment de l'exécution mais qu'une fois affectée, elle doit rester identique tout au long du programme, vous devez utiliser lecture seulement. Par exemple, si vous devez attribuer l'heure de début du programme ou stocker une valeur fournie par l'utilisateur lors de l'initialisation de l'objet et que vous devez le restreindre pour toute modification ultérieure, vous devez utiliser le mode lecture seule.

2
waqar ahmed

Ajout d'un aspect de base pour répondre à cette question:

Les propriétés peuvent être exprimées en lecture seule en laissant de côté l'opérateur set. Ainsi, dans la plupart des cas, vous n'aurez pas besoin d'ajouter le mot clé readonly aux propriétés:

public int Foo { get; }  // a readonly property

Contrairement à cela: les champs ont besoin du mot clé readonly pour obtenir un effet similaire:

public readonly int Foo; // a readonly field

Ainsi, un avantage de marquer un champ comme étant readonly peut être d'atteindre un niveau de protection en écriture similaire à celui d'une propriété sans opérateur set - sans avoir à changer le champ en une propriété, si pour quelque raison que ce soit.

1
Chris 14214

Étonnamment, en lecture seule, le code peut être ralenti, comme l’a constaté Jon Skeet lors du test de sa bibliothèque Noda Time. Dans ce cas, un test exécuté en 20 secondes ne prenait que 4 secondes après la suppression en lecture seule.

https://codeblog.jonskeet.fr/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/

1
Neil

readonly peut être initialisé lors de la déclaration ou obtenir sa valeur uniquement du constructeur. Contrairement à const, il doit être initialisé et déclaré en même temps .readonlya toutconsta, plus initialisation constructeur

codehttps://repl.it/HvRU/1

using System;

class MainClass {
    public static void Main (string[] args) {

        Console.WriteLine(new Test().c);
        Console.WriteLine(new Test("Constructor").c);
        Console.WriteLine(new Test().ChangeC()); //Error A readonly field 
        // `MainClass.Test.c' cannot be assigned to (except in a constructor or a 
        // variable initializer)
    }


    public class Test {
        public readonly string c = "Hello World";
        public Test() {

        }

        public Test(string val) {
          c = val;
        }

        public string ChangeC() {
            c = "Method";
            return c ;
        }
    }
}
1
Mina Gabriel

Soyez prudent avec les tableaux privés en lecture seule. Si ceux-ci sont exposés en tant qu'objet d'un client (vous pouvez le faire pour COM interop comme je l'ai fait), le client peut manipuler des valeurs de tableau. Utilisez la méthode Clone () lors du renvoi d'un tableau en tant qu'objet.

1
Michael

Une autre partie intéressante de l'utilisation du marquage en lecture seule peut être la protection du champ contre l'initialisation en singleton. 

par exemple dans le code de csharpindepth :

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

readonly joue un petit rôle dans la protection du champ Singleton d’être initialisé deux fois. Un autre détail est que pour le scénario mentionné, vous ne pouvez pas utiliser const car const force la création pendant la compilation, mais singleton en crée au moment de l'exécution.

0
Yuriy Zaletskyy

Les performances de WPF peuvent présenter des avantages en termes de performances, car elles suppriment le recours coûteux à DependencyProperties. Cela peut être particulièrement utile avec les collections

0
Shane