c# - 生成动态方法来设置结构的字段而不是使用反射

标签 c# .net reflection reflection.emit dynamic-method

假设我有以下代码更新 struct 的字段使用反射。由于结构实例被复制到 DynamicUpdate方法,it needs to be boxed to an object before being passed .

struct Person
{
    public int id;
}

class Test
{
    static void Main()
    {
        object person = RuntimeHelpers.GetObjectValue(new Person());
        DynamicUpdate(person);
        Console.WriteLine(((Person)person).id); // print 10
    }

    private static void DynamicUpdate(object o)
    {
        FieldInfo field = typeof(Person).GetField("id");
        field.SetValue(o, 10);
    }
}

代码运行良好。现在,假设我不想使用反射,因为它很慢。相反,我想生成一些 CIL 直接修改 id字段并将该 CIL 转换为可重用委托(delegate)(例如,使用动态方法功能)。特别地,我想用这样的 s/t 替换上面的代码:

static void Main()
{
    var action = CreateSetIdDelegate(typeof(Person));
    object person = RuntimeHelpers.GetObjectValue(new Person());
    action(person, 10);
    Console.WriteLine(((Person)person).id); // print 10
}

private static Action<object, object> CreateSetIdDelegate(Type t)
{
    // build dynamic method and return delegate
}    

我的问题:有没有办法实现CreateSetIdDelegate除了使用以下技术之一?

  1. 生成使用反射调用 setter 的 CIL(作为本文中的第一个代码段)。这是没有意义的,因为要求是摆脱反射,但这是一个可能的实现,所以我只是提到。
  2. 而不是使用 Action<object, object> , 使用签名为 public delegate void Setter(ref object target, object value) 的自定义委托(delegate).
  3. 而不是使用 Action<object, object> , 使用 Action<object[], object>数组的第一个元素是目标对象。

我不喜欢 2 和 3 的原因是因为我不想为对象的 setter 和 struct 的 setter 使用不同的委托(delegate)(也不想让 set-object-field 委托(delegate)更复杂不必要的,例如 Action<object, object> )。我估计 CreateSetIdDelegate 的执行会根据目标类型是结构还是对象生成不同的 CIL,但我希望它返回向用户提供相同 API 的相同委托(delegate)。

最佳答案

再次编辑:现在结构有效。

在 C# 4 中有一种绝妙的方法可以做到这一点,但在此之前您必须编写自己的 ILGenerator 发出代码。他们向 .NET Framework 4 添加了 ExpressionType.Assign

这适用于 C# 4(已测试):

public delegate void ByRefStructAction(ref SomeType instance, object value);

private static ByRefStructAction BuildSetter(FieldInfo field)
{
    ParameterExpression instance = Expression.Parameter(typeof(SomeType).MakeByRefType(), "instance");
    ParameterExpression value = Expression.Parameter(typeof(object), "value");

    Expression<ByRefStructAction> expr =
        Expression.Lambda<ByRefStructAction>(
            Expression.Assign(
                Expression.Field(instance, field),
                Expression.Convert(value, field.FieldType)),
            instance,
            value);

    return expr.Compile();
}

编辑:这是我的测试代码。

public struct SomeType
{
    public int member;
}

[TestMethod]
public void TestIL()
{
    FieldInfo field = typeof(SomeType).GetField("member");
    var setter = BuildSetter(field);
    SomeType instance = new SomeType();
    int value = 12;
    setter(ref instance, value);
    Assert.AreEqual(value, instance.member);
}

关于c# - 生成动态方法来设置结构的字段而不是使用反射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1272454/

相关文章:

c# - 绑定(bind)到 ObservableCollection 的 TextBox 不更新

c# - 如何检测父进程的关闭?

python - 相当于 Python Numpy 函数的 F# 库或 .Net Numerics

c# - .Equals 和 == 有什么区别

c# - 从委托(delegate)检索调用方法和元数据

c# - 使用 C# 和 LINQ 比较两个大型字符串列表的最佳方法?

c# - "End of Central Directory record could not be found"- VS 社区 2015 中的 NuGet

c# - LINQ 是否可以动态添加 where 子句

C# 反射 : Fastest Way to Update a Property Value?

java - 使用反射启动 JavaFX 项目