无法通过 IL Emit on Object 设置某些类型值

Cannot set certain type values via IL Emit on Object

我正在使用 ILGenerator 和 Emit 创建一个函数来设置指定实例上 属性 的值。主要原因是我正在处理的解决方案的性能方面。我有一个基本的代码形式,它应该采用 2 个对象并通过 IL 加载它们以调用一个集合函数。我发现的问题是我传递给设置的值似乎被忽略了,另一个看似生成的值被设置在它的位置。

这是我在 LinqPad 中创建的用于演示此问题的基本用法示例:

void Main()
{
    var instance = new TestClass
    {
        Id = new Guid("f0564ce7-f249-4105-8fc4-2c65cfe095f6"),
        StringValue = "Something",
        IntValue = 0
    };

    MethodOne(instance);
}

private void MethodOne(TestClass instance)
{
    var setStringMethod = GenerateMethodAssignment("StringValue");
    setStringMethod(instance, "Something Else");

    var setGuidMethod = GenerateMethodAssignment("Id");
    setGuidMethod(instance, new Guid("f8b0fae2-40bb-422a-815f-2300cceb4329"));

    var setIntMethod = GenerateMethodAssignment("IntValue");
    setIntMethod(instance, 100);
    instance.Dump();
}

// Define other methods and classes here
public class TestClass
{
    public Guid Id { get; set; }
    public string StringValue { get; set; }
    public int IntValue { get; set; }
}

private Action<object, object> GenerateMethodAssignment(string propName)
{
    var setMethod = typeof(TestClass).GetProperty(propName).GetSetMethod();
    var argTypes = new Type[] { typeof(object), typeof(object) };
    var method = new DynamicMethod(Guid.NewGuid().ToString(), null, argTypes, GetType(), true);

    var ilGenerator = method.GetILGenerator();
    ilGenerator.Emit(OpCodes.Ldarg_0);
    ilGenerator.Emit(OpCodes.Ldarg_1);
    ilGenerator.Emit(OpCodes.Call, setMethod);
    ilGenerator.Emit(OpCodes.Ret);

    var action = (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>));
    return action;
}

输出:

字符串值设置正常,但 Guid 或 Integer 值未按我的预期设置。我是 IL 的新手,可能对代码如此简单抱有太高的期望。

然而即便如此,我还是注意到 运行 这段代码似乎多次以看似连续的顺序生成 IntValueId 的值,所以我很好奇这些值到底来自哪里来自.

您正在创建的方法和委托采用对象类型的参数。这适用于目标实例(因为它是引用类型),当它是字符串时适用于值参数,但当它是值类型(如 int 或 guid)时则不行。您必须添加拆箱指令以将装箱的值类型转换为堆栈上的实际值。

 Type propertyType = ...;
 var ilGenerator = method.GetILGenerator();
 ilGenerator.Emit(OpCodes.Ldarg_0);
 ilGenerator.Emit(OpCodes.Ldarg_1);
 if (propertyType.IsValueType)
     ilGenerator.Emit(OpCodes.Unbox, propertyType);
 ilGenerator.Emit(OpCodes.Call, setMethod);
 ilGenerator.Emit(OpCodes.Ret);