Ron Klein
Ron Klein

Reputation: 9440

Can I change a private readonly field in C# using reflection?

I am wondering, since a lot of things can be done using reflection, can I change a private readonly field after the constructor completed its execution?
(note: just curiosity)

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

Upvotes: 134

Views: 66048

Answers (9)

Oleksii Zhyglov
Oleksii Zhyglov

Reputation: 133

You can change a private read-only field in C# using inheritance. Create a child, and in it's constractor write: base.PrivateField = something or even base.AnotherPrivatePropertyWhereFirstPrivatePropertyTakesItsValue. And then just yous your new inherited class instead of original one.

Upvotes: 0

zezba9000
zezba9000

Reputation: 3383

Another simple way to do this using unsafe (or you could pass the field to a C method via DLLImport and set it there).

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);
        }
    }
}

Upvotes: 3

Tynan Sylvester
Tynan Sylvester

Reputation: 81

Don't do this.

I just spent a day fixing a surreal bug where objects could be not of their own declared type.

Modifying the readonly field worked once. But if you tried to modify it again, you'd get situations like this:

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

So don't do it.

This was on the Mono runtime (Unity game engine).

Upvotes: 5

Dudeman3000
Dudeman3000

Reputation: 601

I just want to add that if you need to do this stuff for unit testing, then you can use:

A) The PrivateObject class

B) You will still need a PrivateObject instance, but you can generate "Accessor" objects with Visual Studio. How to: Regenerate Private Accessors

If you are setting private fields of an object in your code outside of unit testing, that would be an instance of "code smell" I think that perhaps the only other reason you would want to do this is if you are dealing with a third party library and you can't change the target class code. Even then, you probably want to contact the 3rd party, explain your situation and see if they won't go ahead and change their code to accomodate your need.

Upvotes: 0

Andreas
Andreas

Reputation: 6465

I agree with the other answers in that it works generally and especially with the comment by E. Lippert that this is not documented behavior and therefore not future-proof code.

However, we also noticed another issue. If you're running your code in an environment with restricted permissions you might get an exception.

We've just had a case where our code worked fine on our machines, but we received a VerificationException when the code ran in a restricted environment. The culprit was a reflection call to the setter of a readonly field. It worked when we removed the readonly restriction of that field.

Upvotes: 11

Necroposter
Necroposter

Reputation: 51

You asked why you would want to break the encapsulation like that.

I use an entity helper class to hydrate entities. This uses reflection to get all the properties of a new empty entity, and matches the property/field name to the column in the resultset, and set's it using propertyinfo.setvalue().

I don't want anyone else to be able to change the value, but I don't want to take all the effort to custom code hydration methods for every entity either.

My many of my stored procs return resultsets that don't correspond directly to tables or views, so the code gen ORM's do nothing for me.

Upvotes: 5

Powerlord
Powerlord

Reputation: 88796

The answer is yes, but more importantly:

Why would you want to? Intentionally breaking encapsulation seems like a horrifically bad idea to me.

Using reflection to change a readonly or constant field is like combining the Law of Unintended Consequences with Murphy's Law.

Upvotes: 2

Philippe Leybaert
Philippe Leybaert

Reputation: 171744

You can:

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

Upvotes: 173

Jon Skeet
Jon Skeet

Reputation: 1500055

The obvious thing is to try it:

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);
    }        
}

This works fine. (Java has different rules, interestingly - you have to explicitly set the Field to be accessible, and it will only work for instance fields anyway.)

Upvotes: 57

Related Questions