有没有一种方法可以创建一个委托来获取和设置FieldInfo的值?

32

对于属性,有GetGetMethodGetSetMethod,这样我就可以这么做:

Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), 
                                             propertyInfo.GetGetMethod());

并且

Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), 
                                               propertyInfo.GetSetMethod());

那么如何处理 FieldInfo 呢?

我不想使用 GetValueSetValue 委托(这意味着每次都会调用反射)。

Getter = s => (T)fieldInfo.GetValue(s);
Setter = (s, t) => (T)fieldInfo.SetValue(s, t);

但如果这里有一个CreateDelegate的方法呢?我的意思是,既然赋值语句返回一个值,那我能否像调用方法一样对待赋值语句呢?如果可以,那么是否有一个MethodInfo处理它?换句话说,我如何将设置和获取成员字段的正确MethodInfo传递给CreateDelegate方法,以便获得一个委托,通过该委托可以直接读写字段?

Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), fieldInfo.??);
Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), fieldInfo.??);

我可以构建表达式并编译它,但我正在寻找更简单的方法。最后,如果没有答案回答所提出的问题,我也不介意采用表达式的方式。如下所示:
var instExp = Expression.Parameter(typeof(S));
var fieldExp = Expression.Field(instExp, fieldInfo);
Getter = Expression.Lambda<Func<S, T>>(fieldExp, instExp).Compile();
if (!fieldInfo.IsInitOnly)
{
    var valueExp = Expression.Parameter(typeof(T));
    Setter = Expression.Lambda<Action<S, T>>(Expression.Assign(fieldExp, valueExp), instExp, valueExp).Compile();
}

我是否在追求不存在的东西(因为我从未见过类似的东西)?

你有必要调用 Delegate.CreateDelegate 吗?你已经有一个带有 getter 的委托。只需调用 getter(myInstanceOfT) 就会调用 fieldInfo.GetValue 方法并返回值。 - Chris Sinclair
@ChrisSinclair 是的,性能是关键。请参见此线程:https://dev59.com/A13Ua4cB1Zd3GeqP9h4M - nawfal
这个 Func<S, T> getter = s => (T)fieldInfo.GetValue(s); 是你所能做的,因为字段没有像属性一样的setter\getter方法。如果性能是关键,我建议使用Expression。 - Vyacheslav Volkov
@vvs0205 我知道如果我不能传递一个MethodInfo来设置和获取字段的值,最终我将不得不使用表达式。只是想看看它是否可以模拟。 - nawfal
1
@newfal:看看我的回答吧。你可以随意修改它,以便使只读字段保持只读状态,或者你可以强制覆盖它们。这两个小函数让我节省了很多时间。 - Martin Mulder
显示剩余6条评论
8个回答

32

正如Peter Ritchie所建议的那样,您可以在运行时编译自己的代码。只要您第一次调用委托,该方法就会被编译。因此,第一次调用将很慢,但任何后续调用都将是.NET中最快的,而不需要非托管指针/联合。除了第一次调用外,委托比FieldInfo直接调用快约500倍。

class DemoProgram
{
    class Target
    {
        private int value;
    }

    static void Main(string[] args)
    {
        FieldInfo valueField = typeof(Target).GetFields(BindingFlags.NonPublic| BindingFlags.Instance).First();
        var getValue = CreateGetter<Target, int>(valueField);
        var setValue = CreateSetter<Target, int>(valueField);

        Target target = new Target();

        setValue(target, 42);
        Console.WriteLine(getValue(target));
    }

    static Func<S, T> CreateGetter<S, T>(FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName + ".get_" + field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, typeof(T), new Type[1] { typeof(S) }, true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldsfld, field);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldfld, field);
        }
        gen.Emit(OpCodes.Ret);
        return (Func<S, T>)setterMethod.CreateDelegate(typeof(Func<S, T>));
    }

    static Action<S, T> CreateSetter<S,T>(FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName+".set_"+field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2]{typeof(S),typeof(T)},true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Stsfld, field);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Stfld, field);
        }
        gen.Emit(OpCodes.Ret);
        return (Action<S, T>)setterMethod.CreateDelegate(typeof(Action<S, T>));
    }
}

