2015-11-06 9 views
6

लोड करने के लिए एमिट आईएल कोड मेरे पास आईएल कोड उत्सर्जित करने के लिए इस तरह का कोड है जो पूर्णांक या स्ट्रिंग मान लोड करता है। लेकिन मुझे नहीं पता कि decimal उस प्रकार को कैसे जोड़ना है। यह Emit विधि में समर्थित नहीं है। इसके लिए कोई समाधान?दशमलव मान

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 opcode उत्पन्न नहीं करता है, लेकिन इसके बजाय nop और stloc.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 /। संक्षेप में: decimals सीएलआर आदिम प्रकार नहीं हैं और सीधे एक लोड करने के लिए कोई आईएल opcode नहीं है। –

+0

एक गैर-कार्य समाधान के लिए उपरोक्त मेरा संपादन देखें। – ygoe

उत्तर

9

चलो, कुछ सी # कोड को बस डीकंपाइल करें जो एक ही चीज करता है - आप देखेंगे कि कोई दशमलव आदिम नहीं है।

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 विधि को रिवर्स-इंजीनियर भी कर सकते हैं ताकि आप उचित मूल्यों के साथ सरल कन्स्ट्रक्टर को कॉल कर सकें, या आंतरिक स्थिति को पढ़ने के लिए प्रतिबिंब का उपयोग कर सकें - इसमें बहुत सारे विकल्प हैं।

संपादित करें:

आप निकट पहुंच गए हैं, लेकिन आप Ilgen तोड़ दिया - जब निर्माता के अंतिम तर्क एक 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:

कैसे आप इस मामले पेड़ सी # संकलक द्वारा बनाया गया है में अभिव्यक्ति के पेड़ (उपयोग कर सकते हैं, लेकिन यह आप पर निर्भर है की एक साधारण उदाहरण) गतिशील विधि निकायों को परिभाषित करने के:

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

यह वह जगह है जाहिरा तौर पर क्योंकि _ "विस्तारित अंकीय पुस्तकालय" _ क्योंकि _ _ ("कुछ सामान्य रूप से उपलब्ध प्रोसेसर डेटा प्रकार के लिए प्रत्यक्ष सहायता प्रदान नहीं करते", कोल इंडिया विनिर्देश का हिस्सा नहीं है स्रोत: http: //www.ecma -international.org/publications/files/ECMA-ST/ECMA-335.pdf, बड़े पीडीएफ)। यही कारण है कि 'दशमलव' (न ही 'एकल') लोड करने के लिए कोई ऑपोड नहीं है। – CodeCaster

+0

सुराग के लिए धन्यवाद। दुर्भाग्यवश यह अभी भी ठीक से काम नहीं करता है। प्रश्न में मेरा संपादन देखें। – ygoe

+1

@LonelyPixel सही कोड के साथ अपडेट किया गया - 'ldc.i4' * * को' int' पास किया जाना चाहिए। यह एक शर्म की बात है कि आईएलजीन आपको ऐसा करने देगा, लेकिन आपको केवल सावधान रहना होगा :) हालांकि, आपको वास्तव में आईएलजीएन की ज़रूरत नहीं है - आजकल 'अभिव्यक्ति.कंपाइल' का उपयोग क्यों न करें? – 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); 
} 
संबंधित मुद्दे