128

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
Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Ron Klein
  • 9,178
  • 9
  • 55
  • 88

9 Answers9

167

You can:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);
Philippe Leybaert
  • 168,566
  • 31
  • 210
  • 223
  • 2
    You are absolutely right, of course. My apologies. And yes, I did try, but I attempted to set a readonly property directly, not using a backing field. What I attempted made no sense. Your solution works perfectly fine (tested again, correctly this time) – Sage Pourpre Apr 17 '15 at 02:57
  • how can we do this with moq? – Alex Gordon Mar 06 '16 at 12:35
  • 2
    In dotnet core 3.0 this is no longer possible. A System.FieldAccessException is thrown saying: "Cannot set initonly static field 'bar' after type 'Foo' is initialized." – David Perfors Nov 29 '19 at 11:03
  • Works perfectly fine in .NET 5 – Travis Jan 15 '21 at 23:03
  • 2
    Doesn't work in .NET 5 and since .NET Core 3.0 there is a documented change for this behavior. "This method cannot be used to set values of static, `init-only` (`readonly` in C#) fields reliably. In .NET Core 3.0 and later versions, an exception is thrown if you attempt to set a value on a static, `init-only` field." – ai_enabled Jan 21 '21 at 15:26
56

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

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    I think this breaks the language specifications, is not it? – Ahmed Jun 01 '09 at 14:05
  • 4
    Ahmed - but we're not using the language to do it, so the language spec doesn't get a vote... – Marc Gravell Jun 01 '09 at 14:06
  • 4
    Yup - there's a lot that can be done which "breaks" what the language wants. You can run type initializers multiple times, for example. – Jon Skeet Jun 01 '09 at 14:12
  • 31
    I note also that just because you can in some implementation today does not mean that you can on every implementation for all time. I am not aware of any place where we document that readonly fields must be mutable via reflection. As far as I know, a conforming implementation of the CLI is perfectly free to implement readonly fields such that they throw exceptions when mutated via reflection after the constructor is done. – Eric Lippert Jun 01 '09 at 15:39
  • 3
    That's not good though, because there are cases when I need to extend a class more than it was originally designed to be. There should always be a way to override encapsulation when it is well-planned. The only alternatives are gory and sometimes not possible without rewriting part of the framework. – drifter Dec 29 '10 at 10:53
  • 5
    @drifter: At that point, you are opening yourself up to a world of pain. You're relying on current implementation details which can easily change in a future version. – Jon Skeet Dec 29 '10 at 10:59
  • @Jon : How do I change the value if the field is a static field? – unj2 Jan 05 '12 at 18:04
  • 2
    @kunj2aan: You pass in null as the "target". See the docs for `FieldInfo.SetValue`. – Jon Skeet Jan 05 '12 at 18:31
  • 2
    @EricLippert: That's the behavior I would have expected. – Daniel Hilgarth Aug 17 '12 at 12:03
  • sometimes it is useful if you want to set some stuff for unit testing – Louis Rhys Feb 26 '13 at 02:00
  • 2
    @LouisRhys: That *usually* suggests that you should take a step back and try to work out a different design, in my experience. – Jon Skeet Feb 26 '13 at 07:12
  • @JonSkeet I see. any suggestion how to be able to set/assert a private value without exposing it as internal? – Louis Rhys Feb 26 '13 at 07:35
  • 1
    @LouisRhys: Usually you'd work out what visible effect that value has. If it doesn't have any, it's pointless. If it does have one, then you test that :) I find myself promoting *methods* to be `internal` for the sake of simpler testing more than variables/properties. – Jon Skeet Feb 26 '13 at 07:36
11

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.

Andreas
  • 6,447
  • 2
  • 34
  • 46
5

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.

Necroposter
  • 51
  • 1
  • 1
  • 2
    I've also used it to get past some api limitations where the value was either hardcoded, or required a config file I would be unable to provide. (WSE 2.0 file size for DIME attachments when the assemblies were loaded via reflection, for example) – StingyJack Jan 27 '10 at 21:26
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).

  • 4
    FYI - The Unity Engine can't be used to effectively answer deep C# language-specific questions such as this because Unity performs it's own compilation of C# as if the .cs were scripts, in a sense. I'm not saying your point isn't valid, but it's certainly specific to the Unity Engine & C# together. – Dave Jellison Jun 03 '16 at 17:26
3

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);
        }
    }
}
zezba9000
  • 3,247
  • 1
  • 29
  • 51
2

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.

Powerlord
  • 87,612
  • 17
  • 125
  • 175
  • 1
    the answer is "just curiosity", as mentioned above. – Ron Klein Jun 01 '09 at 15:59
  • There are times when I find myself having to do just this trick in order to write the best code I can. Case in point - http://elegantcode.com/2008/04/17/testing-a-membership-provider/ – sparker Apr 12 '10 at 18:09
  • 3
    I too use this trick in unit testing projects to override default values which should not be changed in any business code... – Koen Dec 06 '10 at 10:53
  • And I was attempting to set private internal properties in the base class libraries for testing purposes, specifically the Membership API, where MS marked everything private, internal and with no setters on properties. There are cases for this, but you are correct if the question applied to an API under your control – Chad Grant Jan 28 '12 at 17:51
  • 3
    There are situations where it makes sense. O/R mappers like NHibernate do it all the time for hydration, because this is the only way you can implement data encapsulation for persistent entities in the first place. – chris Feb 21 '12 at 18:22
  • This is useful to break some license protection. – Dima Feb 07 '23 at 19:25
0

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.

Dudeman3000
  • 551
  • 8
  • 21
0

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.