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

Solution 1

You can:


Solution 2

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

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.)

Solution 3

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.

Solution 4

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.

Solution 5

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).

Solution 6

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);
            Console.WriteLine("Forced Value: " + program.i);

Solution 7

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.

Solution 8

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.