为什么使用动态方法设置只读字段会在该类中导致错误



可以使用动态方法中的strfld op码存储在类的只读字段中,如果它的所有者设置为该类并且JIT检查关闭。这里有一个例子。然而,这种方法无法处理来自f#的类,即FSharpOption。请分析下面的例子:

using Microsoft.FSharp.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
#if true
using MyType = Microsoft.FSharp.Core.FSharpOption<string>;
#else
using MyType = System.Tuple<string>;
#endif
namespace ConsoleApplication27
{
    class Program
    {
        static void Main(string[] args)
        {
            var something = new MyType("test");
            var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(void), new [] { typeof(MyType) }, typeof(MyType), true);
            var generator = dynMethod.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldstr, "success");
            generator.Emit(OpCodes.Stfld, typeof(MyType).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)[0]);
            generator.Emit(OpCodes.Ret);
            var method = (Action<MyType>) dynMethod.CreateDelegate(typeof(Action<MyType>));
            method(something);
            Console.WriteLine(typeof(MyType).GetProperties()[0].GetGetMethod().Invoke(something, new object[0]));
        }
    }
}

首先,你必须引用FSharp。Core库来运行它。然后,通过将#if true更改为#if false,您可以在为TupleFSharpOption写入只读字段之间切换。对于前者,即使两者具有相似的结构,也可以完美地工作,即通过属性访问单个只读字段。对于后者,它会导致验证失败。为什么会这样呢?

很晚才回复。这很奇怪。通过将MyType替换为字段类型的模块。一切都开始工作了

var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static,
            CallingConventions.Standard, typeof(void), new[] {typeof(MyType)}, typeof(MyType), true);

就变成:

var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static,
            CallingConventions.Standard, typeof(void), new[] {typeof(MyType)}, typeof(string).Module, true);

:

 typeof(MyType), true);

 typeof(string).Module, true);

要设置的字段类型是string,所以我们取它的模块。我很想听到一个关于为什么的更好的解释。

图变厚

结果是,typeof(string).module使它适用于任何类型。即使我像这样定义自己的类型:

using System;
using System.Reflection;
using System.Reflection.Emit;
#if true
using MyType = Microsoft.FSharp.Core.FSharpOption<string>;
#else
using MyType = System.Tuple<ConsoleApplication27.Poco>;
#endif
namespace ConsoleApplication27
{
    public class Poco
    {
    }
    class Program
    {
        static void Main(string[] args)
        {
            var something = new MyType(null);
            var dynMethod = new DynamicMethod("ChangeField", MethodAttributes.Public | MethodAttributes.Static,
                CallingConventions.Standard, typeof(void), new[] {typeof(MyType)}, typeof(string).Module, true);
            var generator = dynMethod.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldnull);
            generator.Emit(OpCodes.Stfld,
                typeof(MyType).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)[0]);
            generator.Emit(OpCodes.Ret);
            var method = (Action<MyType>) dynMethod.CreateDelegate(typeof(Action<MyType>));
            method(something);
            Console.WriteLine(typeof(MyType).GetProperties()[0].GetGetMethod().Invoke(something, new object[0]));
        }
    }
}

现在什么都说不通了。FSharpOption类型存在于f#模块中。Poco类位于我的程序模块中。当传递的模块是corelib模块时,它仍然可以工作。但是,如果我传递的两个(以上)中的任何一个都没有意义…

相关内容

  • 没有找到相关文章