Могу ли я изменить личное поле только для чтения в 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
722   8  

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

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

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

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

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

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

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

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

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

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

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

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

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

А) PrivateObject класс

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

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

Comments

    Ничего не найдено.