请记住,结构体是按值传递的。这意味着如果作为第一个参数按值传递,则无法使用Action<S, T>更改结构体成员。


这是你和里奇的答案之间的50-50局面。最终,我决定选择他的答案作为正确答案,因为它给出了问题的正确答案;并授予你赏金以表彰你的努力。 - nawfal
当字段是静态的(没有'this'指针)时,为什么要使用Ldarg_1而不是Ldarg_0? - vexe
因为委托始终是 (S obj, T value),即使该字段是静态的。用户将传递 null 或某个值作为第一个参数。 - Zotta
1
为了避免运行时错误,我必须添加一个 field.Module 参数,就像这样:var getMethod = new DynamicMethod(methodName, typeof(TMember), new[] { typeof(TObject) }, field.Module, true); - Timo

22

[2019年更新:由于本文一直是我最喜欢的文章之一,但令人遗憾的是,我在自己的项目中展示的方法已经完全被一个新的、完全不同且更加流畅的技术所取代,我在this answer中详细介绍了这种技术。]


使用 C# 7.0 中的新“ref return”特性可以使创建和使用运行时动态生成的 get/set 访问器的过程更加简单和语法透明。现在,您不需要使用 DynamicMethod 来发出用于访问字段的单独的 gettersetter 函数,而是可以使用一个返回对该字段的托管指针类型引用的单个方法,基本上是一个单个访问器,它(反过来)使得方便、临时的getset访问成为可能。下面,我提供了一个帮助实用程序函数,它简化了为任何类中的任意(即私有的)实例字段生成一个ByRef getter 函数的过程。
要查看代码,请跳转到下面的注释。 作为一个运行示例,假设我们想要访问一个私有实例字段 m_iPrivate,它是在类 OfInterestClass 中定义的 int 类型。
public class OfInterestClass
{
    private int m_iPrivate;
};

下面假设我们有一个静态字段"reference-getter"函数,它接受一个OfInterestClass实例,并使用新的C# 7 " ref return "功能通过引用返回所需的字段值(下面,我将提供通过DynamicMethod在运行时生成这样的函数的代码):
public static ref int __refget_m_iPrivate(this OfInterestClass obj)
{
     /// ...
}

我们只需要一个“ref-getter”函数,就可以完全读写访问私有字段。在以下示例中,特别注意调用setter的操作,以及使用(即)++和+=运算符的演示,因为如果您不了解C#7,直接将这些运算符应用于方法调用可能看起来有点不寻常。

void MyFunction(OfInterestClass oic)
{
    int the_value = oic.__refget_m_iPrivate();      // 'get'
    oic.__refget_m_iPrivate() = the_value + 100;    // 'set'

    /// or simply...
    oic.__refget_m_iPrivate() += 100;                // <-- yes, you can

    oic.__refget_m_iPrivate()++;                     // <-- this too, no problem

    ref int prv = ref oic.__refget_m_iPrivate();     // via "ref-local" in C#7
    prv++;
    foo(ref prv);                                    // all of these directly affect…
    prv = 999;                                       // …field m_iPrivate 'in-situ'
}

在这些示例中所展示的每个操作都操纵 m_iPrivate 原位(即“直接”在其包含实例oic中)以便于“任何和所有”的更改都能够立即公开显示出来。重要的是要认识到,这意味着尽管被声明为本地变量且具有int类型的prv不像您典型的“本地”变量那样运行。对于并发代码而言,这一点尤其重要; 不仅更改可以在MyFunction退出之前可见,而且现在随着C# 7,调用者有能力保留一个ref return 托管指针(作为ref local),从而可以继续修改目标任意长的时间a̲f̲t̲e̲r̲w̲a̲r̲d̲s̲(尽管必须保持在获取ref的堆栈帧之下)。

当然,在这里和其他一般情况下使用托管指针的一个显而易见的优点是,它会继续保持有效(再次强调,仅在其堆栈帧的生命周期内),即使oic本身是一个分配在GC堆中的引用类型实例,在垃圾回收期间可能会被移动。这与原生指针相比是巨大的区别。

