0

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

1 回答 1

0

您所看到的完全符合规格。看看ECMA-335 第 II.16.1.2 节

initonly 标记初始化后不变的字段。这些字段只能在构造函数内部发生变化。如果该字段是一个静态字段,那么它只能在声明它的类型的类型初始化器内进行变异。如果它是一个实例字段,那么它只能在定义它的类型的实例构造函数之一中发生变异。它不应在任何其他方法或任何其他构造函数中发生变异,包括派生类的构造函数。

[注意:在 initonly 字段上使用 ldflda 或 ldsflda 会使代码无法验证。在无法验证的代码中,VES 不需要检查 initonly 字段是否在构造函数之外发生了变异。如果一个方法改变了一个常量的值,VES 不需要报告任何错误。但是,这样的代码是无效的。尾注]

要优雅地处理这种情况,您可以使用FieldInfo.IsInitOnly 属性

于 2017-12-03T14:50:33.913 回答