Bu da tam bir cevap değil, ama birkaç fikrim var.
.NET JIT ekibinin cevabından biri olmadan bulacağımız kadar iyi bir açıklama bulduğuma inanıyorum.
GÜNCELLEME
Biraz daha derinlere baktım ve sorunun kaynağını bulduğuma inanıyorum. JIT türü başlatma mantığındaki bir hata ve C # derleyicisindeki JIT'in amaçlandığı gibi çalıştığı varsayımına dayanan bir değişiklikten kaynaklanıyor gibi görünüyor. JIT hata .NET 4.0 vardı, ama .NET 4.5 için derleyici değişikliği ile ortaya çıktığını düşünüyorum.
beforefieldinitBurada tek sorun olduğunu sanmıyorum . Bence bundan daha basit.
Tip System.String.NET 4.0 den mscorlib.dll statik yapıcı içerir:
.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 dosyasının .NET 4.5 sürümünde String.cctor(statik yapıcı) dikkat çekici bir şekilde yoktur:
..... Statik yapıcı yok :( .....
Her iki versiyonda da Stringtip ile süslenmiştir beforefieldinit:
.class public auto ansi serializable sealed beforefieldinit System.String
Benzer şekilde IL'ye derlenecek bir tür oluşturmaya çalıştım (böylece statik alanlar var, ancak statik yapıcı yok .cctor), ancak yapamadım. Tüm bu türlerin .cctorIL'de bir yöntemi vardır:
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(); }
}
Benim tahminime göre .NET 4.0 ve 4.5 arasında iki şey değişti:
Birincisi: EE, String.Emptyyönetilmeyen koddan otomatik olarak başlatılacak şekilde değiştirildi . Bu değişiklik muhtemelen .NET 4.0 için yapılmıştır.
İkincisi: Derleyici String.Empty, yönetilmeyen taraftan atanacağını bilerek, dize için statik bir yapıcı yaymayacak şekilde değişti . Bu değişiklik .NET 4.5 için yapılmış gibi görünüyor.
O EE görünür değil atamak String.Emptybazı optimizasyon yollar boyunca çok yakında. Derleyicide yapılan değişiklik (veya String.cctorkaybolmak için değiştirilen her şey ), EE'nin bu atamayı herhangi bir kullanıcı kodu yürütülmeden yapmasını bekledi, ancak EE'nin bu atamayı daha önce String.Emptyreferans türü reified generic sınıflarında kullanılmadığı anlaşılıyor .
Son olarak, hatanın JIT tip başlatma mantığında daha derin bir sorunun göstergesi olduğuna inanıyorum. Derleyicideki değişiklik için özel bir durum olduğu anlaşılıyor System.String, ancak JIT'in burada özel bir dava açtığından şüpheliyim System.String.
orijinal
Her şeyden önce, WOW BCL çalışanları bazı performans optimizasyonlarıyla çok yaratıcı oldular. Birçok ait Stringyöntemlerle artık Konu statik önbelleğe kullanılarak yapılmaktadır StringBuildernesneyi.
Bir süre o yol izledi, ancak StringBuilderüzerinde kullanılmaz TrimBen bir Konu statik sorun olamazdı karar, kod yolu.
Sanırım aynı hatanın garip bir tezahürünü buldum.
Bu kod erişim ihlaliyle başarısız oluyor:
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);
}
}
Yorumsuz size Ancak, //new A<int>(out s);içinde Maindaha sonra kod gayet güzel çalışıyor. Aslında, Aherhangi bir referans türüyle yeniden bağlanırsa, program başarısız olur, ancak Aherhangi bir değer türüyle yeniden bağlanırsa kod başarısız olmaz. Ayrıca A, statik yapıcı yorum yaparsanız , kod asla başarısız olmaz. İçine kazma sonra Trimve Formatproblem olduğu açıktır Lengthinlined olan ve yukarıda bu örneklerin bu Stringtip başlatılmamış. Özel olarak, gövdesi içinde Abireyin yapıcı, string.Emptydoğru olmasa da gövdesinin içinde, atanmamışsa Main, string.Emptydoğru bir şekilde tayin edilir.
Benim için şaşırtıcı bir Stringşekilde bir şekilde Atiplendirmenin bir değer tipiyle ilişkilendirilip birleştirilmemesine bağlı olması şaşırtıcı . Tek teorim, genel tip başlatma için tüm türler arasında paylaşılan bazı optimize edici JIT kod yolu olması ve bu yolun BCL referans türleri ("özel tipler?") Ve durumları hakkında varsayımlar yapmasıdır. Diğer BCL sınıfları olsa hızlı bir bakış public statictemelde alanları gösterir hepsi bunların statik yapıcısı (hatta olduğu gibi boş kurucular eder ve veri ile uygulamak System.DBNullve System.Emptybirlikte. BCL değer türleri public staticalanları statik bir yapıcısı (uygulamak görünmüyor System.IntPtr) örneğin Bu, JIT'in BCL referans türü başlatma hakkında bazı varsayımlar yaptığını göstermektedir.
FYI İşte iki versiyon için JITed kodu:
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
}
Kodun ( Main) geri kalanı iki sürüm arasında aynıdır.
DÜZENLE
Ek olarak, iki versiyondan gelen IL, ilk versiyonun IL'sini içeren A.ctorin çağrısı haricinde aynıdır B.Main():
newobj instance void class A`1<object>::.ctor(string&)
karşı
... A`1<int32>...
saniyede.
Dikkat edilmesi gereken başka bir şey, JITed kodunun A<int>.ctor(out string): genel olmayan sürümdeki ile aynı olmasıdır.