Quelle est la différence entre const et readonly en C #?


1363

Quelle est la différence entre constet readonlyen C #?

Quand utiliseriez-vous l'un sur l'autre?


J'ai dû regarder plusieurs réponses pour trouver ce lien, mais c'est une bonne réponse. La vision d'Eric Lippert sur l'immuabilité en C #
Frank Bryce

2
@donstack, en fait selon la référence C # , un champ en lecture seule peut être affecté et réaffecté plusieurs fois dans la déclaration de champ et le constructeur.
Marques

Réponses:


1289

Outre la différence apparente de

  • avoir à déclarer la valeur au moment de la définition d'une valeur constVS readonlypeut être calculé dynamiquement mais doit être assigné avant que le constructeur ne quitte .. après cela il est figé.
  • 'const sont implicitement static. Vous utilisez une ClassName.ConstantNamenotation pour y accéder.

Il y a une subtile différence. Considérons une classe définie dans AssemblyA.

public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly int I_RO_VALUE;
  public Const_V_Readonly()
  {
     I_RO_VALUE = 3;
  }
}

AssemblyBréférence AssemblyAet utilise ces valeurs dans le code. Lorsque cela est compilé,

  • dans le cas de la constvaleur, c'est comme un find-replace, la valeur 2 est «cuite dans» l' AssemblyBIL du. Cela signifie que si demain je mettrai à jour I_CONST_VALUE20 à l'avenir. AssemblyBaurait encore 2 jusqu'à ce que je le recompile .
  • dans le cas de la readonlyvaleur, c'est comme un refvers un emplacement mémoire. La valeur n'est pas intégrée dans AssemblyBl'IL de. Cela signifie que si l'emplacement mémoire est mis à jour, AssemblyBobtient la nouvelle valeur sans recompilation. Donc, si I_RO_VALUEest mis à jour à 30, il vous suffit de construire AssemblyA. Tous les clients n'ont pas besoin d'être recompilés.

Donc, si vous êtes sûr que la valeur de la constante ne changera pas, utilisez a const.

public const int CM_IN_A_METER = 100;

Mais si vous avez une constante qui peut changer (précision egwrt) .. ou en cas de doute, utilisez a readonly.

public readonly float PI = 3.14;

Mise à jour: Aku doit obtenir une mention parce qu'il l'a d'abord signalé. J'ai aussi besoin de brancher où j'ai appris cela .. C # efficace - Bill Wagner


77
Le staticpoint semble être le point le plus important et le plus utile -consts are implicitly static
LCJ

28
La partie sur les valeurs de référence est la plus importante. Les valeurs const peuvent être optimisées.
CodingBarfield

22
readonlyles variables peuvent être modifiées en dehors du constructeur (réflexion). Ce n'est que le compilateur qui essaie de vous empêcher de modifier la var en dehors du constructeur.
Bitterblue

12
Les readonlyvariables @ mini-me ne peuvent pas être modifiées une fois que le constructeur a terminé, même par réflexion. Le runtime n'arrive pas à appliquer cela. Le runtime n'arrive pas non plus à appliquer ce que vous ne changez string.Emptypas "Hello, world!", mais je ne dirais toujours pas que cela rend string.Emptymodifiable, ou que le code ne devrait pas supposer que ce string.Emptysera toujours une chaîne de longueur nulle.

7
blogs.msmvps.com/jonskeet/2014/07/16/… est une lecture intéressante seulement des frais généraux de readonly
CAD bloke

275

Il y a un piège avec des consts! Si vous référencez une constante d'un autre assembly, sa valeur sera compilée directement dans l'assembly appelant. De cette façon, lorsque vous mettez à jour la constante dans l'assembly référencé, elle ne change pas dans l'assembly appelant!


8
Lors de la décompilation (Reflector, ILSpy, ..) une constante JAMAIS JAMAIS est référencée par n'importe qui, peu importe le même assemblage ou un autre assemblage, vous ne pouvez donc pas du tout analyser l'utilisation d'une constante dans le code compilé.
springy76

159

