IlGenerator 发射
IlGenerator Emit
我正在尝试 DynamicMethod
并尝试使用 IL
创建一些对象。我想创建以下非常基本的对象:
new Queue<double>(new List<double>{100});
我已经使用 ILDASM 来查看 OpCodes
需要什么来生成它。这是 ILDASM 告诉我的:
IL_0000: newobj instance void class [System.Collections]System.Collections.Generic.List`1<float64>::.ctor()
IL_0005: dup
IL_0006: ldc.r8 100.
IL_000f: callvirt instance void class [System.Collections]System.Collections.Generic.List`1<float64>::Add(!0)
IL_0014: newobj instance void class [System.Collections]System.Collections.Generic.Queue`1<float64>::.ctor(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!0>)
IL_0019: pop
IL_001a: ret
这就是我正在做的事情:
var dynMethod = new DynamicMethod("QueueMaker", typeof(Queue<double>), Type.EmptyTypes);
ILGenerator ilGen = dynMethod.GetILGenerator();
ilGen.Emit(OpCodes.Newobj, typeof(List<double>).GetConstructor(Type.EmptyTypes));
ilGen.Emit(OpCodes.Dup);
ilGen.Emit(OpCodes.Ldc_R8, 100);
ilGen.EmitCall(OpCodes.Callvirt, typeof(List<double>).GetMethod("Add"), null);
ilGen.Emit(OpCodes.Newobj, typeof(Queue<double>).GetConstructor(new[] { typeof(IEnumerable<double>) }));
ilGen.Emit(OpCodes.Pop);
ilGen.Emit(OpCodes.Ret);
var returnFunc = (Func<Queue<double>>)dynMethod.CreateDelegate(typeof(Func<Queue<double>>));
var queue = returnFunc();
我遇到异常System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'
我做错了什么?
你真的想和 Emit 一起玩吗?您只想创建动态方法,在我看来 Expressions Trees 更容易使用。
结果是一样的:一个Func<Queue<double>>
.
但是首先你的代码可以简单化。您调用一个 ctor of Queue
来获取元素集合。您只想将值 100
添加到队列中。也许结果会是一样的:
var result = new Queue<double>();
result.Enqueue(100);
return result;
创建这个的代码是:
// Getting types/methods
var queueItemType = typeof(double);
var queueType = typeof(Queue<>).MakeGenericType(queueItemType);
var queueEnqueueMethod = queueType.GetMethod(nameof(Queue<object>.Enqueue), new[] { queueItemType });
// Build the Func<>
var result = Expression.Parameter(queueType, "result");
var queueInstance = Expression.New(queueType);
var resultAssign = Expression.Assign(result, queueInstance);
var queueItem = Expression.Constant(Convert.ChangeType(100, queueItemType), queueItemType);
var addCall = Expression.Call(result, queueEnqueueMethod, queueItem);
var body = new List<Expression>
{
resultAssign,
addCall,
result // The last line in body will be the result value of the Func<>.
};
var lambda = Expression.Lambda<Func<Queue<double>>>(Expression.Block(new[] { result }, body));
var func = lambda.Compile();
// Call it :-)
var queue = func();
Console.WriteLine(queue.Count);
Console.WriteLine(queue.Dequeue());
可以通过合并一些行来缩短代码,但为了清楚起见,我将每个部分都写在一行中。
提示:您可以对每个表达式调用 ToString()
以查看生成的内容,这对于查找错误非常有用。
如果您确实需要,可以使用相同的方式创建列表。
编辑
还有一个'one-liner':
() => new Queue<double>(Enumerable.Repeat(100.0, 1))
这是构建它的代码:
// Getting types/methods
var itemType = typeof(double);
var repeatMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.Repeat)).MakeGenericMethod(itemType);
var queueType = typeof(Queue<>).MakeGenericType(itemType);
var queueCtor = queueType.GetConstructor(new[] { typeof(IEnumerable<>).MakeGenericType(itemType) });
// Build the Func<>
var repeatCall = Expression.Call(repeatMethod, Expression.Constant(Convert.ChangeType(100, itemType)), Expression.Constant(1, typeof(int)));
var ctorCall = Expression.New(queueCtor, repeatCall);
var lambda = Expression.Lambda<Func<Queue<double>>>(ctorCall);
var func = lambda.Compile();
// Call it :-)
var queue = func();
Console.WriteLine(queue.Count);
Console.WriteLine(queue.Dequeue());
What am I doing wrong?
两件事:
(1)
ilGen.Emit(OpCodes.Ldc_R8, 100);
传递的值类型不正确。确保为 double
:
调用重载
ilGen.Emit(OpCodes.Ldc_R8, (double)100); // or 100d
(2)
ilGen.Emit(OpCodes.Pop);
很可能 ILDASM 包含了这一点,因为这里 new Queue<double>(new List<double>{100});
您正在丢弃结果,但是当您需要 return 将结果提供给调用者时,此指令无效。将新的 List<double>
实例保存在计算堆栈上的 Dup
指令已被 Queue<double>
构造函数调用消耗,因此这会将结果从堆栈中移除,这最终导致无效堆叠。
去掉那一行,问题就解决了。
我正在尝试 DynamicMethod
并尝试使用 IL
创建一些对象。我想创建以下非常基本的对象:
new Queue<double>(new List<double>{100});
我已经使用 ILDASM 来查看 OpCodes
需要什么来生成它。这是 ILDASM 告诉我的:
IL_0000: newobj instance void class [System.Collections]System.Collections.Generic.List`1<float64>::.ctor()
IL_0005: dup
IL_0006: ldc.r8 100.
IL_000f: callvirt instance void class [System.Collections]System.Collections.Generic.List`1<float64>::Add(!0)
IL_0014: newobj instance void class [System.Collections]System.Collections.Generic.Queue`1<float64>::.ctor(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!0>)
IL_0019: pop
IL_001a: ret
这就是我正在做的事情:
var dynMethod = new DynamicMethod("QueueMaker", typeof(Queue<double>), Type.EmptyTypes);
ILGenerator ilGen = dynMethod.GetILGenerator();
ilGen.Emit(OpCodes.Newobj, typeof(List<double>).GetConstructor(Type.EmptyTypes));
ilGen.Emit(OpCodes.Dup);
ilGen.Emit(OpCodes.Ldc_R8, 100);
ilGen.EmitCall(OpCodes.Callvirt, typeof(List<double>).GetMethod("Add"), null);
ilGen.Emit(OpCodes.Newobj, typeof(Queue<double>).GetConstructor(new[] { typeof(IEnumerable<double>) }));
ilGen.Emit(OpCodes.Pop);
ilGen.Emit(OpCodes.Ret);
var returnFunc = (Func<Queue<double>>)dynMethod.CreateDelegate(typeof(Func<Queue<double>>));
var queue = returnFunc();
我遇到异常System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'
我做错了什么?
你真的想和 Emit 一起玩吗?您只想创建动态方法,在我看来 Expressions Trees 更容易使用。
结果是一样的:一个Func<Queue<double>>
.
但是首先你的代码可以简单化。您调用一个 ctor of Queue
来获取元素集合。您只想将值 100
添加到队列中。也许结果会是一样的:
var result = new Queue<double>();
result.Enqueue(100);
return result;
创建这个的代码是:
// Getting types/methods
var queueItemType = typeof(double);
var queueType = typeof(Queue<>).MakeGenericType(queueItemType);
var queueEnqueueMethod = queueType.GetMethod(nameof(Queue<object>.Enqueue), new[] { queueItemType });
// Build the Func<>
var result = Expression.Parameter(queueType, "result");
var queueInstance = Expression.New(queueType);
var resultAssign = Expression.Assign(result, queueInstance);
var queueItem = Expression.Constant(Convert.ChangeType(100, queueItemType), queueItemType);
var addCall = Expression.Call(result, queueEnqueueMethod, queueItem);
var body = new List<Expression>
{
resultAssign,
addCall,
result // The last line in body will be the result value of the Func<>.
};
var lambda = Expression.Lambda<Func<Queue<double>>>(Expression.Block(new[] { result }, body));
var func = lambda.Compile();
// Call it :-)
var queue = func();
Console.WriteLine(queue.Count);
Console.WriteLine(queue.Dequeue());
可以通过合并一些行来缩短代码,但为了清楚起见,我将每个部分都写在一行中。
提示:您可以对每个表达式调用 ToString()
以查看生成的内容,这对于查找错误非常有用。
如果您确实需要,可以使用相同的方式创建列表。
编辑
还有一个'one-liner':
() => new Queue<double>(Enumerable.Repeat(100.0, 1))
这是构建它的代码:
// Getting types/methods
var itemType = typeof(double);
var repeatMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.Repeat)).MakeGenericMethod(itemType);
var queueType = typeof(Queue<>).MakeGenericType(itemType);
var queueCtor = queueType.GetConstructor(new[] { typeof(IEnumerable<>).MakeGenericType(itemType) });
// Build the Func<>
var repeatCall = Expression.Call(repeatMethod, Expression.Constant(Convert.ChangeType(100, itemType)), Expression.Constant(1, typeof(int)));
var ctorCall = Expression.New(queueCtor, repeatCall);
var lambda = Expression.Lambda<Func<Queue<double>>>(ctorCall);
var func = lambda.Compile();
// Call it :-)
var queue = func();
Console.WriteLine(queue.Count);
Console.WriteLine(queue.Dequeue());
What am I doing wrong?
两件事:
(1)
ilGen.Emit(OpCodes.Ldc_R8, 100);
传递的值类型不正确。确保为 double
:
ilGen.Emit(OpCodes.Ldc_R8, (double)100); // or 100d
(2)
ilGen.Emit(OpCodes.Pop);
很可能 ILDASM 包含了这一点,因为这里 new Queue<double>(new List<double>{100});
您正在丢弃结果,但是当您需要 return 将结果提供给调用者时,此指令无效。将新的 List<double>
实例保存在计算堆栈上的 Dup
指令已被 Queue<double>
构造函数调用消耗,因此这会将结果从堆栈中移除,这最终导致无效堆叠。
去掉那一行,问题就解决了。