2015-11-06 49 views
6

我有这样的代码来发送加载整数或字符串值的IL代码。但我不知道如何添加decimal类型。它在Emit方法中不受支持。任何解决方案?发送IL代码来加载一个十进制值

ILGenerator ilGen = methodBuilder.GetILGenerator(); 
if (type == typeof(int)) 
{ 
    ilGen.Emit(OpCodes.Ldc_I4, Convert.ToInt32(value, CultureInfo.InvariantCulture)); 
} 
else if (type == typeof(double)) 
{ 
    ilGen.Emit(OpCodes.Ldc_R8, Convert.ToDouble(value, CultureInfo.InvariantCulture)); 
} 
else if (type == typeof(string)) 
{ 
    ilGen.Emit(OpCodes.Ldstr, Convert.ToString(value, CultureInfo.InvariantCulture)); 
} 

不工作:

else if (type == typeof(decimal)) 
{ 
    ilGen.Emit(OpCodes.Ld_???, Convert.ToDecimal(value, CultureInfo.InvariantCulture)); 
} 

编辑:好了,这里就是我所做的:

else if (type == typeof(decimal)) 
{ 
    decimal d = Convert.ToDecimal(value, CultureInfo.InvariantCulture); 
    // Source: https://msdn.microsoft.com/en-us/library/bb1c1a6x.aspx 
    var bits = decimal.GetBits(d); 
    bool sign = (bits[3] & 0x80000000) != 0; 
    byte scale = (byte)((bits[3] >> 16) & 0x7f); 
    ilGen.Emit(OpCodes.Ldc_I4, bits[0]); 
    ilGen.Emit(OpCodes.Ldc_I4, bits[1]); 
    ilGen.Emit(OpCodes.Ldc_I4, bits[2]); 
    ilGen.Emit(sign ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); 
    ilGen.Emit(OpCodes.Ldc_I4, scale); 
    var ctor = typeof(decimal).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) }); 
    ilGen.Emit(OpCodes.Newobj, ctor); 
} 

但它不会产生newobj操作码,但而不是nopstloc.0。找到构造函数并将其传递给Emit调用。这里有什么问题?很显然,当试图执行生成的代码时会抛出一个InvalidProgramException,因为堆栈是完全混乱的。

+1

显然,(但不要把我的话)的“负载十进制”没有直接的操作码,您加载参数并调用构造函数小数:请参阅http ://stackoverflow.com/a/485834/266143 – CodeCaster

+1

另请参阅http://codeblog.jonskeet.uk/2014/08/22/when-is-a-constant-not-a-constant-when-its-a -decimal /。简而言之:小数不是CLR原始类型,并且没有用于直接加载一个的IL操作码。 –

+0

请参阅上面的我的编辑,了解非工作解决方案。 – ygoe

回答

9

来吧,只是反编译一些C#代码,做同样的事情 - 你会看到,没有小数原语。

42M 

编译成

ldc.i4.s 2A 
newobj  System.Decimal..ctor 

对于一个十进制数,这是更加复杂:

42.3M 

ldc.i4  A7 01 00 00 
ldc.i4.0  
ldc.i4.0  
ldc.i4.0  
ldc.i4.1  
newobj  System.Decimal..ctor 

最简单的方法是使用构造函数的int[]重载和静态方法GetBits。你也可以逆向工程SetBits方法,让你用适当的值调用更简单的构造函数,或使用反射来读取内部状态 - 有很多选项。

编辑:

你接近,但你打破了伊尔根 - 而最后一个参数的构造函数是一个byte,你加载常数必须int。如预期了以下工作:

var bits = decimal.GetBits(d); 
bool sign = (bits[3] & 0x80000000) != 0; 
int scale = (byte)((bits[3] >> 16) & 0x7f); 
gen.Emit(OpCodes.Ldc_I4, bits[0]); 
gen.Emit(OpCodes.Ldc_I4, bits[1]); 
gen.Emit(OpCodes.Ldc_I4, bits[2]); 
gen.Emit(sign ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); 
gen.Emit(OpCodes.Ldc_I4, scale); 
var ctor = typeof(decimal).GetConstructor(new[] { typeof(int), typeof(int), 
               typeof(int), typeof(bool), typeof(byte) }); 
gen.Emit(OpCodes.Newobj, ctor); 
gen.Emit(OpCodes.Ret); 

编辑2:

你如何使用表达式树(在这种情况下,树是由C#编译器创建的,但这是给你一个简单的例子)来定义动态方法机构:

var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Test"), 
                AssemblyBuilderAccess.Run); 
var module = assembly.DefineDynamicModule("Test"); 
var type = module.DefineType("TestType"); 

var methodBuilder = type.DefineMethod("MyMethod", MethodAttributes.Public 
                | MethodAttributes.Static); 
methodBuilder.SetReturnType(typeof(decimal)); 

Expression<Func<decimal>> decimalExpression =() => 42M; 

decimalExpression.CompileToMethod(methodBuilder); 

var t = type.CreateType(); 

var result = (decimal)t.GetMethod("MyMethod").Invoke(null, new object[] {}); 

result.Dump(); // 42 :) 
+3

这显然是因为_“扩展数字库”_不是CIL规范的一部分,因为_“某些常用的 处理器不提供对数据类型的直接支持”_(来源:http://www.ecma -international.org/publications/files/ECMA-ST/ECMA-335.pdf,大PDF)。这就是为什么没有加载'decimal'(也不是'single')的操作码。 – CodeCaster

+0

感谢您的线索。不幸的是它仍然无法正常工作。看到我的编辑问题。 – ygoe

+1

@LonelyPixel更新了正确的代码 - 'ldc.i4' *必须*传递一个'int'。这是一个耻辱,ILGen会让你这样做,但你必须小心:)但是,你现在不需要那么多的ILGen - 为什么不使用'Expression.Compile'? – Luaan

0

由于Luaan前面提到的,你可以使用decimal.GetBits方法和int[]构造。看看这个例子:

public static decimal RecreateDecimal(decimal input) 
{ 
    var bits = decimal.GetBits(input); 

    var d = new DynamicMethod("recreate", typeof(decimal), null); 
    var il = d.GetILGenerator(); 

    il.Emit(OpCodes.Ldc_I4_4); 
    il.Emit(OpCodes.Newarr, typeof(int)); 

    il.Emit(OpCodes.Dup); 
    il.Emit(OpCodes.Ldc_I4_0); 
    il.Emit(OpCodes.Ldc_I4, bits[0]); 
    il.Emit(OpCodes.Stelem_I4); 

    il.Emit(OpCodes.Dup); 
    il.Emit(OpCodes.Ldc_I4_1); 
    il.Emit(OpCodes.Ldc_I4, bits[1]); 
    il.Emit(OpCodes.Stelem_I4); 

    il.Emit(OpCodes.Dup); 
    il.Emit(OpCodes.Ldc_I4_2); 
    il.Emit(OpCodes.Ldc_I4, bits[2]); 
    il.Emit(OpCodes.Stelem_I4); 

    il.Emit(OpCodes.Dup); 
    il.Emit(OpCodes.Ldc_I4_3); 
    il.Emit(OpCodes.Ldc_I4, bits[3]); 
    il.Emit(OpCodes.Stelem_I4); 

    il.Emit(OpCodes.Newobj, typeof(decimal).GetConstructor(new[] {typeof(int[])})); 

    il.Emit(OpCodes.Ret); 
    return (decimal) d.Invoke(null, null); 
}