Могу ли я изменить частное поле только для чтения в C # с помощью отражения? - PullRequest
109 голосов
/ 01 июня 2009

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

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 ]

142 голосов
/ 01 июня 2009

Вы можете:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);
52 голосов
/ 01 июня 2009

Очевидная вещь состоит в том, чтобы попробовать это:

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 как доступный, и он все равно будет работать только для полей экземпляра.)

12 голосов
/ 26 февраля 2013

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

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

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

4 голосов
/ 28 января 2010

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

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

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

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

2 голосов
/ 22 февраля 2018

Другой простой способ сделать это с помощью небезопасного (или вы можете передать поле методу 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);
        }
    }
}
2 голосов
/ 12 мая 2014

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

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

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

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

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

Это было во время выполнения Mono (игровой движок Unity).

2 голосов
/ 01 июня 2009

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

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

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

0 голосов
/ 24 мая 2013

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

A) PrivateObject класс

B) Вам все еще понадобится экземпляр PrivateObject, но вы можете создавать объекты «Accessor» с помощью Visual Studio. Как: восстановить частные средства доступа

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

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