如上所述,ref-getter是一个static扩展方法,可以在任何地方声明和/或使用。但是,如果您能够创建一个派生自OfInterestClass的类(也就是说,如果OfInterestClass不是密封类),那么您可以使其更加优美。在派生类中,您可以公开C#语法,以便使用基类的私有字段,就好像它是派生类的公共字段一样。要做到这一点,只需向您的类添加一个C#只读ref return属性,将静态ref-getter方法绑定到当前实例this即可:

public ref int m_iPrivate => ref __refget_m_iPrivate(this);

在这里,该属性被设置为public,因此任何人都可以访问该字段(通过对派生类的引用)。我们实质上公开发布了基类中的私有字段。现在,在派生类(或其他适当的地方)中,您可以执行以下任何操作:
int v = m_iPrivate;                             // get the value

m_iPrivate = 1234;                              // set the value

m_iPrivate++;                                   // increment it

ref int pi = ref m_iPrivate;                    // reference as C# 7 ref local

v = Interlocked.Exchange(ref m_iPrivate, 9999); // even do in-situ atomic operations on it!

你可以看到,因为像之前的方法一样,属性也具有按引用返回值,所以它的行为几乎与字段完全相同。

现在让我们来说细节。如何创建我上面展示的静态ref-getter函数?使用DynamicMethod,这应该很简单。例如,这里是传统(按值)静态getter函数的IL代码:

// static int get_iPrivate(OfInterestClass oic) => oic.m_iPrivate;
IL_0000: ldarg.0    
IL_0001: ldfld Int32 m_iPrivate/OfInterestClass
IL_0006: ret       

以下是我们所需的IL代码(ref-return):

// static ref int refget_iPrivate(OfInterestClass oic) => ref oic.m_iPrivate;
IL_0000: ldarg.0    
IL_0001: ldfld̲a Int32 m_iPrivate/OfInterestClass
IL_0006: ret     

The only difference from the by-value getter is that we are using the ldflda (load field address) opcode instead of ldfld (load field). So if you're well-practiced with DynamicMethod it should be no problem, right?
"Wrong!"... unfortunately DynamicMethod does not allow a by-ref return value!
If you try to call the DynamicMethod constructor specifying a ByRef type as the return value...
var dm = new DynamicMethod(
        "",                                 // method name
        typeof(int).MakeByRefType(),        // by-ref return type   <-- ERROR
        new[] { typeof(OfInterestClass) },  // argument type(s)
        typeof(OfInterestClass),            // owner type
        true);                              // private access

......该函数会抛出NotSupportedException,并显示以下信息:

返回类型包含一些无效类型(例如null、ByRef)

显然,这个函数没有接收到C#7和ref-return的备忘录。幸运的是,我找到了一个简单的解决方法来使它正常工作。如果你将一个非ref类型作为临时“占位符”传递给构造函数,但随后立即使用反射在新创建的DynamicMethod实例上更改其m_returnType私有字段,使其成为您实际想要的ByRef类型类型(sic.),那么一切都似乎可以正常工作。

为了加快速度,我将剪切完成的通用方法,通过创建/返回静态ref-getter函数来自动化整个过程,以获取类型为U、名称为提供的名称、定义在类T中的私有实例字段。


如果你只想要完整的可工作代码,请从以下位置复制到末尾。

首先我们需要定义一个代表 ref-getter 的委托,因为无法声明使用 ByRef 的 Func<T,TResult> 委托。幸运的是,旧版的 delegate 语法可以做到这一点 (呼!)。

public delegate ref U RefGetter<T, U>(T obj);

将代理与以下静态函数一起放置在一个中央实用类中,以便可以在整个项目中访问它们。这是最终的ref-getter创建函数,可用于在任何类中创建名为“instance”字段的静态ref-getter。请保留HTML标签。
public static RefGetter<T, U> create_refgetter<T, U>(String s_field)
{
    const BindingFlags bf = BindingFlags.NonPublic |
                            BindingFlags.Instance |
                            BindingFlags.DeclaredOnly;

    var fi = typeof(T).GetField(s_field, bf);
    if (fi == null)
        throw new MissingFieldException(typeof(T).Name, s_field);

    var s_name = "__refget_" + typeof(T).Name + "_fi_" + fi.Name;

    // workaround for using ref-return with DynamicMethod:
    //   a.) initialize with dummy return value
    var dm = new DynamicMethod(s_name, typeof(U), new[] { typeof(T) }, typeof(T), true);

    //   b.) replace with desired 'ByRef' return value
    dm.GetType().GetField("m_returnType", bf).SetValue(dm, typeof(U).MakeByRefType());

    var il = dm.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldflda, fi);
    il.Emit(OpCodes.Ret);

    return (RefGetter<T, U>)dm.CreateDelegate(typeof(RefGetter<T, U>));
}

