OP tarafından atıfta bulunulan kaynağın güvenilirliği var ... ama Microsoft'a ne dersiniz - yapı kullanımına ilişkin duruş nedir? Microsoft'tan fazladan öğrenmeye çalıştım ve işte bulduğum şey:
Tür örnekleri küçük ve genellikle kısa ömürlü ise veya genellikle diğer nesnelere gömülmüşse, sınıf yerine bir yapı tanımlamayı düşünün.
Tür aşağıdaki özelliklerin hepsine sahip olmadığı sürece bir yapı tanımlamayın:
- Mantıksal olarak ilkel tiplere (tamsayı, çift vb.) Benzer tek bir değeri temsil eder.
- 16 bayttan küçük bir örnek boyutuna sahiptir.
- Değişmez.
- Sık sık kutsanması gerekmeyecek.
Microsoft sürekli olarak bu kuralları ihlal ediyor
Tamam, yine de # 2 ve # 3. Sevgili sözlüğümüzün 2 iç yapısı vardır:
[StructLayout(LayoutKind.Sequential)] // default for structs
private struct Entry //<Tkey, TValue>
{
// View code at *Reference Source
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Enumerator :
IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable,
IDictionaryEnumerator, IEnumerator
{
// View code at *Reference Source
}
* Referans Kaynağı
'JonnyCantCode.com' kaynağı 4'te 3 elde etti - # 4 muhtemelen sorun olmayacağından oldukça affedilebilir. Kendinizi bir yapıya bürünmüş bulursanız, mimarinizi yeniden düşünün.
Microsoft'un neden bu yapıları kullanacağına bakalım:
- Her yapı
Entry
ve Enumerator
tek değerleri temsil eder.
- hız
Entry
hiçbir zaman Dictionary sınıfının dışında bir parametre olarak iletilmez. Daha ayrıntılı araştırmalar, IEnumerable'ın uygulanmasını tatmin etmek için Dictionary'nin Enumerator
bir numaralandırıcı her istendiğinde kopyaladığı yapıyı kullandığını anlamlıdır.
- Dictionary sınıfının içinde.
Enumerator
herkese açıktır, çünkü Sözlük numaralandırılabilir ve IEnumerator arabirimi uygulamasına eşit erişilebilirliğe sahip olmalıdır - örn. IEnumerator getter.
Güncelleme - Ek olarak, bir yapı - Enumerator'ın yaptığı gibi - bir arabirim uyguladığında ve bu uygulanan türe verildiğinde, yapının bir referans türü haline geldiğini ve yığına taşındığını fark edin. İç Sözlük sınıfına, Listeleyicisi olduğu hala bir değer türü. Ancak, bir yöntem çağrılır çağrılmaz GetEnumerator()
bir başvuru türü IEnumerator
döndürülür.
Burada görmediğimiz, yapıları değişmez tutmak veya yalnızca 16 bayt veya daha küçük bir örnek boyutunu korumak için herhangi bir girişim veya kanıt kanıtıdır:
- Yukarıda bildirildi yapılar hiçbir şey
readonly
- değil değişmez
- Bu yapının boyutu 16 baytın üzerinde olabilir
Entry
belirsiz bir ömrü vardır (mesafede Add()
için Remove()
, Clear()
ya da çöp toplama);
Ve ... 4. Her iki yapı da hepimiz bildiğimiz TKey ve TValue'yu referans türleri olma yeteneğine sahiptir (ilave bonus bilgisi)
Karma anahtarlar buna rağmen, kısmen hızlıdır çünkü bir yapıyı örneklemek bir referans türünden daha hızlıdır. Burada, Dictionary<int, int>
art arda artan tuşları ile 300.000 rasgele tamsayı depolayan bir var.
Kapasite: 312874
Boyut: 2660827 bayt
Tamamlandı Yeniden Boyutlandır: 5ms
Toplam doldurma süresi: 889ms
Kapasite : dahili dizi yeniden boyutlandırılmadan önce kullanılabilecek öğe sayısı.
MemSize : sözlüğü bir MemoryStream içine serileştirerek ve bir bayt uzunluğu alarak (amacımız için yeterince doğru) belirlenir.
Tamamlandı Yeniden Boyutlandır : dahili dizinin 150862 öğeden 312874 öğeye yeniden boyutlandırılması için geçen süre. Her öğenin sırayla kopyalandığını anladığınızda Array.CopyTo()
, bu çok perişan değildir.
Doldurulacak toplam süre : günlük kaydı ve OnResize
kaynağa eklediğim bir olay nedeniyle kuşkusuz eğri ; ancak, işlem sırasında 15 kez yeniden boyutlandırırken 300k tamsayıları doldurmak yine de etkileyicidir. Sadece meraktan, kapasiteyi zaten biliyorsam toplam doldurma süresi ne olurdu? 13ms
Şimdi, ya Entry
bir sınıf olsaydı ? Bu zamanlar veya metrikler gerçekten çok farklı mıdır?
Kapasite: 312874
Boyut: 2660827 byte
Tamamlandı Yeniden Boyutlandır: 26ms
Toplam doldurma süresi: 964ms
Açıkçası, büyük fark yeniden boyutlandırmada. Sözlük Kapasite ile başlatılırsa herhangi bir fark var mı? Endişelenmek için yeterli değil ... 12ms .
Ne olur, çünkü Entry
bir yapı olduğundan , bir referans türü gibi başlatma gerektirmez. Bu, değer türünün hem güzelliği hem de çetesi. Entry
Referans türü olarak kullanmak için aşağıdaki kodu eklemek zorunda kaldım:
/*
* Added to satisfy initialization of entry elements --
* this is where the extra time is spent resizing the Entry array
* **/
for (int i = 0 ; i < prime ; i++)
{
destinationArray[i] = new Entry( );
}
/* *********************************************** */
Her dizi öğesini Entry
bir başvuru türü olarak başlatmamın nedeni MSDN: Structure Design'da bulunabilir . Kısacası:
Bir yapı için varsayılan bir yapıcı sağlamayın.
Bir yapı varsayılan kurucuyu tanımlarsa, yapının dizileri oluşturulduğunda, ortak dil çalışma zamanı her dizi öğesinde varsayılan kurucuyu otomatik olarak yürütür.
C # derleyicisi gibi bazı derleyiciler yapıların varsayılan kuruculara sahip olmasına izin vermez.
Aslında oldukça basit ve Asimov'un Üç Robotik Yasası'ndan ödünç alacağız :
- Yapının kullanımı güvenli olmalıdır
- Kural 1'i ihlal etmediği sürece yapı işlevini verimli bir şekilde yerine getirmelidir
- Kural # 1 kuralını yerine getirmek için imha edilmesi gerekmedikçe, yapı kullanım sırasında bozulmadan kalmalıdır.
... bundan ne alacağız : kısacası değer türlerinin kullanımından sorumlu olmak. Hızlı ve etkilidirler, ancak düzgün bir şekilde muhafaza edilmezlerse (yani kasıtsız kopyalar) birçok beklenmedik davranışa neden olabilirler.
System.Drawing.Rectangle
bu kuralların üçünü de ihlal eder.