无法通过对象

时间:2017-07-28 22:28:06

标签: c# .net reflection reflection.emit

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

这是我在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;
}

输出: Value set on object

字符串值设置正常,但Guid或Integer值未按我的预期设置。我对IL很陌生,可能对此代码抱有太高的期望,因为代码很简单。

然而,即便如此,我注意到多次运行此代码似乎以看似顺序的顺序生成IntValueId的值,因此我很好奇这些值来自何处。

1 个答案:

答案 0 :(得分:2)

您正在创建的方法和委托正在使用类型为object的参数。这适用于目标实例(因为它是一个引用类型),当它是一个字符串时可以用于value参数,但是当它是一个像int或guid这样的值类型时不行。您必须添加一个unbox指令,将盒装值类型转换为堆栈中的实际值。

 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);