【问题标题】:Is there a way to create a delegate to get and set values for a FieldInfo?有没有办法创建一个委托来获取和设置 FieldInfo 的值?
【发布时间】:2013-04-10 23:39: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());

但是我该如何处理FieldInfos?

我不是在寻找 GetValueSetValue 的代表(这意味着我每次都会调用反射)

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

但是如果这里有CreateDelegate 方法呢? 我的意思是 since assignments return a value,我可以将分配视为一种方法吗? 如果可以,是否有一个 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 方法并返回值。
  • @ChrisSinclair 是的,性能是关键。看到这个线程:stackoverflow.com/questions/8087611/…
  • Func&lt;S, T&gt; getter = s =&gt; (T)fieldInfo.GetValue(s); 是你能做的,因为字段没有属性中的setter\getter 方法。如果性能是关键,我建议使用表达式。
  • @vvs0205 我知道,如果我无法将用于设置和获取值的方法信息传递给字段,我最终将不得不使用表达式。看看能不能模拟出来。
  • @newfal:看看我的回答。无论如何,您都可以修改它以完善只读字段以保持只读状态,或者您可以强制覆盖它们。拥有这两个小功能为我节省了很多时间。

标签: c# delegates expression-trees setvalue fieldinfo


【解决方案1】:

正如 Peter Ritchie 所建议的,您可以在运行时编译自己的代码。一旦您第一次调用委托,该方法就会被编译。所以第一次调用会很慢,但是任何后续调用都将尽可能快地在没有非托管指针/联合的情况下进入 .NET。除了第一次调用,delegate 比直接调用 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&lt;S, T&gt; 作为第一个参数按值传递,则不能使用 Action&lt;S, T&gt; 更改结构的成员。

【讨论】:

  • 你的答案和里奇的答案是 50-50。最后我决定选择他的答案为正确的,因为它给出了问题的正确答案;奖励你的努力。
  • 当字段是静态的(没有“this”指针)时,为什么要使用 Ldarg_1 而不是 Ldarg_0?
  • 因为委托总是 (S obj, T value),即使字段是静态的。用户将传递 null 或其他东西作为第一个参数。
  • 为了避免运行时错误,我不得不添加一个field.Module 参数,如下所示:var getMethod = new DynamicMethod(methodName, typeof(TMember), new[] { typeof(TObject) }, field.Module, true);
【解决方案2】:

[2019 年编辑: 由于这篇文章一直是我的最爱之一,因此我苦乐参半地注意到,在我自己的项目中,我在这里展示的方法已完全被新的、完全不同,更时尚的技术,我在this answer] 上详细介绍。


使用 C# 7.0 中的新“ref return”功能可以使创建和使用运行时动态生成的 get/set 访问器的过程更加简单,并且在语法上更透明。您现在可以使用一个方法返回一个托管指针 类型对字段的引用,本质上是一个访问器,它(反过来)启用方便的临时 get a̲n̲d̲ set 访问。下面,我提供了一个辅助实用函数,它简化了为任何类中的任意(即私有)实例字段生成 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 in situ(即,直接在其包含实例 oic 中),以便 任何和所有 em> 更改立即在那里公开可见。重要的是要意识到这意味着prv 尽管是int 类型和本地声明的,但其行为与典型的“本地”变量不同。这对于并发代码尤其重要;不仅更改可见 b̲e̲f̲o̲r̲e̲ MyFunction 已退出,而且现在使用 C# 7,调用者可以保留 引用返回托管指针(作为ref local),从而在任意长时间内继续修改目标a̲f̲t̲e̲r̲wards(尽管必须保持在获取引用堆栈的下方框架,即)。

当然,在这里和其他地方使用托管指针的一个主要且明显的优势是它继续保持有效(同样,在其堆栈帧的生命周期内),即使oic 本身就是一个引用类型在GC 堆中分配的实例——可能在垃圾回收期间被移动。这是与原生指针的巨大差异。

如上所示,ref-getter 是一个static extension method,可以在任何地方声明和/或使用。但是,如果您能够创建自己的派生自OfInterestClass 的类(也就是说,如果OfInterestClass 不是sealed),您可以做得更好。在派生类中,您可以公开 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!

如您所见,因为 属性 和前面的 方法 一样,也有一个 by reference 返回值,所以它的行为几乎完全一样就像一个字段一样。

现在了解详细信息。你如何创建我上面展示的静态 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     

与按值获取器的唯一区别是我们使用ldflda(加载字段地址)操作码而不是ldfld(加载字段)。所以如果你熟练使用DynamicMethod 应该没问题吧?

错了!...
   不幸的是,DynamicMethod 不允许 by-ref 返回值!

如果您尝试调用 DynamicMethod 构造函数并指定 ByRef 类型作为返回值...

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。幸运的是,我找到了一个简单的解决方法,让它工作。如果您将非引用类型作为临时“虚拟”传递给构造函数,但随后立即在新创建的 DynamicMethod 实例上使用反射将其 m_returnType 私有字段更改为 ByRef-type 键入您真正想要的 (sic.),然后一切似乎都正常工作。

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


如果您只想要完整的工作代码,请从此处复制到末尾


首先我们必须定义一个代表 ref-getter 的委托,因为不能声明使用 ByRef 的 Func&lt;T,TResult&gt; 委托。幸运的是,旧的 delegate 语法确实可以做到这一点(phew!)。

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

