it-swarm-tr.com

Sınıf yerine bir yapı ne zaman kullanılır?

Yapıların ve sınıfların ne zaman kullanılacağı konusunda temel kurallarınız nelerdir? Ben bu terimlerin C # tanımını düşünüyorum ama dilinizde benzer kavramlar varsa ben de görüşlerinizi duymak istiyorum.

Hemen hemen her şey için sınıfları kullanma eğilimindeyim ve sadece bir şey çok basit olduğunda ve bir PhoneNumber veya bunun gibi bir değer türü olması gerektiğinde yapıları kullanıyorum. Ancak bu nispeten küçük bir kullanım gibi görünüyor ve umarım daha ilginç kullanım örnekleri vardır.

176
RationalGeek

Takip edilecek genel kural, yapıların, oluşturulduktan sonra değiştirilemeyen ilgili özelliklerin küçük, basit (tek seviyeli) koleksiyonları olması gerektiğidir; başka bir şey için bir sınıf kullanın.

C #, yapıların ve sınıfların, tanımlayıcı anahtar kelime dışında bildirimde belirgin farklılıkları bulunmaması nedeniyle Nice; bu nedenle, bir yapıyı bir sınıfa "yükseltmeniz" veya tersine bir sınıfı bir yapıya "düşürmeniz" gerektiğini düşünüyorsanız, çoğunlukla anahtar kelimeyi değiştirmek basit bir konudur (birkaç başka gotcha vardır; yapılar türetilemez) herhangi bir sınıftan veya yapı türünden ve açıkça varsayılan bir parametresiz yapıcı tanımlayamaz).

"Çoğunlukla" diyorum, çünkü yapılar hakkında bilinmesi gereken en önemli şey, değer türleri oldukları için, onlara sınıflar (referans türleri) gibi davranmanın bir buçuk acıya neden olabileceğidir. Özellikle, bir yapının özelliklerini değişken hale getirmek beklenmedik davranışlara neden olabilir.

Örneğin, A ve B olmak üzere iki özelliğe sahip bir SimpleClass sınıfınız olduğunu varsayalım. Bu sınıfın bir kopyasını başlatır, A ve B'yi başlatır ve örneği başka bir yönteme geçirirsiniz. Bu yöntem ayrıca A ve B'yi de değiştirir. Çağıran işlevde (örneği oluşturan yöntem) geri döndüğünüzde, örneğin A ve B'si, çağrılan yöntemle kendilerine verilen değerlere sahip olur.

Şimdi, bunu bir yapı haline getiriyorsunuz. Özellikler hala değiştirilebilir. Önceki işlemlerle aynı sözdizimiyle aynı işlemleri gerçekleştirirsiniz, ancak şimdi, yöntem çağrıldıktan sonra A ve B'nin yeni değerleri örnekte değildir. Ne oldu? Sınıfınız artık bir yapı, yani bir değer türü. Bir yönteme bir değer türü iletirseniz, varsayılan değer (bir out veya ref anahtar sözcüğü olmadan) "değere göre" iletilmesidir; örneğin sığ bir kopyası yöntem tarafından kullanılmak üzere oluşturulur ve sonra yöntem ilk örneği olduğu gibi bırakarak imha edilir.

Yapınızın bir üyesi olarak bir referans türüne sahip olsaydınız bu daha da kafa karıştırıcı hale gelir (izin verilmez, ancak son derece hemen hemen tüm durumlarda kötü uygulama); sınıf klonlanmayacaktır (yalnızca yapının referansı), bu nedenle yapıda yapılan değişiklikler orijinal nesneyi etkilemez, ancak yapının alt sınıfındaki değişiklikler örneği çağrı kodundan etkiler. Bu, gerçek problemin bulunduğu yerden çok uzakta hatalara neden olabilecek çok tutarsız durumlara kolayca değişebilir yapıları yerleştirebilir.

Bu nedenle, C # üzerindeki neredeyse her otorite yapılarınızı her zaman değişmez kıldığını söyler; tüketicinin özelliklerin değerlerini yalnızca bir nesnenin yapımında belirtmesine izin verin ve hiçbir zaman bu örneğin değerlerini değiştirmek için herhangi bir yol sunmayın. Salt okunur alanlar veya salt get özellikleri kuraldır. Tüketici değeri değiştirmek isterse, eskisinin değerlerine dayalı olarak istediği değişikliklerle yeni bir nesne oluşturabilir veya aynı şeyi yapacak bir yöntem çağırabilir. Bu onları yapılarınızın tek bir örneğini bölünmez ve diğerlerinden farklı (ama muhtemelen eşit) kavramsal bir "değer" olarak ele almaya zorlar. Türünüz tarafından saklanan bir "değer" üzerinde işlem yaparlarsa, başlangıç ​​değerlerinden farklı, ancak yine de karşılaştırılabilir ve/veya anlamsal olarak eşit olan yeni bir "değer" alırlar.

