web-dev-qa-db-fra.com

Puis-je modifier un champ privé en lecture seule en C # en utilisant la réflexion?

Je me demande, puisque beaucoup de choses peuvent être faites en utilisant la réflexion, puis-je changer un champ privé en lecture seule après que le constructeur a terminé son exécution?
(note: juste de la curiosité)

public class Foo
{
 private readonly int bar;

 public Foo(int num)
 {
  bar = num;
 }

 public int GetBar()
 {
  return bar;
 }
}

Foo foo = new Foo(123);
Console.WriteLine(foo.GetBar()); // display 123
// reflection code here...
Console.WriteLine(foo.GetBar()); // display 456
110
Ron Klein

Vous pouvez:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);
142

L'essentiel est de l'essayer:

using System;
using System.Reflection;

public class Test
{
    private readonly string foo = "Foo";

    public static void Main()
    {
        Test test = new Test();
        FieldInfo field = typeof(Test).GetField
            ("foo", BindingFlags.Instance | BindingFlags.NonPublic);
        field.SetValue(test, "Hello");
        Console.WriteLine(test.foo);
    }        
}

Cela fonctionne bien. (Java a des règles différentes, ce qui est intéressant - vous devez définir explicitement le Field pour qu'il soit accessible, et cela ne fonctionnera que pour les champs d'instance de toute façon.)

52
Jon Skeet

Je suis d'accord avec les autres réponses en ce sens que cela fonctionne généralement et surtout avec le commentaire d'E. Lippert selon lequel ce n'est pas un comportement documenté et donc pas un code à l'épreuve du temps.

Cependant, nous avons également remarqué un autre problème. Si vous exécutez votre code dans un environnement avec des autorisations restreintes, vous pourriez obtenir une exception.

Nous venons de voir un cas où notre code fonctionnait bien sur nos machines, mais nous avons reçu un VerificationException lorsque le code s'exécutait dans un environnement restreint. Le coupable était un appel de réflexion au poseur d'un champ en lecture seule. Cela a fonctionné lorsque nous avons supprimé la restriction en lecture seule de ce champ.

12
Andreas

Vous avez demandé pourquoi vous souhaitiez rompre l'encapsulation comme ça.

J'utilise une classe d'assistance d'entité pour hydrater les entités. Cela utilise la réflexion pour obtenir toutes les propriétés d'une nouvelle entité vide, et fait correspondre le nom de la propriété/du champ à la colonne dans l'ensemble de résultats, et la définit à l'aide de propertyinfo.setvalue ().

Je ne veux pas que quelqu'un d'autre puisse changer la valeur, mais je ne veux pas non plus prendre tous les efforts pour personnaliser les méthodes d'hydratation du code pour chaque entité.

Mes nombreux procs stockés renvoient des ensembles de résultats qui ne correspondent pas directement aux tables ou aux vues, donc les ORM de génération de code ne font rien pour moi.

4
Necroposter

La réponse est oui, mais plus important encore:

Pourquoi voudriez-vous? Briser l'encapsulation intentionnellement me semble une horriblement mauvaise idée.

Utiliser la réflexion pour changer un champ en lecture seule ou constant, c'est comme combiner la loi des conséquences involontaires avec loi de Murphy .

2
Powerlord

Ne fais pas ça.

Je viens de passer une journée à corriger un bug surréaliste où les objets pouvaient ne pas être de leur propre type déclaré.

La modification du champ en lecture seule a fonctionné une fois. Mais si vous essayez à nouveau de le modifier, vous obtiendrez des situations comme celle-ci:

SoundDef mySound = Reflection_Modified_Readonly_SoundDef_Field;
if( !(mySound is SoundDef) )
    Log("Welcome to impossible-land!"); //This would run

Alors ne le fais pas.

C'était sur le runtime Mono (moteur de jeu Unity).

2
Tynan Sylvester

Une autre façon simple de le faire en utilisant dangereux (ou vous pouvez passer le champ à une méthode C via DLLImport et le définir là).

using System;

namespace TestReadOnly
{
    class Program
    {
        private readonly int i;

        public Program()
        {
            i = 66;
        }

        private unsafe void ForceSet()
        {
            fixed (int* ptr = &i) *ptr = 123;
        }

        static void Main(string[] args)
        {
            var program = new Program();
            Console.WriteLine("Contructed Value: " + program.i);
            program.ForceSet();
            Console.WriteLine("Forced Value: " + program.i);
        }
    }
}
2
zezba9000

Je veux juste ajouter que si vous avez besoin de faire ces choses pour les tests unitaires, vous pouvez utiliser:

A) La classe PrivateObject

B) Vous aurez toujours besoin d'une instance PrivateObject, mais vous pouvez générer des objets "Accessor" avec Visual Studio. Comment: régénérer des accesseurs privés

Si vous définissez des champs privés d'un objet dans votre code en dehors des tests unitaires, ce serait une instance d '"odeur de code" Je pense que la seule autre raison pour laquelle vous voudriez le faire est si vous traitez avec un tiers bibliothèque et vous ne pouvez pas modifier le code de la classe cible. Même dans ce cas, vous voudrez probablement contacter le tiers, expliquer votre situation et voir s'ils ne vont pas aller de l'avant et changer leur code pour répondre à votre besoin.

0
Dudeman3000