यह भी एक पूर्ण उत्तर नहीं है, लेकिन मेरे पास कुछ विचार हैं।
मेरा मानना है कि मैंने .NET जेआईटी टीम से किसी के बिना उत्तर के रूप में अच्छा स्पष्टीकरण पाया है।
अपडेट करें
मैं थोड़ा गहरा लग रहा था, और मुझे विश्वास है कि मुझे इस मुद्दे का स्रोत मिल गया है। यह JIT टाइप-इनिशियलाइज़ेशन लॉजिक में बग के संयोजन के कारण प्रतीत होता है, और C # कंपाइलर में एक बदलाव जो इस धारणा पर निर्भर करता है कि JIT इरादा के अनुसार काम करता है। मुझे लगता है कि JIT बग .NET 4.0 में मौजूद था, लेकिन .NET 4.5 के कंपाइलर में बदलाव से इसे उजागर किया गया था।
मुझे नहीं लगता कि beforefieldinitयहां केवल यही मुद्दा है। मुझे लगता है कि यह उससे ज्यादा सरल है।
System.String.NET 4.0 से mscorlib.dll के प्रकार में एक स्थिर निर्माता है:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr ""
IL_0005: stsfld string System.String::Empty
IL_000a: ret
} // end of method String::.cctor
Mscorlib.dll के .NET 4.5 संस्करण में, String.cctor(स्टैटिक कंस्ट्रक्टर) विशिष्ट रूप से अनुपस्थित है:
..... कोई स्थिर निर्माता नहीं :( .....
दोनों संस्करणों में Stringप्रकार के साथ सजी है beforefieldinit:
.class public auto ansi serializable sealed beforefieldinit System.String
मैंने एक ऐसा प्रकार बनाने की कोशिश की, जो समान रूप से IL को संकलित करेगा (ताकि इसमें स्थैतिक क्षेत्र हों लेकिन कोई स्थैतिक निर्माता नहीं .cctor), लेकिन मैं ऐसा नहीं कर सका। इन सभी प्रकारों .cctorमें IL में एक विधि है:
public class MyString1 {
public static MyString1 Empty = new MyString1();
}
public class MyString2 {
public static MyString2 Empty = new MyString2();
static MyString2() {}
}
public class MyString3 {
public static MyString3 Empty;
static MyString3() { Empty = new MyString3(); }
}
मेरा अनुमान है कि .NET 4.0 और 4.5 के बीच दो चीजें बदल गई हैं:
पहला: ईई को बदल दिया गया था ताकि यह स्वचालित रूप से प्रारंभ हो जाए String.Empty से अनवांटेड कोड से हो जाए। यह परिवर्तन संभवतः .NET 4.0 के लिए किया गया था।
दूसरा: संकलक बदल गया ताकि यह स्ट्रिंग के लिए एक स्थिर निर्माता का उत्सर्जन न करे, यह जानकर String.Empty इसे अप्रबंधित पक्ष से सौंपा जाएगा। यह परिवर्तन .NET 4.5 के लिए किया गया प्रतीत होता है।
ऐसा प्रतीत होता है कि ईई जल्द ही कुछ अनुकूलन पथों के साथ पर्याप्त रूप से असाइन नहीं होता है String.Empty। संकलक के लिए किए गए परिवर्तन (या जो कुछ भी String.cctorगायब करने के लिए बदल गया है) को उम्मीद थी कि ईई इस असाइनमेंट को किसी भी उपयोगकर्ता कोड को निष्पादित करने से पहले कर देगा, लेकिन ऐसा प्रतीत होता है कि ईई इस असाइनमेंट String.Emptyको संदर्भ प्रकार के पुनरीक्षित सामान्य वर्गों के तरीकों में उपयोग करने से पहले नहीं करता है ।
अंत में, मेरा मानना है कि बग JIT टाइप-इनिशियलाइज़ेशन लॉजिक में एक गहरी समस्या का संकेत है। ऐसा प्रतीत होता है कि कंपाइलर में बदलाव एक विशेष मामला है System.String, लेकिन मुझे संदेह है कि जेआईटी ने यहां एक विशेष मामला बनाया है System.String।
मूल
सबसे पहले, वाह BCL लोगों को कुछ प्रदर्शन अनुकूलन के साथ बहुत रचनात्मक मिल गया है। कई के Stringतरीके अब एक धागा स्थिर कैश्ड का उपयोग कर प्रदर्शन कर रहे हैं StringBuilderवस्तु।
मैंने कुछ समय के लिए उस लीड का अनुसरण किया, लेकिन कोड पथ StringBuilderपर इसका उपयोग नहीं किया गया है Trim, इसलिए मैंने फैसला किया कि यह थ्रेड स्टैटिक समस्या नहीं हो सकती।
मुझे लगता है कि मुझे उसी बग का एक अजीब प्रकटीकरण मिला।
यह कोड एक्सेस उल्लंघन के साथ विफल होता है:
class A<T>
{
static A() { }
public A(out string s) {
s = string.Empty;
}
}
class B
{
static void Main() {
string s;
new A<object>(out s);
//new A<int>(out s);
System.Console.WriteLine(s.Length);
}
}
हालाँकि, यदि आप इसमें असुविधा करते //new A<int>(out s);हैं Mainतो कोड ठीक काम करता है। वास्तव में, यदि Aकिसी संदर्भ प्रकार के साथ पुनरीक्षण किया जाता है, तो कार्यक्रम विफल हो जाता है, लेकिन यदि Aकिसी भी प्रकार के मूल्य के साथ पुनरीक्षण किया जाता है, तो कोड विफल नहीं होता है। इसके अलावा अगर आप Aस्टैटिक कंस्ट्रक्टर को कमेंट करते हैं , तो कोड कभी भी फेल नहीं होता है। में खुदाई करने के बाद Trimऔर Format, यह स्पष्ट है कि समस्या यह है कि Lengthइनलेट किया जा रहा है, और इन Stringप्रकारों के ऊपर के नमूनों को प्रारंभिक नहीं किया गया है। विशेष रूप से, Aनिर्माता string.Emptyके शरीर के अंदर, सही ढंग से सौंपा नहीं गया है, हालांकि शरीर के अंदर Main,string.Empty सही ढंग से दिया जाता है।
यह मेरे लिए आश्चर्यजनक है कि Stringकिसी भी प्रकार का आरंभिक Aमूल्य मान के साथ पुन: परिभाषित किया जाता है या नहीं । मेरा एकमात्र सिद्धांत यह है कि जेनेरिक टाइप-इनिशियलाइज़ेशन के लिए कुछ अनुकूलन जेआईटी कोड पथ है जो सभी प्रकारों के बीच साझा किया जाता है, और वह रास्ता बीसीएल संदर्भ प्रकारों ("विशेष प्रकार?") और उनके राज्य के बारे में धारणा बनाता है। public staticखेतों के साथ अन्य बीसीएल वर्गों का एक त्वरित रूप से पता चलता है कि मूल रूप से उनमें से सभी एक स्थिर निर्माता को लागू करते हैं (यहां तक कि खाली निर्माणकर्ताओं और कोई डेटा नहीं है, जैसे System.DBNullऔर फ़ील्ड के System.Emptyसाथ बीसीएल मूल्य प्रकार public staticएक स्थिर निर्माता को लागू करने के लिए नहीं लगते हैं ()System.IntPtr उदाहरण के लिए) । यह इंगित करता है कि जेसीएल बीसीएल संदर्भ प्रकार आरंभीकरण के बारे में कुछ धारणाएं बनाता है।
FYI यहाँ दो संस्करणों के लिए JITed कोड है:
A<object>.ctor(out string):
public A(out string s) {
00000000 push rbx
00000001 sub rsp,20h
00000005 mov rbx,rdx
00000008 lea rdx,[FFEE38D0h]
0000000f mov rcx,qword ptr [rcx]
00000012 call 000000005F7AB4A0
s = string.Empty;
00000017 mov rdx,qword ptr [FFEE38D0h]
0000001e mov rcx,rbx
00000021 call 000000005F661180
00000026 nop
00000027 add rsp,20h
0000002b pop rbx
0000002c ret
}
A<int32>.ctor(out string):
public A(out string s) {
00000000 sub rsp,28h
00000004 mov rax,rdx
s = string.Empty;
00000007 mov rdx,12353250h
00000011 mov rdx,qword ptr [rdx]
00000014 mov rcx,rax
00000017 call 000000005F691160
0000001c nop
0000001d add rsp,28h
00000021 ret
}
बाकी कोड ( Main) दो संस्करणों के बीच समान है।
संपादित करें
इसके अलावा, दो संस्करणों से IL, कॉल करने के लिए समान A.ctorहै B.Main(), जिसमें पहले संस्करण के लिए IL शामिल है:
newobj instance void class A`1<object>::.ctor(string&)
बनाम
... A`1<int32>...
क्षण में।
एक और ध्यान देने वाली बात यह है कि इसके लिए JITed कोड A<int>.ctor(out string): गैर-जेनेरिक संस्करण के समान है।