可以使用动态方法中的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
,您可以在为Tuple
和FSharpOption
写入只读字段之间切换。对于前者,即使两者具有相似的结构,也可以完美地工作,即通过属性访问单个只读字段。对于后者,它会导致验证失败。为什么会这样呢?
很晚才回复。这很奇怪。通过将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模块时,它仍然可以工作。但是,如果我传递的两个(以上)中的任何一个都没有意义…