W rzeczywistości nie mogę uzyskać tego do nieudanego - przynajmniej podczas generowania w pamięci.
Zacznijmy od prostego, z polem public readonly
(więc nie łamiemy żadnych zasad dostępu); Moja pierwsza próba jest jak poniżej, i to działa dobrze:
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>));
}
}
Tylko raz robi się ciekawie jest, jeśli pole jest private
:
private readonly int i;
Powyższy kod następnie daje jakże niejasne :
Operacja może zdestabilizować środowisko wykonawcze.
Ale obejść, że udając, że metoda jest wewnątrz pola typ uznającej:
var method = new DynamicMethod("evil", null,
new[] { typeof(object), typeof(int) }, field.DeclaringType);
niektórych innych kontroli wewnętrznych można zrobić poprzez umożliwienie skipVisibility
:
var method = new DynamicMethod("evil", null,
new[] { typeof(object), typeof(int) }, field.DeclaringType, true);
jednak zauważ, że nie wszystko to jest możliwe w przypadku generowania samodzielnych złożeń. Podczas tworzenia rzeczywistych bibliotek dll są utrzymywane znacznie wyższe standardy. Z tego powodu narzędzie precompiler
(do wstępnego generowania złożeń) nie może obsłużyć dość tego samego zakresu scenariuszy, co kod metaprogramowania w pamięci.
Sprawdzę to od razu. Może mój problem nie był ściśle związany z polami tylko do odczytu. Zabawne jest to, że mój kod działa na Mono i nie działa na platformie .NET. –
Doh. Nie udało mi się podać DeclaringType. Ale teraz zastanawiam się - dlaczego to działa? Czy nie powinno się zapisywać pola tylko do odczytu z konstruktora obiektu? Dzięki za odpowiedź. –
@PiotrZierhoffer wiele rzeczy jest egzekwowanych tylko w kompilatorze iw pełnym 'PEVerify'. Ostatecznie, możesz zmienić pola 'readonly' przez odbicie, a serializatory/materializatory często ** całkowicie pomijają ** konstruktora, więc gdyby tak było, nie byłoby możliwości przypisania wartości –