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.
beforefieldinit
Burada 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 String
tip 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 .cctor
IL'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.Empty
yö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.Empty
bazı optimizasyon yollar boyunca çok yakında. Derleyicide yapılan değişiklik (veya String.cctor
kaybolmak 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.Empty
referans 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 String
yöntemlerle artık Konu statik önbelleğe kullanılarak yapılmaktadır StringBuilder
nesneyi.
Bir süre o yol izledi, ancak StringBuilder
üzerinde kullanılmaz Trim
Ben 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 Main
daha sonra kod gayet güzel çalışıyor. Aslında, A
herhangi bir referans türüyle yeniden bağlanırsa, program başarısız olur, ancak A
herhangi 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 Trim
ve Format
problem olduğu açıktır Length
inlined olan ve yukarıda bu örneklerin bu String
tip başlatılmamış. Özel olarak, gövdesi içinde A
bireyin yapıcı, string.Empty
doğru olmasa da gövdesinin içinde, atanmamışsa Main
, string.Empty
doğru bir şekilde tayin edilir.
Benim için şaşırtıcı bir String
şekilde bir şekilde A
tiplendirmenin 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 static
temelde alanları gösterir hepsi bunların statik yapıcısı (hatta olduğu gibi boş kurucular eder ve veri ile uygulamak System.DBNull
ve System.Empty
birlikte. BCL değer türleri public static
alanları 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.ctor
in ç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.