首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >IlGenerator发射

IlGenerator发射
EN

Stack Overflow用户
提问于 2018-12-11 03:25:27
回答 2查看 1K关注 0票数 1

我正在使用DynamicMethod,并尝试使用IL创建一些对象。我想创建以下非常基本的对象:

代码语言:javascript
复制
new Queue<double>(new List<double>{100});

我已经使用ILDASM来查看需要什么OpCodes来生成它。这就是ILDASM告诉我的:

代码语言:javascript
复制
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

这就是我要做的:

代码语言:javascript
复制
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.'我做错了什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-12-11 09:09:10

我做错了什么?

有两件事:

(1)

代码语言:javascript
复制
ilGen.Emit(OpCodes.Ldc_R8, 100);

这传递了不正确的值类型。确保为double调用重载

代码语言:javascript
复制
ilGen.Emit(OpCodes.Ldc_R8, (double)100); // or 100d

(2)

代码语言:javascript
复制
ilGen.Emit(OpCodes.Pop);

很可能ILDASM包含了这一点,因为在这里,new Queue<double>(new List<double>{100});会丢弃结果,但是当您需要将结果返回给调用者时,这个指令是无效的。将新的List<double>实例保存在计算堆栈上的List<double>指令已被Queue<double>构造函数调用占用,因此这将从堆栈中删除结果,这最终导致堆栈无效。

把那条线移开,问题就解决了。

票数 5
EN

Stack Overflow用户

发布于 2018-12-11 06:20:16

你真的想和发射一起玩吗?您只想创建动态方法,在我看来,表达式树更易于使用。

结果将是相同的:一个Func<Queue<double>>

但首先,您的代码可以简化。您可以调用接受元素集合的克托尔 of Queue。您只想将值100添加到队列中。也许结果将与:

代码语言:javascript
复制
var result = new Queue<double>();
result.Enqueue(100);
return result;

这就是要创建的代码是:

代码语言:javascript
复制
// 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(),查看将用什么来生成错误,这对查找错误非常有用。

如果你真的需要的话,这个列表可以用同样的方式创建。

编辑

还有一个“一条龙”:

() => new Queue<double>(Enumerable.Repeat(100.0, 1))

这是构建它的代码:

代码语言:javascript
复制
// 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());

演示

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53716931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档