回到本文开头,我们可以轻松提供使一切开始的 __refget_m_iPrivate 函数。我们将使用静态 ref-getter 建立函数体并在运行时存储在静态委托类型字段中(具有相同的签名),而不是直接在 C# 中编写静态函数。在实例属性或其他地方调用它的语法与编译器编写该函数时相同。
最后,要缓存动态创建的 ref-getter 委托,请将以下行放置在您选择的任何 static 类中。将 OfInterestClass 替换为基类的类型,将 int 替换为私有字段的字段类型,并更改字符串参数以匹配私有字段的名称。如果您无法创建派生自 OfInterestClass 的类(或不想创建),那么您已完成;只需将此字段设置为 public,就可以像调用函数一样调用它,传递任何 OfInterestClass 实例以获取引用,从而读取、写入或监视其 int 值的 private 字段“m_iPrivate”。
// Static delegate instance of ref-getter method, statically initialized.
// Requires an 'OfInterestClass' instance argument to be provided by caller.
static RefGetter<OfInterestClass, int> __refget_m_iPrivate = 
                                create_refgetter<OfInterestClass, int>("m_iPrivate");

如果您想以更干净或更自然的语法发布隐藏字段,可以选择定义一个(非静态)代理类,该类要么包含一个实例,要么更好地(如果可能)派生自字段隐藏类OfInterestClass。 不要像之前在全局static类中展示代码行一样部署它,而是将其放置在代理类中,然后还要添加以下行:

// optional: ref-getter as an instance property (no 'this' argument required)
public ref int m_iPrivate => ref __refget_m_iPrivate(this);

这太棒了!我希望StackOverflow有一种方式可以通过点数来向人们表示感谢,哈哈。你在DynamicMethod中绕过ref返回限制的方法真是太聪明了,如果是我,我肯定会放弃的。你是否在任何地方报告了这个缺陷,以便得到解决? - Mike Marynowski
@MikeMarynowski 谢谢!虽然不是最理想的,但通常人们会使用悬赏来达到这个目的。 - Glenn Slayden
1
只是针对.NET 3.5的一个小修复(对于我们使用Unity和mono的人来说)。DynamicMethod没有“m_returnType”字段,而是称为“returnType”。所以我会重构它:var f_returnType = dm.GetType().GetField("m_returnType", bf); if (f_returnType == null) f_returnType = dm.GetType().GetField("returnType", bf); f_returnType.SetValue(dm, typeof(U).MakeByRefType()); - Andreas Pardeike
1
@Dai 不是很需要。对于引用访问的IL将使用多余的间接,如果您从不需要更改该字段值,则这是不必要的,而只需获取句柄值字段即可。 - Glenn Slayden
2
@GlennSlayden P.S. 在较新的 .NET Core 运行时中,不需要使用反射 hack。请参见 https://github.com/dotnet/corefx/issues/30753 - Mike Marynowski
显示剩余4条评论

11

字段访问不是通过方法(如getter和setter)执行的-它是通过一个IL指令执行的-因此你不能将任何东西赋值给委托。你需要使用表达式路线创建一个代码“块”(有效的IL),可以被分配给委托。


你说得对,我认为没有什么“反射”的东西,因为通过编译表达式进行赋值和读取非常快速。 - nawfal
是的,编译后的表达式应该和你自己编写访问字段的方法并正常编译一样快。 - Peter Ritchie
2
彼得,我曾经对编译表达式和使用createdelegate创建属性的读取和设置进行过基准测试,结果createdelegate明显胜出,因此才有了这个问题。 - nawfal
1
@nawfal 是的,但你需要一个方法来使用CreateDelegate--否则你得在编译时或运行时自己创建(用编译表达式或代码生成--我不认为编译表达式比代码生成更快)。 - Peter Ritchie
我认为这已经是一个答案的总结了,但在授予奖励之前,我还会等待其他答案。我一直在等待社区对此有一个明确的看法。 - nawfal