İyi bir örnek için DateTime türüne bakın. Bir DateTime örneğinin alanlarını doğrudan atayamazsınız; yeni bir tane oluşturmalı veya mevcut yöntemde yeni bir örnek oluşturacak bir yöntemi çağırmalısınız. Bunun nedeni, tarih ve saatin 5 sayısı gibi bir "değer" olması ve 5 sayısında yapılacak bir değişikliğin 5 olmayan yeni bir değerle sonuçlanmasıdır. 5 + 1 = 6, 5'in artık 6 olduğu anlamına gelmez. çünkü ona 1 ekledin. DateTimes aynı şekilde çalışır; 12:00 "olmaz" 12:01 bir dakika eklerseniz, bunun yerine 12: 00'dan farklı yeni bir 12:01 değeri elde edersiniz. Bu, türünüz için mantıklı bir durumsa (.NET'te yerleşik olmayan iyi kavramsal örnekler Para, Mesafe, Ağırlık ve işlemlerin değerin tüm bölümlerini dikkate alması gereken bir UOM'nin diğer miktarlarıdır), daha sonra bir yapı kullanın ve buna göre tasarlayın. Bir nesnenin alt öğelerinin bağımsız olarak değiştirilebilir olması gereken diğer çoğu durumda, bir sınıf kullanın.

170
KeithS

Cevap: "Saf veri yapıları için bir yapı kullanın ve işlemleri olan nesneler için bir sınıf kullanın" kesinlikle yanlış IMO. Bir yapı çok sayıda özelliğe sahipse, sınıf neredeyse her zaman daha uygundur. Microsoft genellikle, verimlilik açısından, türünüz 16 bayttan büyükse bir sınıf olması gerektiğini söyler.

https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes

14
bytedev

class ve struct arasındaki en önemli fark, aşağıdaki durumda olan şeydir:

 Şey11 = yeni Şey (); 
 Şey1.somePropertyOrField = 5; 
 Şey şey2 = şey1; 
 Şey2.somePropertyOrField = 9; 

Son ifadenin thing1.somePropertyOrField Üzerindeki etkisi ne olmalıdır? Thing bir yapı ve somePropertyOrField açıkta kalan bir ortak alansa, thing1 Ve thing2 Nesneleri birbirinden "ayrılacaktır". ifadesi thing1 'u etkilemez. Thing bir sınıfsa, thing1 Ve thing2 Birbirine eklenir ve böylece son ifade thing1.somePropertyOrField Yazar. Eski anlambilimin daha anlamlı olacağı durumlarda bir yapı kullanılmalı ve son anlambilimin daha anlamlı olacağı durumlarda bir sınıf kullanılmalıdır.

Bazı insanlar değişebilir bir şey yapma arzusunun sınıf olması lehine bir argüman olduğunu tavsiye ederken, bunun tersini öneririm: bazı verileri tutmak için var olan bir şey değişebilir olacak ve örneklerin herhangi bir şeye bağlı olup olmadığı aşikar olmayacaksa, örneklerin başka bir şeye bağlı olmadığını açıkça belirtmek için, bir şey (açıkta kalan alanlarla) yapı olmalıdır.

