VerificationException при установке полей только для чтения (C#)
Я пытаюсь написать код, который может устанавливать произвольные поля для объектов, предоставляемых вызывающей стороной, которые могут включать анонимные. Создание делегатов невозможно (компилятор выражений понимает, что поля анонимных объектов доступны только для чтения), поэтому я решил испустить некоторый IL. Однако при этом я сталкиваюсь с VerificationException ("Операция может дестабилизировать его во время выполнения"). Тот же простой код прекрасно работает с объектами с обычными полями. Сбой на полях только для чтения. Что еще можно сделать здесь? Я использую.Net 4.6.2.
Заранее спасибо!
class TestRegular
{
private string field;
}
class TestReadOnly
{
private readonly string field;
}
class Program
{
static void Main(string[] args)
{
Verify(new TestRegular()); // this works
Verify(new TestReadOnly()); // this does not work
Verify(new { field = "abc" }); // this does not work
Console.WriteLine("Done");
}
private static void Verify<T>(T test)
{
var fields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
Action <T, object> setter = CompileSetter<T>(fields[0]);
setter(test, "value");
}
private static Action<TResult, object> CompileSetter<TResult>(FieldInfo field)
{
string methodName = field.ReflectedType.FullName + ".TestSetter";
DynamicMethod setterMethod = new DynamicMethod(methodName, null, new[] { typeof(TResult), typeof(object) }, true);
ILGenerator gen = setterMethod.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Castclass, field.FieldType);
gen.Emit(OpCodes.Stfld, field);
gen.Emit(OpCodes.Ret);
return (Action<TResult, object>)setterMethod.CreateDelegate(typeof(Action<TResult, object>));
}
}
1 ответ
То, что вы видите, абсолютно соответствует спецификациям. Взгляните на ECMA-335 Раздел II.16.1.2:
initonly отмечает поля, которые являются постоянными после их инициализации. Эти поля должны быть изменены только внутри конструктора. Если поле является статическим полем, оно должно быть видоизменено только внутри инициализатора типа того типа, в котором оно было объявлено. Если это поле экземпляра, то оно должно быть видоизменено только в одном из конструкторов экземпляра того типа, в котором оно было определено. Он не должен быть видоизменен ни в каком другом методе или в любом другом конструкторе, включая конструкторы производных классов.
[Примечание: использование ldflda или ldsflda в поле initonly делает код непроверяемым. В непроверяемом коде VES не нужно проверять, видоизменены ли поля initonly вне конструкторов. VES не нужно сообщать о каких-либо ошибках, если метод изменяет значение константы. Однако такой код недействителен. конечная нота]
Для обработки этого случая вы можете использовать свойство FieldInfo.IsInitOnly.