10

没有简单的方法来创建一个获取/设置字段的委托。

你需要编写自己的代码来提供这个功能。我建议在一个共享库中提供两个函数来实现此功能。

使用你的代码(在这个例子中,我只展示了创建获取委托的过程):

static public class FieldInfoExtensions
{
    static public Func<S, T> CreateGetFieldDelegate<S, T>(this FieldInfo fieldInfo)
    {
        var instExp = Expression.Parameter(typeof(S));
        var fieldExp = Expression.Field(instExp, fieldInfo);
        return Expression.Lambda<Func<S, T>>(fieldExp, instExp).Compile();
    }
}
这使得从FieldInfo创建一个get-delegate变得容易(假设该字段是int类型)。

这使得从FieldInfo创建一个get-delegate变得容易(假设该字段是int类型):

Func<MyClass, int> getter = typeof(MyClass).GetField("MyField").CreateGetFieldDelegate<MyClass, int>();

或者如果我们稍微改变一下你的代码:

static public class TypeExtensions
{
    static public Func<S, T> CreateGetFieldDelegate<S, T>(this Type type, string fieldName)
    {
        var instExp = Expression.Parameter(type);
        var fieldExp = Expression.Field(instExp, fieldName);
        return Expression.Lambda<Func<S, T>>(fieldExp, instExp).Compile();
    }
}

这使得它更加容易:

Func<MyClass, int> getter = typeof(MyClass).CreateGetFieldDelegate<MyClass, int>("MyField");

也可以使用IL创建这些委托,但这段代码更加复杂且在性能方面并没有更多优势(如果有的话)。


没有简单的方法来创建委托以获取/设置字段。这回答了我的问题(已由Ritchie添加)。其余部分只是将我已经在问题中发布的代码重构为有用的方法。无论如何,感谢您加入讨论。顺便说一句,在您的帮助程序中可以避免(Func<S,T>)强制转换。这只是可读性问题。 - nawfal

2
我不知道如果您使用表达式,为什么要避免反射?大多数Expression操作都依赖于反射。 GetValueSetValue本身就是字段的get方法set方法,但它们不是针对特定字段的。
字段不像属性那样,它们只是字段,并且没有理由为每个字段生成get/set方法。但是,类型可能因不同的字段而异,因此GetValueSetValue将参数/返回值定义为object以进行变化。 GetValue甚至是一个抽象方法,也就是说,对于每个类(仍然是反射)覆盖它,必须在相同的签名内。
如果您不对它们进行类型,则以下代码应该可以:
public static void SomeMethod(FieldInfo fieldInfo) {
    var Getter=(Func<object, object>)fieldInfo.GetValue;
    var Setter=(Action<object, object>)fieldInfo.SetValue;
}

但是如果你想要,有一种受限制的方式:
public static void SomeMethod<S, T>(FieldInfo fieldInfo)
    where S: class
    where T: class {
    var Getter=(Func<S, object>)fieldInfo.GetValue;
    var Setter=(Action<S, T>)fieldInfo.SetValue;
}

由于Getter仍然是Func<S,object>,您可能想看一下Lippert先生博客上的C#中的协变和逆变,第三部分:方法组转换方差

