无法通过 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 的新手,可能对代码如此简单抱有太高的期望。
然而即便如此,我还是注意到 运行 这段代码似乎多次以看似连续的顺序生成 IntValue
和 Id
的值,所以我很好奇这些值到底来自哪里来自.
您正在创建的方法和委托采用对象类型的参数。这适用于目标实例(因为它是引用类型),当它是字符串时适用于值参数,但当它是值类型(如 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);
我正在使用 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 的新手,可能对代码如此简单抱有太高的期望。
然而即便如此,我还是注意到 运行 这段代码似乎多次以看似连续的顺序生成 IntValue
和 Id
的值,所以我很好奇这些值到底来自哪里来自.
您正在创建的方法和委托采用对象类型的参数。这适用于目标实例(因为它是引用类型),当它是字符串时适用于值参数,但当它是值类型(如 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);