C# et immuabilité et champs de lecture ... un mensonge?

j'ai trouvé que les gens prétendent que l'utilisation de tous les champs readonly dans une classe ne rend pas nécessairement l'instance de cette classe immuable parce qu'il y a des" façons " de changer les valeurs des champs readonly même après l'initialisation (construction).

comment? De quelles façons?

donc ma question Est Quand pouvons-nous vraiment avoir un objet" réel " immuable en C#, que je peux utiliser en toute sécurité dans le threading?

aussi les types anonymes créent des objets immuables? Et certains disent que LINQ utilise des objets immuables en interne. Comment exactement?

44
demandé sur Sathya 2011-07-17 11:56:09

5 réponses

vous avez posé cinq questions. Je répondrai à la première:

avoir tous les champs en lecture seule dans une classe ne rend pas nécessairement l'instance de cette classe immuable parce qu'il y a des" façons " de changer les valeurs des champs en lecture seule même après la construction. Comment?

est-il possible de changer un seul champ après la construction?

Oui, si vous êtes suffisamment en confiance pour enfreindre les règles de lecture seule .

comment ça marche?

chaque morceau de mémoire de l'utilisateur dans votre processus est mutable. les Conventions comme readonly fields peuvent faire apparaître certains bits comme immuables, mais si vous essayez assez fort, vous pouvez les muter. Par exemple, vous pouvez prendre une instance d'objet immuable, obtenir son adresse, et changer les bits bruts directement. Cela nécessite beaucoup d'habileté et de connaissance des détails d'implémentation interne du gestionnaire de mémoire, mais d'une certaine manière le gestionnaire de mémoire réussit à muter cette mémoire, donc vous pouvez aussi si vous essayez assez fort. Vous pouvez également utiliser la "réflexion privée" pour briser diverses parties du système de sécurité si vous avez suffisamment confiance.

par définition, le code de confiance complète est autorisé à enfreindre les règles du système de sécurité . Qu'est ce que "entièrement confiance". Si votre code de confiance choisit pour utiliser des outils comme la réflexion privée ou le code dangereux pour briser les règles de sécurité de la mémoire, le code entièrement fiable est autorisé à le faire.

Non. Le faire est dangereux et déroutant. La mémoire système de sécurité est conçu pour rendre plus facile de raisonner sur l'exactitude de votre code; délibérément violer ses règles est une mauvaise idée.

alors, est-ce que "readonly" est un mensonge? Et bien, supposons que je vous ai dit que si tout le monde obéit aux règles , tout le monde reçoit une tranche de gâteau. Le gâteau est un mensonge? Cette revendication est pas la demande ", vous obtiendrez une tranche de gâteau". C'est l'affirmation que si tout le monde obéit aux règles , vous obtiendrez une part de gâteau. Si quelqu'un trompe et prend ta part, pas de gâteau pour toi.

est un champ en lecture seule d'une classe en lecture seule? Oui mais seulement si tout le monde obéit aux règles . Ainsi, seuls les champs ne sont pas"un mensonge". Le contrat est, si tout le monde obéit aux règles du système, alors le champ est observé en lecture seule. Si quelqu'un enfreint les règles, ce n'est peut-être pas le cas. Cela ne fait pas la déclaration "si tout le monde obéit aux règles, le champ est readonly" un mensonge!

Une question que vous n'avez pas de questions, mais peut-être devrait avoir est de savoir si "lecture seule" sur les champs d'une structure est un "mensonge". Voir l'utilisation des champs publics readonly pour les structures immuables fonctionne-t-elle? pour quelques réflexions sur ce question. Les champs Readonly sur une structure sont beaucoup plus un mensonge que les champs readonly sur une classe.

quant au reste de vos questions -- je pense que vous obtiendrez de meilleurs résultats si vous posez une question par question, plutôt que cinq questions par question.

104
répondu Eric Lippert 2017-05-23 11:54:34

EDIT: je me suis concentré sur le code travaillant "à l'intérieur" du système par opposition à l'utilisation de la réflexion etc comme Eric le mentionne.

cela dépend du type de champ. Si le champ lui-même est d'un type immuable (par exemple String ) alors c'est très bien. Si c'est StringBuilder alors l'objet peut apparaître pour changer même si les champs eux-mêmes ne changent pas leurs valeurs, parce que les objets" embedded " peuvent changer.