1
你:“那为什么要避免反射?”回答:是的,表达式也利用了反射。反射很慢,所以我们必须避免使用它。但是,您可以使用反射和/或表达式来创建委托。一旦创建了直接调用方法或直接修改字段而不使用反射的委托,您就有了一个非常快速的解决方案。因此,以这种方式创建委托很慢,但是调用它一百万次并不慢。 - Martin Mulder
1
@MartinMulder:嗯,我相信我们无法为字段创建这样的委托..可能有一些解决方法.. OpCodes.Callvirt - Ken Kin
这要看你的角度。一个字段总是由一段代码编写的。因此,是的,必须存在或创建一段代码。一旦知道了这段代码,就可以创建一个委托来调用它。使用表达式创建这段代码相当缓慢,但一旦完成,它就很快了。 - Martin Mulder
1
GetValueSetValue都是反射世界的一部分。创建这些函数的委托很快。调用它们仍然很慢,因为它们是通用的(适用于所有类型的字段)。调用它们一百万次就会变得1,000,000倍慢。我甚至还没有谈到装箱和拆箱。如果您使用泛型创建代码片段,则实际上创建了类似于GetValueSetValue的函数,但只是专门化和高速的。因此,在这两种情况下,您都有一段代码,但您更喜欢哪一段代码呢? - Martin Mulder
@MartinMulder:...看起来我没有表达清楚,我的意思是据我所知,对于字段来说似乎没有这样的东西..,不过,我现在正在尝试更深入地追踪反射以找到答案。 - Ken Kin
显示剩余3条评论

2

DynamicMethod中的ref return限制在v4.0.30319版本中似乎已经消失。

参考Glenn Slayden的回答,结合Microsoft文档进行修改:

最初的回答已被Glenn Slayden修改并添加了信息。

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApp {
    public class MyClass {
        private int privateInt = 6;
    }

    internal static class Program {
        private delegate ref TReturn OneParameter<TReturn, in TParameter0>(TParameter0 p0);

        private static void Main() {
            var myClass = new MyClass();

            var method = new DynamicMethod(
                "methodName",
                typeof(int).MakeByRefType(), // <- MakeByRefType() here
                new[] {typeof(MyClass)}, 
                typeof(MyClass), 
                true); // skip visibility


            const BindingFlags bindings = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

            var il = method.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldflda, typeof(MyClass).GetField("privateInt", bindings));
            il.Emit(OpCodes.Ret);

            var getPrivateInt = (OneParameter<int, MyClass>) method.CreateDelegate(typeof(OneParameter<int, MyClass>));

            Console.WriteLine(typeof(string).Assembly.ImageRuntimeVersion);
            Console.WriteLine(getPrivateInt(myClass));
        }
    }
}

输出:

6

1
只是为了添加更多的方法 :D
 public static Func<T, TResult> CreatePropertyOrFieldReaderDelegate<T, TResult>(string field)
        {
            var input = Expression.Parameter(typeof(T));
            return Expression.Lambda<Func<T, TResult>>(Expression.PropertyOrField(input, field), input).Compile();
        }


这将创建一个返回值的方法。 测试用例。
class Testing {
  public int Data = 2;
  public string PropData { get; } = "Default";
 }


  [Fact]
  public void CreateSingleFieldReader()
        {
            var a = ReflectionHelper.CreatePropertyOrFieldReaderDelegate<Testing, int>("Data");
            Assert.Equal(2, a(new Testing()));

        }

1

当您使用对象(不知道字段的具体类型)时,还有另一种创建委托的选项。但如果字段是结构体(因为装箱),它会比较慢。

public static class ReflectionUtility
{
    public static Func<object, object> CompileGetter(this FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName + ".get_" + field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, typeof(object), new[] { typeof(object) }, true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldsfld, field);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Box, field.FieldType);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Castclass, field.DeclaringType);
            gen.Emit(OpCodes.Ldfld, field);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Box, field.FieldType);
        }
        gen.Emit(OpCodes.Ret);
        return (Func<object, object>)setterMethod.CreateDelegate(typeof(Func<object, object>));
    }

    public static Action<object, object> CompileSetter(this FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName + ".set_" + field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, null, new[] { typeof(object), typeof(object) }, true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Unbox_Any, field.FieldType);
            gen.Emit(OpCodes.Stsfld, field);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Castclass, field.DeclaringType);
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Unbox_Any, field.FieldType);
            gen.Emit(OpCodes.Stfld, field);
        }
        gen.Emit(OpCodes.Ret);
        return (Action<object, object>)setterMethod.CreateDelegate(typeof(Action<object, object>));
    }
}

这不就是Zotta的答案吗,只是没有使用泛型吗? - nawfal
是的,我修改了Zotta的回答,以适应不能使用泛型的情况。 - Nickolay Andreychuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接