代码之家  ›  专栏  ›  技术社区  ›  JohanP

发光二极管

  •  1
  • JohanP  · 技术社区  · 6 年前

    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.' 我做错了什么?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Ivan Stoev    6 年前

    我做错了什么?

    两件事:

    ilGen.Emit(OpCodes.Ldc_R8, 100);
    

    这传递的值类型不正确。一定要打电话给超载者 double :

    ilGen.Emit(OpCodes.Ldc_R8, (double)100); // or 100d
    

    (2)

    ilGen.Emit(OpCodes.Pop);
    

    new Queue<double>(new List<double>{100}); 您正在丢弃结果,但当需要将结果返回给调用方时,此指令无效。这个 Dup List<double> 计算堆栈上的实例已被 Queue<double> 构造函数调用,因此这将从堆栈中删除结果,这最终导致堆栈无效。

        2
  •  1
  •   Sebastian Schumann    6 年前

    你真的想和我玩自大游戏吗?在我看来,你只想创建动态方法 Expressions Trees 更容易使用。

    Func<Queue<double>> .

    但首先,您的代码可以简化。你叫一个 ctor 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());
    

    DEMO

    提示:你可以打电话 ToString() 在每个表达式上查看生成的内容对于查找错误非常有用。

    如果您确实需要,可以用同样的方法创建列表。

    编辑

    还有一个“一行”:

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

    DEMO