Les types anonymes sont exactement les mêmes. Par exemple:

var foo = new { Bar = new StringBuilder() };
Console.WriteLine(foo); // { Bar = }
foo.Bar.Append("Hello");
Console.WriteLine(foo); // { Bar = Hello }

donc, fondamentalement si vous avez un type que vous voulez être correctement immuable, vous devez vous assurer qu'il se réfère uniquement à des données immuables.

il y a aussi la question des structures qui peuvent avoir des champs lisibles mais qui exposent encore des méthodes qui se mutent en réassignant this . De telles structures se comportent un peu étrangement selon la situation exacte dans laquelle vous les appelez. Pas sympa - ne pas le faire.

Eric Lippert a écrit un beaucoup de choses sur l'immuabilité - c'est tout en or, comme vous pouvez vous y attendre... allez lire :) (Je n'avais pas remarqué Qu'Eric écrivait une réponse à cette question lorsque j'ai écrit ce passage. Évidemment lire sa réponse aussi!)

14
répondu Jon Skeet 2011-07-17 08:05:18

je pense que la réponse D'Eric dépasse de loin la portée de la question initiale, au point de ne même pas y répondre, alors je vais tenter ma chance:

quoi est seulement? Eh bien, si nous parlons de types de valeurs, c'est simple: Une fois que le type de valeur est initialisé et qu'on lui donne une valeur, il ne peut jamais changer (du moins en ce qui concerne le compilateur).

la confusion commence à s'installer lorsque nous parlons d'utiliser readonly avec les types de référence. C'est à ce stade que nous devons distinguer les deux composantes d'un type de référence:

  • la "référence" (variable, pointeur) qui pointe vers l'adresse en mémoire où vit votre objet
  • la mémoire qui contient les données que la référence pointe à

une référence à un objet est elle-même un type de valeur. lorsque vous utilisez readonly avec un type de référence, vous faites référence à votre objet immuable, ne forçant pas l'immutabilité sur la mémoire dans laquelle l'objet vit.

considérez maintenant un objet qui contient des types de valeurs et des références à d'autres objets, et ces objets contiennent des types de valeurs et des références à d'autres objets. Si vous composez vos objets de manière à ce que tous les champs de tous les objets soient lisibles, vous pouvez, en essence, atteindre l'immuabilité que vous désirez.

13
répondu w.brian 2011-07-21 00:26:24

Readonly et Thread Safety sont deux notions différentes comme Eric Lippert l'explique.

8
répondu Darin Dimitrov 2011-07-17 08:13:22

peut-être que le "peuple" avait entendu de la 3e main et avait mal communiqué au sujet du privilège spécial que le constructeur obtient en ce qui concerne les domaines "en lecture seule". Ils ne sont pas en lecture seule.

non seulement ça, mais ce n'est pas une seule affectation. Le constructeur d'une classe peut modifier le champ autant de fois qu'il le souhaite. Tout cela sans enfreindre aucune"règle".

Les constructeurs

peuvent aussi appeler d'autres méthodes arbitraires, se passant eux-mêmes comme paramètres. Donc, si vous êtes cette autre méthode, vous ne pouvez pas être certain si ce champ est encore immuable, parce que peut-être vous avez été invoqué par le constructeur de cet objet, et le constructeur va modifier le champ dès que vous avez terminé.

Vous pouvez essayer vous-même:

using System;
class App
{
    class Foo 
    {
        readonly int x;
        public Foo() {  x = 1; Frob(); x = 2; Frob(); }
        void Frob() { Console.WriteLine(x); }
    }
    static void Main()
    {
        new Foo();
    }
}

ce programme imprime 1, 2. 'x' est modifié après que Frob l'ait lu.

maintenant, dans n'importe quel corps de méthode simple, la valeur doit être constante -- le constructeur ne peut pas delegate modify-accès à d'autres méthodes ou délégués, donc jusqu'à ce qu'une méthode revienne, Je suis presque sûr que le champ devra rester stable.

Tout ce qui précède concerne les classes. Les structures sont une toute autre histoire.

0
répondu Aaron 2011-07-22 05:48:01