Constantes

  • Les constantes sont statiques par défaut
  • Ils doivent avoir une valeur au moment de la compilation (vous pouvez avoir par exemple 3.14 * 2, mais vous ne pouvez pas appeler de méthodes)
  • Pourrait être déclaré dans les fonctions
  • Sont copiés dans chaque assembly qui les utilise (chaque assembly obtient une copie locale des valeurs)
  • Peut être utilisé dans les attributs

Champs d'instance en lecture seule

  • Doit avoir défini une valeur, au moment où le constructeur quitte
  • Sont évalués lors de la création de l'instance

Champs statiques en lecture seule

  • Sont évalués lorsque l'exécution de code atteint la référence de classe (lorsqu'une nouvelle instance est créée ou qu'une méthode statique est exécutée)
  • Doit avoir une valeur évaluée au moment où le constructeur statique est terminé
  • Il n'est pas recommandé de mettre ThreadStaticAttribute sur ceux-ci (les constructeurs statiques seront exécutés dans un seul thread et définiront la valeur de son thread; tous les autres threads auront cette valeur non initialisée)

58

Juste pour ajouter, ReadOnly pour les types de référence ne fait que la référence en lecture seule et non les valeurs. Par exemple:

public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly char[] I_RO_VALUE = new Char[]{'a', 'b', 'c'};

  public UpdateReadonly()
  {
     I_RO_VALUE[0] = 'V'; //perfectly legal and will update the value
     I_RO_VALUE = new char[]{'V'}; //will cause compiler error
  }
}

Existe-t-il un autre type de référence que stringcelui que vous pourriez utiliser comme constante?
springy76

Vous pouvez avoir constdes types de référence autres que chaîne, mais la constante ne peut avoir que la valeur null.
Mike Rosoft

40

Cela explique cela . Résumé: const doit être initialisé au moment de la déclaration, readonly peut être initialisé sur le constructeur (et donc avoir une valeur différente selon le constructeur utilisé).

EDIT: Voir le gotcha de Gishu ci-dessus pour la différence subtile


32

const: Ne peut être changé nulle part.

readonly: Cette valeur ne peut être modifiée que dans le constructeur. Ne peut pas être modifié dans les fonctions normales.


26

Il y a un petit gotcha avec lecture seule. Un champ en lecture seule peut être défini plusieurs fois dans le ou les constructeurs. Même si la valeur est définie dans deux constructeurs chaînés différents, elle est toujours autorisée.


public class Sample {
    private readonly string ro;

    public Sample() {
        ro = "set";
    }

    public Sample(string value) : this() {
        ro = value; // this works even though it was set in the no-arg ctor
    }
}

26

Un membre constant est défini au moment de la compilation et ne peut pas être modifié au moment de l'exécution. Les constantes sont déclarées sous forme de champ, à l'aide du constmot clé et doivent être initialisées au fur et à mesure qu'elles sont déclarées.

public class MyClass
{
    public const double PI1 = 3.14159;
}

Un readonlymembre est comme une constante en ce qu'il représente une valeur immuable. La différence est qu'un readonlymembre peut être initialisé au moment de l'exécution, dans un constructeur, tout en pouvant être initialisé lors de leur déclaration.

public class MyClass1
{
     public readonly double PI2 = 3.14159;

     //or

     public readonly double PI3;

     public MyClass2()
     {
         PI3 = 3.14159;
     }
}

const

  • Ils ne peuvent pas être déclarés comme static(ils sont implicitement statiques)
  • La valeur de constante est évaluée au moment de la compilation
  • les constantes sont initialisées uniquement lors de la déclaration

lecture seulement

  • Ils peuvent être au niveau de l'instance ou statiques
  • La valeur est évaluée au moment de l'exécution
  • readonly peut être initialisé en déclaration ou par code dans le constructeur

6
Ils ne peuvent pas être statiques , ils sont statiques. Vous devriez être clair si vous vouliez dire qu'on ne peut pas déclarerstatic const int i = 0;
nawfal

Pouvez-vous expliquer pourquoi les constdéclarations ne peuvent pas être faites à l'intérieur des méthodes?
Minh Tran