Örneğin, ifadeleri göz önünde bulundurun:

 Kişi somePerson = myPeople.GetPerson ("123-45-6789"); [.____. Some somePerson.Name = "Mary Johnson"; // "Mary Smith" olmuştu 

İkinci ifade myPeople içinde saklanan bilgileri değiştirecek mi? Person açıkta kalan bir alansa, ve bunun maruz kalan bir alan yapısı olmasının bariz bir sonucu olmayacağı gerçeği; Person bir yapı ise ve myPeople 'ı güncellemek isterse, açıkça myPeople.UpdatePerson("123-45-6789", somePerson) gibi bir şey yapmak gerekir. Ancak Person bir sınıfsa, yukarıdaki kodun MyPeople içeriğini asla güncellemeyeceğini, her zaman güncelleyeceğini veya bazen güncelleyip güncellemeyeceğini belirlemek çok daha zor olabilir.

Yapıların "değişmez" olması gerektiği fikrine gelince, genel olarak aynı fikirde değilim. "Değişmez" yapılar (değişmezlerin bir kurucuda uygulandığı) için geçerli kullanım durumları vardır, ancak değişikliklerin herhangi bir bölümünde garip, israflı ve hataya neden olacak şekilde hataya neden olmaktan çok daha uygun olması için tüm bir yapının yeniden yazılması gerekir alanları doğrudan. Örneğin, alanları, diğerleri arasında PhoneNumber ve AreaCode olan bir Exchange yapısı düşünün ve birinin List<PhoneNumber> Olduğunu varsayalım. Aşağıdakilerin etkisi oldukça açık olmalıdır:

(int i = 0; i <myList.Count; i ++) için 
 {
 PhoneNumber theNumber = myList [i]; 
 eğer (theNumber.AreaCode = = "312") 
 {
 String newExchange = ""; 
 İf (new312to708Exchanges.TryGetValue (theNumber.Exchange), newExchange üzerinden) [.____. {
 theNumber.AreaCode = "708"; 
 theNumber.Exchange = newExchange; 
 myList [i] = theNumber; 
} 
} [.____] } 

Yukarıdaki koddaki hiçbir şeyin PhoneNumber içindeki AreaCode ve Exchange dışındaki alanları bilmediğini veya bunlarla ilgilenmediğini unutmayın. PhoneNumber "değişmez" bir yapı olsaydı, her alan için withXX yöntemi sağlaması gerekirdi; bu, iletilen içeriyi tutan yeni bir yapı örneği döndürür değeri, aksi takdirde yukarıdaki gibi kodun yapıdaki her alan hakkında bilgi sahibi olması gerekir. Tam olarak çekici değil.

BTW, yapıların değiştirilebilir türlere makul şekilde atıfta bulunabileceği en az iki durum vardır.

  1. Yapının semantiği, nesnenin özelliklerini tutmak için bir kısayol yerine söz konusu nesnenin kimliğini sakladığını gösterir. Örneğin, bir 'KeyValuePair' belirli düğmelerin kimliklerini tutar, ancak bu düğmenin konumları, vurgulama durumları vb. Hakkında kalıcı bir bilgi tutması beklenmez.
  2. Yapı, o nesneye tek referansı tuttuğunu, başka hiç kimsenin referans alamayacağını ve bu nesneye gerçekleştirilecek herhangi bir mutasyonun, herhangi bir yerde saklanmadan önce yapılmış olacağını bilir.

Önceki senaryoda, nesnenin KİMLİĞİ değişmez olacaktır; ikincisinde, iç içe nesnenin sınıfı değişmezliği zorlamayabilir, ancak referansı tutan yapı olacaktır.

10
supercat

Sadece bir yönteme ihtiyaç duyulduğunda yapıları kullanma eğilimindeyim, böylece bir sınıfın çok "ağır" görünmesini sağlıyorum. Böylece bazen yapıları hafif nesneler olarak görüyorum. DİKKAT: Bu her zaman işe yaramaz ve her zaman yapılacak en iyi şey değildir, bu yüzden gerçekten duruma bağlıdır!

EKSTRA KAYNAKLAR

Daha resmi bir açıklama için MSDN diyor ki: http://msdn.Microsoft.com/en-us/library/ms229017.aspx Bu bağlantı yukarıda bahsettiğimi doğrular, yapılar yalnızca bir konut için kullanılmalıdır az miktarda veri.

7
rrazd

Saf veri yapıları için bir yapı ve işlemleri olan nesneler için bir sınıf kullanın.

Veri yapınızın erişim kontrolü gerektirmiyorsa ve get/set dışında özel bir işlemi yoksa, bir yapı kullanın. Bu, tüm bu yapının veri için bir kap olduğunu açıkça ortaya koymaktadır.

Yapınızda, yapıyı daha karmaşık bir şekilde değiştiren işlemler varsa, bir sınıf kullanın. Bir sınıf, yöntemlere argümanlar yoluyla diğer sınıflarla da etkileşime girebilir.

Bunu bir tuğla ve bir uçak arasındaki fark olarak düşünün. Tuğla bir yapıdır; uzunluk, genişlik ve yüksekliğe sahiptir. Fazla bir şey yapamaz. Bir uçağın, kontrol yüzeylerini hareket ettirme ve motor itme kuvvetini değiştirme gibi bir şekilde onu değiştiren birden fazla işlemi olabilir. Sınıf olarak daha iyi olurdu.

2
Michael K