На самом деле, я не могу получить его до fail - по крайней мере, при генерации в памяти.
Начнем с поля public readonly
(поэтому мы не нарушаем никаких правил доступности); моя первая попытка, как показано ниже, и она отлично работает:
using System;
using System.Reflection;
using System.Reflection.Emit;
class Foo
{
public readonly int i;
public int I { get { return i; } }
public Foo(int i) { this.i = i; }
}
static class Program
{
static void Main()
{
var setter = CreateWriteAnyInt32Field(typeof(Foo), "i");
var foo = new Foo(123);
setter(foo, 42);
Console.WriteLine(foo.I); // 42;
}
static Action<object, int> CreateWriteAnyInt32Field(Type type, string fieldName)
{
var field = type.GetField(fieldName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var method = new DynamicMethod("evil", null,
new[] { typeof(object), typeof(int) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, field);
il.Emit(OpCodes.Ret);
return (Action<object, int>)method.CreateDelegate(typeof(Action<object, int>));
}
}
Единственный раз, когда это становится интересным, если поле private
:
private readonly int i;
код выше, то дает о так расплывчато :
Операция может дестабилизировать время выполнения.
Но мы получаем вокруг этого, делая вид, что метод находится внутри типа объявляющего месторождения:
var method = new DynamicMethod("evil", null,
new[] { typeof(object), typeof(int) }, field.DeclaringType);
некоторых других внутренних проверок может быть сделано путем предоставления skipVisibility
:
var method = new DynamicMethod("evil", null,
new[] { typeof(object), typeof(int) }, field.DeclaringType, true);
Однако, обратите внимание, что не все это возможно при создании автономных сборок. При создании фактических библиотек вы придерживаетесь гораздо более высоких стандартов. По этой причине инструмент precompiler
(для предварительной генерации сборок) не может обрабатывать тот же диапазон сценариев, что и код метапрограммирования в памяти.
Я проверю его сразу. Возможно, моя проблема не была строго связана с полями readonly. Самое забавное, что мой код работает на Mono и не работает на .NET. –
Дох. Мне не удалось предоставить DeclaringType. Но теперь мне интересно - почему это работает? Должно ли поле readonly записываться только из конструктора объекта? Спасибо за ответ, так или иначе. –
@PiotrZierhoffer многие вещи применяются только в компиляторе и в полном объеме «PEVerify». В конечном счете, вы можете изменять поля 'readonly' через отражение, а сериализаторы/материализаторы часто ** полностью пропускают ** конструктор, поэтому, если бы это было так, то было бы невозможно назначить значение –