21

Un const est une constante de compilation tandis que la lecture seule permet de calculer une valeur au moment de l'exécution et de la définir dans le constructeur ou l'initialiseur de champ. Ainsi, un «const» est toujours constant, mais «readonly» est en lecture seule une fois qu'il est attribué.

Eric Lippert de l'équipe C # a plus d'informations sur les différents types d'immuabilité


15

Voici un autre lien montrant comment const n'est pas une version sûre ou pertinente pour les types de référence.

Résumé :

  • La valeur de votre propriété const est définie au moment de la compilation et ne peut pas changer au moment de l'exécution
  • Const ne peut pas être marqué comme statique - le mot-clé indique qu'ils sont statiques, contrairement aux champs en lecture seule qui le peuvent.
  • Const ne peut pas être autre chose que des types de valeur (primitifs)
  • Le mot clé en lecture seule marque le champ comme immuable. Cependant, la propriété peut être modifiée à l'intérieur du constructeur de la classe
  • Le mot clé en lecture seule peut également être combiné avec statique pour le faire agir de la même manière qu'un const (au moins en surface). Il y a une différence marquée quand on regarde l'IL entre les deux
  • les champs const sont marqués comme "littéraux" en IL tandis que readonly est "initonly"

11

Lecture seule : la valeur peut être modifiée via Ctor lors de l'exécution. Mais pas via la fonction membre

Constante : par défaut statique. La valeur ne peut pas être modifiée de n'importe où (Ctor, Function, runtime etc no-where)


merci de ne pas m'avoir fait lire 4 paragraphes juste pour ces deux plats à emporter ...
Don Cheadle


6

Je crois qu'une constvaleur est la même pour tous les objets (et doit être initialisée avec une expression littérale), alors qu'elle readonlypeut être différente pour chaque instanciation ...


5

L'un des membres de l'équipe de notre bureau a fourni les conseils suivants sur le moment d'utiliser const, static et readonly:

  • Utilisez const lorsque vous avez une variable d'un type que vous pouvez savoir au moment de l'exécution (littéral de chaîne, entier, double, énumérations, ...) que vous voulez que toutes les instances ou les consommateurs d'une classe aient accès à l'endroit où la valeur ne doit pas changer.
  • Utilisez statique lorsque vous disposez de données pour que toutes les instances ou tous les consommateurs d'une classe aient accès à l'endroit où la valeur peut changer.
  • Utilisez statique en lecture seule lorsque vous avez une variable d'un type que vous ne pouvez pas savoir au moment de l'exécution (objets) que vous souhaitez que toutes les instances ou les consommateurs d'une classe aient accès à l'endroit où la valeur ne doit pas changer.
  • Utilisez en lecture seule lorsque vous avez une variable de niveau d'instance que vous saurez au moment de la création d'objet qui ne devrait pas changer.

Une dernière remarque: un champ const est statique, mais l'inverse n'est pas vrai.


1
Je pense que vous voulez dire «converser». L'inverse serait "un champ non const n'est pas statique". Ce qui peut être vrai ou non. L'inverse, "un champ statique est (toujours) const" n'est pas vrai.
Michael Blackburn

5

Ils sont tous les deux constants, mais un const est également disponible au moment de la compilation. Cela signifie qu'un aspect de la différence est que vous pouvez utiliser des variables const comme entrée pour attribuer des constructeurs, mais pas des variables en lecture seule.

Exemple:

public static class Text {
  public const string ConstDescription = "This can be used.";
  public readonly static string ReadonlyDescription = "Cannot be used.";
}

public class Foo 
{
  [Description(Text.ConstDescription)]
  public int BarThatBuilds {
    { get; set; }
  }

  [Description(Text.ReadOnlyDescription)]
  public int BarThatDoesNotBuild {
    { get; set; }
  }
}

5
  • quand utiliser constoureadonly

    • const

      • constante de compilation : constante absolue , la valeur est définie lors de la déclaration, se trouve dans le code IL lui-même
    • readonly

      • constante d' exécution : peut être définie dans le constructeur / init via le fichier de configuration, c'est App.config-à- dire , mais une fois initialisée, elle ne peut pas être modifiée

