Могу ли я изменить частное поле readonly в C#, используя отражение?

Мне интересно, так как многое можно сделать с помощью отражения, могу ли я изменить частное поле только для чтения после завершения конструктора?
(Примечание: просто любопытство)

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

8 ответов


вы можете:

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

очевидная вещь-попробовать:

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

это прекрасно работает. (Java имеет разные правила, интересно-вы должны явно установить Field быть доступным, и он будет работать только для полей экземпляра в любом случае.)


Я согласен с другими ответами в том, что он работает вообще и особенно с комментарием Э. Липперта, что это не документированное поведение и, следовательно, не будущий код.

однако, мы также заметили еще одну проблему. Если вы используете свой код в среде с ограниченными правами, вы можете получить исключение.

у нас только что был случай, когда наш код отлично работал на наших машинах, но мы получили VerificationException когда код выполнялся в ограниченная среда. Преступник был рефлекторным призывом к сеттеру поля только для чтения. Это сработало, когда мы удалили ограничение readonly этого поля.


вы спросили, почему вы хотите сломать инкапсуляцию таким образом.

Я использую класс Entity helper для гидратации сущностей. Это использует отражение, чтобы получить все свойства новой пустой сущности, и соответствует имени свойства/поля столбцу в resultset, и установить его с помощью propertyinfo.метод setValue().

Я не хочу, чтобы кто-то еще мог изменить значение, но я не хочу прилагать все усилия для пользовательских методов гидратации кода для каждого объекта любой.

многие из моих сохраненных процессоров возвращают результирующие наборы, которые не соответствуют непосредственно таблицам или представлениям, поэтому код gen ORM ничего не делает для меня.


ответ-да, но что еще более важно:

Почему вы хотите? Намеренное нарушение инкапсуляции кажется мне ужасающе плохой идеей.

использование отражения для изменения только для чтения или постоянного поля похоже на объединение закон непредвиденных последствий С закон Мерфи.


Не делай этого.

Я только что провел день, исправляя сюрреалистическую ошибку, где объекты могут быть не их собственного объявленного типа.

изменение поля readonly работало один раз. Но если вы попытаетесь изменить его снова, вы получите такие ситуации:

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

Так что не делайте этого.

Это было в Mono runtime (игровой движок Unity).


еще один простой способ сделать это с помощью unsafe (или вы можете передать поле методу C через DLLImport и установить его там).

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

Я просто хочу добавить, что если вам нужно сделать эти вещи для модульного тестирования, вы можете использовать:

A)PrivateObject класс

B) вам все равно понадобится экземпляр PrivateObject, но вы можете создавать объекты "Accessor" с помощью Visual Studio. как: регенерация частных аксессоров

Если вы устанавливаете частные поля объекта в своем коде вне модульного тестирования, это будет экземпляр "запаха кода", я думаю, что возможно, единственная другая причина, по которой вы хотите сделать это, - это иметь дело с библиотекой сторонних разработчиков, и вы не можете изменить код целевого класса. Даже тогда вы, вероятно, захотите связаться с третьей стороной, объяснить свою ситуацию и посмотреть, не изменят ли они свой код, чтобы удовлетворить ваши потребности.