我正在使用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;
}
字符串值设置正常,但Guid或Integer值未按我的预期设置。我对IL很陌生,可能对此代码抱有太高的期望,因为代码很简单。
然而,即便如此,我注意到多次运行此代码似乎以看似顺序的顺序生成IntValue
和Id
的值,因此我很好奇这些值来自何处。
答案 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);