4

Les variables marquées const sont un peu plus que des macros #define fortement typées, au moment de la compilation, les références des variables const sont remplacées par des valeurs littérales en ligne. Par conséquent, seuls certains types de valeurs primitifs intégrés peuvent être utilisés de cette manière. Les variables marquées en lecture seule peuvent être définies, dans un constructeur, au moment de l'exécution et leur lecture seule est également appliquée lors de l'exécution. Il y a un coût de performance mineur associé à cela, mais cela signifie que vous pouvez utiliser en lecture seule avec n'importe quel type (même les types de référence).

En outre, les variables const sont intrinsèquement statiques, tandis que les variables en lecture seule peuvent être spécifiques à l'instance si vous le souhaitez.


Ajout que les consts sont des macros #define fortement typées . Sinon, nous pouvons effrayer toutes les personnes C ou C ++. :-)
Jason Baker

4

CONST

  1. Le mot clé const peut être appliqué aux champs ou aux variables locales
  2. Nous devons attribuer un champ const au moment de la déclaration
  3. Aucune mémoire allouée car la valeur const est incorporée dans le code IL lui-même après la compilation. C'est comme trouver toutes les occurrences de la variable const et remplacer par sa valeur. Ainsi, le code IL après la compilation aura des valeurs codées en dur à la place des variables const
  4. Les const en C # sont par défaut statiques.
  5. La valeur est constante pour tous les objets
  6. Il existe un problème de version de DLL - Cela signifie que chaque fois que nous modifions une variable ou une propriété const publique ((en fait, elle n'est pas censée être modifiée théoriquement)), toute autre DLL ou assemblage qui utilise cette variable doit être reconstruite.
  7. Seuls les types intégrés C # peuvent être déclarés constants
  8. Le champ const ne peut pas être transmis comme paramètre ref ou out

Lecture seulement

  1. le mot clé en lecture seule s'applique uniquement aux champs et non aux variables locales
  2. Nous pouvons attribuer un champ en lecture seule au moment de la déclaration ou dans le constructeur, pas dans d'autres méthodes.
  3. mémoire dynamique allouée aux champs en lecture seule et nous pouvons obtenir la valeur au moment de l'exécution.
  4. En lecture seule appartient à l'objet créé, donc accessible via une seule instance de classe. Pour le rendre membre de la classe, nous devons ajouter un mot clé statique avant la lecture seule.
  5. La valeur peut être différente selon le constructeur utilisé (car elle appartient à l'objet de la classe)
  6. Si vous déclarez un type non primitif (type référence) en lecture seule, seule la référence est immuable et non l'objet qu'il contient.
  7. Étant donné que la valeur est obtenue au moment de l'exécution, il n'y a pas de problème de version de DLL avec les champs / propriétés en lecture seule.
  8. Nous pouvons passer le champ en lecture seule en tant que paramètres ref ou out dans le contexte du constructeur.

3

Un autre gotcha .

Puisque const ne fonctionne vraiment qu'avec les types de données de base, si vous voulez travailler avec une classe, vous pouvez vous sentir "obligé" d'utiliser ReadOnly. Attention cependant au piège! ReadOnly signifie que vous ne pouvez pas remplacer l'objet par un autre objet (vous ne pouvez pas le faire référence à un autre objet). Mais tout processus qui a une référence à l'objet est libre de modifier les valeurs à l' intérieur de l'objet!

Ne soyez donc pas confus en pensant que ReadOnly implique qu'un utilisateur ne peut pas changer les choses. Il n'y a pas de syntaxe simple en C # pour empêcher une instanciation d'une classe de voir ses valeurs internes modifiées (pour autant que je sache).


Oui, c'est plus un thème général. Si vous disposez d'une propriété get only exposant une liste, vous pouvez toujours modifier la liste. Vous ne pouvez pas définir une liste d'array différente pour cette propriété, mais vous ne pouvez pas empêcher l'utilisateur de modifier la liste d'array.
Gishu

3

A constdoit être codé en dur , où as readonlypeut être défini dans le constructeur de la classe.


3

Il existe une différence notable entre les champs const et readonly dans C # .Net

const est par défaut statique et doit être initialisé avec une valeur constante, qui ne peut pas être modifiée ultérieurement. Le changement de valeur n'est pas non plus autorisé dans les constructeurs. Il ne peut pas être utilisé avec tous les types de données. Par exemple DateTime. Il ne peut pas être utilisé avec le type de données DateTime.

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public readonly string Name = string.Empty; //No error, legal

readonly peut être déclaré comme statique, mais pas nécessaire. Pas besoin d'initialiser au moment de la déclaration. Sa valeur peut être affectée ou modifiée à l'aide du constructeur. Ainsi, il donne un avantage lorsqu'il est utilisé comme membre de classe d'instance. Deux instanciations différentes peuvent avoir une valeur différente de champ en lecture seule. Par exemple -

class A
{
    public readonly int Id;

    public A(int i)
    {
        Id = i;
    }
}

Ensuite, le champ en lecture seule peut être initialisé avec des valeurs spécifiques instantanées, comme suit:

A objOne = new A(5);
A objTwo = new A(10);

Ici, instance objOne aura la valeur du champ en lecture seule comme 5 et objTwo en a 10. Ce qui n'est pas possible en utilisant const.


2

Une constante sera compilée dans le consommateur en tant que valeur littérale tandis que la chaîne statique servira de référence à la valeur définie.

En tant qu'exercice, essayez de créer une bibliothèque externe et de la consommer dans une application console, puis modifiez les valeurs dans la bibliothèque et recompilez-la (sans recompiler le programme consommateur), déposez la DLL dans le répertoire et exécutez EXE manuellement, vous devriez trouver que la chaîne constante ne change pas.


Je doute sincèrement que ce soit vrai ... Je vais vérifier.
ljs

c'est l'une des 50 façons spécifiques d'améliorer votre C # - amazon.co.uk/Effective-Specific-Ways-Improve-Your/dp/0321245660/…
Russ Cam


@Andrew Hare - oui, je viens de vérifier. Je suis très surpris, c'est un vrai piège, je suis vraiment très surpris par ça, étonné que ce soit le cas ...!
ljs

Je m'oppose toutefois à l'utilisation du mot pointeur ici. Ce n'est pas un pointeur, c'est une référence, et il y a une différence en C # car vous pouvez manipuler des pointeurs non gérés en mode non sécurisé, il est donc important de faire la distinction entre les deux.
ljs

2

Constant

Nous devons fournir la valeur au champ const lorsqu'il est défini. Le compilateur enregistre ensuite la valeur de la constante dans les métadonnées de l'assembly. Cela signifie qu'une constante ne peut être définie que pour le type primitif comme booléen, char, octet, etc. Les constantes sont toujours considérées comme des membres statiques et non comme des membres d'instance.

Lecture seulement

Les champs en lecture seule ne peuvent être résolus qu'au moment de l'exécution. Cela signifie que nous pouvons définir une valeur pour une valeur en utilisant le constructeur pour le type dans lequel le champ est déclaré. La vérification est effectuée par le compilateur que les champs en lecture seule ne sont pas écrits par une méthode autre que le constructeur.

Plus sur les deux expliqués ici dans cet article


1

Principalement; vous pouvez attribuer une valeur à un champ en lecture seule statique à une valeur non constante au moment de l'exécution, tandis qu'un const doit recevoir une valeur constante.


1

Const et readonly sont similaires, mais ils ne sont pas exactement les mêmes. Un champ const est une constante au moment de la compilation, ce qui signifie que cette valeur peut être calculée au moment de la compilation. Un champ en lecture seule permet des scénarios supplémentaires dans lesquels du code doit être exécuté lors de la construction du type. Après la construction, un champ en lecture seule ne peut pas être modifié.

Par exemple, les membres const peuvent être utilisés pour définir des membres comme:

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

puisque les valeurs comme 3.14 et 0 sont des constantes de compilation. Cependant, considérez le cas où vous définissez un type et souhaitez en fournir des instances préfabriquées. Par exemple, vous souhaiterez peut-être définir une classe Color et fournir des "constantes" pour les couleurs courantes comme le noir, le blanc, etc. On pourrait le faire avec des membres statiques réguliers:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

mais alors il n'y a rien pour empêcher un client de Color de s'en occuper, peut-être en échangeant les valeurs Noir et Blanc. Inutile de dire que cela provoquerait la consternation pour les autres clients de la classe Color. La fonctionnalité "en lecture seule" résout ce scénario. En introduisant simplement le mot-clé en lecture seule dans les déclarations, nous préservons l'initialisation flexible tout en empêchant le code client de contourner.

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

Il est intéressant de noter que les membres const sont toujours statiques, alors qu'un membre en lecture seule peut être statique ou non, tout comme un champ normal.

Il est possible d'utiliser un seul mot-clé à ces deux fins, mais cela entraîne des problèmes de version ou des problèmes de performances. Supposons un instant que nous ayons utilisé un seul mot-clé pour cela (const) et un développeur a écrit:

public class A
{
    public static const C = 0;
}

et un développeur différent a écrit du code qui s'appuyait sur A:

public class B
{
    static void Main() {
        Console.WriteLine(A.C);
    }
}

Maintenant, le code généré peut-il s'appuyer sur le fait que AC est une constante de temps de compilation? C'est-à-dire, l'utilisation de AC peut-elle simplement être remplacée par la valeur 0? Si vous dites «oui» à cela, cela signifie que le développeur de A ne peut pas changer la façon dont AC est initialisé - cela lie les mains du développeur de A sans autorisation. Si vous dites «non» à cette question, une optimisation importante est manquée. L'auteur de A est peut-être certain que AC sera toujours nul. L'utilisation de const et readonly permet au développeur de A de spécifier l'intention. Cela permet un meilleur comportement de version et également de meilleures performances.


1

ReadOnly: La valeur ne sera initialisée qu'une seule fois à partir du constructeur de la classe.
const: peut être initialisé dans n'importe quelle fonction mais une seule fois


1

La différence est que la valeur d'un champ en lecture seule statique est définie au moment de l'exécution, de sorte qu'il peut avoir une valeur différente pour différentes exécutions du programme. Cependant, la valeur d'un champ const est définie sur une constante de temps de compilation.

N'oubliez pas: pour les types de référence, dans les deux cas (statique et instance), le modificateur en lecture seule vous empêche uniquement d'attribuer une nouvelle référence au champ. Elle ne rend pas spécifiquement immuable l'objet visé par la référence.

Pour plus de détails, veuillez consulter la foire aux questions C # sur ce sujet: http://blogs.msdn.com/csharpfaq/archive/2004/12/03/274791.aspx


1

Les variables constantes sont déclarées et initialisées au moment de la compilation. La valeur ne peut pas être modifiée après les quartiers. Les variables en lecture seule seront initialisées uniquement à partir du constructeur statique de la classe. La lecture seule est utilisée uniquement lorsque nous voulons affecter la valeur au moment de l'exécution.


1

Const : valeur constante absolue pendant la durée de vie de l'application.

Lecture seule : il peut être modifié en temps d'exécution.


1
Votre définition de "Readonly" qu'il peut changer est erronée. Je suppose que par «changer», vous vouliez dire «définir», comme «il peut être défini au moment de l'exécution».
Ahmed

0

Une chose à ajouter à ce que les gens ont dit ci-dessus. Si vous avez un assembly contenant une valeur en lecture seule (par exemple, readonly MaxFooCount = 4;), vous pouvez modifier la valeur que les assemblys appelants voient en expédiant une nouvelle version de cet assembly avec une valeur différente (par exemple readonly MaxFooCount = 5;)

Mais avec un const, il serait intégré au code de l'appelant lors de la compilation de l'appelant.

Si vous avez atteint ce niveau de compétence C #, vous êtes prêt pour le livre de Bill Wagner, Effective C #: 50 façons spécifiques d'améliorer votre C #, qui répond à cette question en détail (et 49 autres choses).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.