将委托以及以下静态函数放在一个集中的实用程序类中,在整个项目中都可以访问它们。这是最终的 ref-getter 创建函数,可用于为任何类中的所谓实例字段创建静态 ref-getter。

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-valued 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 有办法向人们发送他们的一些观点作为感谢,哈哈。 Genius 破解了 DynamicMethod 中的 ref 返回限制……那时我肯定会放弃。您是否在任何地方报告了缺点以便解决?
  • @MikeMarynowski 谢谢!虽然 not ideal,但通常是 people use bounties 用于此目的。
  • 只是 .NET 3.5 的一个小修复(对于我们这些使用 Unity 和单声道的人)。 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());
  • @Dai 不,不是。用于 ref 访问的 IL 将使用多余的间接寻址,如果您从不需要更改该字段值,那么这对于简单地获取句柄值字段值是不必要的。
  • @GlennSlayden较新的 .NET Core 运行时不需要反射 hack。见github.com/dotnet/corefx/issues/30753
【解决方案3】:

字段访问不是通过方法(如 getter 和 setter)执行的——它是通过 IL 指令执行的——所以没有什么可以分配给委托。您必须使用表达式路由来创建可以分配给委托的代码“块”(实际上是 IL)。

【讨论】:

  • 我认为你是对的。我相信没有什么 reflection-ish 的,因为通过编译表达式分配和读取速度非常快..
  • 是的,编译后的表达式应该和您编写自己的方法来访问该字段并正常编译一样快。
  • 彼得,我曾经在获取和设置属性方面对编译表达式与 createdelegate 进行基准测试,createdelegate 是明显的赢家,因此提出了这个问题
  • @nawfal 是的,但是您需要一种使用 CreateDelegate 的方法——没有你必须在编译时或运行时创建自己的方法(使用已编译的表达式或 codegen——我没有认为编译后的表达式会比 codegen 更快)。
  • 我认为这总结为一个答案,但在奖励赏金之前我也会等待其他答案。我一直在等待社区给出明确的看法。
【解决方案4】:

不,没有简单的方法可以创建委托来获取/设置字段。

您必须编写自己的代码来提供该功能。我建议在共享库中使用两个函数来提供此功能。

使用您的代码(在此示例中,我仅展示了 get-delegate 的创建):

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 类型):

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 创建这些委托,但该代码会更复杂,并且没有更高的性能(如果有的话)。

【讨论】:

  • No there is no easy way to create a delegate to get/set a field. 回答了我的问题,(已由 Ritchie 添加)。剩下的只是将我已经在问题中发布的代码重构为有用的方法。不过,感谢您的参与。顺便说一句,您可以避免在您的辅助函数中使用 (Func&lt;S,T&gt;) 强制转换。没什么,只是可读性
【解决方案5】:

我不知道你是否会使用Expression,那么为什么要避免反射呢? Expression 的大部分操作都依赖于反射。

GetValueSetValue 本身是 get methodset method,用于字段,但它们不适用于任何特定字段。

字段与属性不同,它们是字段,没有理由为每个字段生成 get/set 方法。但是,类型可能随不同的字段而变化,因此GetValueSetValue 被定义为parameter/return valueobject 以供变化。 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&lt;S, object&gt;,你可能想看看:

Covariance and Contravariance in C#, Part Three: Method Group Conversion Variance Lippert 先生的博客。

【讨论】:

  • 你:“那为什么要避免反思呢?”答:是的,表达式也使用反射。反射很慢,所以我们必须避免它。但是您可以使用反射和/或表达式来创建委托。一旦创建了直接调用方法或直接修改字段的委托,而不使用反射,您就有了一个非常快速的解决方案。因此,以这种方式创建委托很慢,但调用一百万次则不会。
  • @MartinMulder:嗯,我相信我们无法为字段创建这样的委托......也许还有一些解决方法......OpCodes.Callvirt
  • 取决于你如何看待它。一个字段总是由一段代码编写的。所以是的,必须存在或必须创建一段代码。一旦知道了这段代码,您就可以为它创建一个委托。创建这段代码(使用表达式)相当慢,但一旦到位,它很快。
  • @MartinMulder:正如我在答案中提到的,我找不到比GetValueSetValue 更多的东西;但是这些方法没有绑定到特定的类。所以它不取决于how I ..,它取决于语言是如何实现的..
  • GetValueSetValue 都是反射世界的一部分。创建这些函数的委托很快。调用它们仍然很慢,因为它们是通用的(适用于所有类型的字段)。调用它们一百万次是 1.000.000 x 慢。而且我什至不谈论拳击和拆箱。如果您使用泛型创建一段代码,您实际上会创建像 GetValueSetValue 这样的函数,但只是专门的和高速的。所以在这两种情况下你都有一段代码,但你更喜欢哪一段代码?
【解决方案6】:

DynamicMethod 中的ref return 限制似乎消失了至少在 v4.0.30319 上

用信息from microsoft docs修改this answer by 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

【讨论】:

    【解决方案7】:

    这是在处理对象时创建委托的另一种选择(不知道字段的特定类型)。虽然如果 field 是一个结构,它会更慢(因为装箱)。

    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 的答案一样,除了非泛型吗?
    • 是的,我修改了 Zotta 的答案以解决您无法使用泛型的情况。
    【解决方案8】:

    只是为了添加更多的方法: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()));
    
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-31
      相关资源
      最近更新 更多