设置只读字段时的VerificationException(C#)

时间:2017-12-02 02:31:29

标签: c# reflection setter anonymous-types verificationexception

我正在尝试编写可以在调用者提供的对象上设置任意字段的代码,这些字段可能包含匿名字段。创建委托是不可能的(表达式编译器意识到匿名对象'字段是只读的),所以我选择发出一些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 个答案:

答案 0 :(得分:0)

您所看到的绝对符合规格。看看ECMA-335 Section II.16.1.2

  

<强> initonly   标记在初始化后保持不变的字段。这些字段只能变异   在构造函数中。如果该字段是静态字段,则它应仅在类型初始值设定项内变异   它被宣布的类型。如果它是一个实例字段,那么它只能在其中一个字段中进行变异   定义它的类型的实例构造函数。它不得以任何其他方法或变异进行变异   在任何其他构造函数中,包括派生类的构造函数。

     

[   注意:    指某东西的用途   ldflda    要么   ldsflda    在...上   initonly    字段使代码无法验证。     在无法验证的代码中,   VES无需检查是否   initonly    字段在构造函数之外变异。 VES需要   如果方法更改常量的值,则不报告任何错误。但是,此类代码无效。   结束   注意   ]

要优雅地处理此案例,您可以使用FieldInfo.IsInitOnly Property