it-swarm-tr.com

Sınıf yerine yapıyı ne zaman kullanmalıyım?

MSDN, hafif nesnelere ihtiyacınız olduğunda yapı kullanmanız gerektiğini söylüyor. Bir sınıftan bir yapı tercih edildiğinde başka senaryolar var mı?

Bazı insanlar bunu unutmuş olabilir:

  1. yapıların yöntemleri olabilir.
  2. yapılar miras alınamaz.

Yapılar ve sınıflar arasındaki teknik farklılıkları anlıyorum, yapıyı kullanmak için ne zaman için iyi bir hisim yok.

295
Esteban Araya

MSDN'nin cevabı şöyle: Sınıflar ve Yapılar Arasında Seçim Yapma .

Temel olarak, bu sayfa size 4 maddelik bir kontrol listesi sunar ve türünüz tüm kriterleri karşılamadıkça bir sınıf kullanmanız gerektiğini söyler.

Tür aşağıdaki özelliklerin tümüne sahip olmadıkça bir yapı tanımlamayın:

  • Mantıksal olarak, ilkel türlere benzer şekilde tek bir değeri temsil eder (tamsayı, çift vb.).
  • 16 bayttan küçük örneklem büyüklüğüne sahiptir.
  • Değişmez.
  • Sık sık kutulanması gerekmez.
293
OwenP

En önemli yönü düşündüğüm önceki cevabın hiçbirini okumadığıma şaşırdım:

Ben kimliği olmayan bir tür istediğimde yapıyı kullanıyorum. Örneğin bir 3B nokta:

public struct ThreeDimensionalPoint
{
    public readonly int X, Y, Z;
    public ThreeDimensionalPoint(int x, int y, int z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public override string ToString()
    {
        return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")";
    }

    public override int GetHashCode()
    {
        return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is ThreeDimensionalPoint))
            return false;
        ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj;
        return this == other;
    }

    public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z;
    }

    public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return !(p1 == p2);
    }
}

Bu yapının iki örneğine sahipseniz, bellekteki tek bir veri parçası olup olmadıklarına aldırmazsınız. Sadece sahip oldukları değerleri umursuyorsunuz.

53
Andrei Rînea

Bill Wagner'in "efektif c #" adlı kitabında bir bölümü var ( http://www.Amazon.com/Effective-Specific-Ways-Improve-Your/dp/032124566 ). Aşağıdaki prensibi kullanarak son bulur:

  1. Veri depolama türünün ana sorumluluğu var mı?
  2. Genel arayüzü tamamen veri üyelerine erişen veya bunları değiştiren özelliklerle tanımlanıyor mu?
  3. Yazınızın asla alt sınıfları olmayacağından emin misiniz?
  4. Tipinizin asla polimorfik olarak tedavi edilmeyeceğinden emin misiniz?

4 soruya da 'evet' cevabını verirseniz: bir yapı kullanın. Aksi takdirde, bir sınıf kullanın.

27
Bart Gijssens

Referans türü yerine değer türü anlambilimi istediğinizde bir yapı kullanın. Yapılar değerine göre kopyalanmıştır, bu yüzden dikkatli olun!

Ayrıca önceki sorulara bakınız.

.NET'te struct ve class arasındaki fark nedir?

15
Simon Steele

Ne zaman yapıları kullanırdım:

  1. bir nesnenin sadece okunması gerekiyordu (kopyalanacak bir yapıyı her geçirdiğinizde/atadığınızda). Salt okunur işlemlere gelince salt okunur nesneler harikadır çünkü çoğu durumda kilitlemeyi gerektirmezler.

  2. bir nesne küçük ve kısa ömürlüdür. Böyle bir durumda, nesnenin yönetilen yığına koymaktan çok daha verimli olan yığına tahsis edilme olasılığı yüksektir. Dahası, nesne tarafından ayrılan hafıza, kapsamı dışına çıkar çıkmaz serbest bırakılacaktır. Başka bir deyişle, Çöp Toplayıcı için daha az iş vardır ve bellek daha verimli kullanılır.

11
Pawel Pabich

Aşağıdaki durumlarda bir sınıf kullanın:

  • Kimliği önemlidir. Yapılar, değere göre bir yönteme aktarıldığında örtük olarak kopyalanır.
  • Geniş bir bellek alanı kaplayacak.
  • Alanlarının başlatıcıya ihtiyacı var.
  • Bir temel sınıftan miras almanız gerekir.
  • Polimorfik davranışa ihtiyacınız var;

Aşağıdaki durumlarda bir yapı kullanın:

  • İlkel bir tür (int, long, byte, vs.) gibi davranır.
  • Küçük bir bellek ayak izi olmalı.
  • Bir yapının değerine göre aktarılmasını gerektiren bir P/Invoke yöntemini çağırıyorsunuz.
  • Çöp toplamanın uygulama performansı üzerindeki etkisini azaltmanız gerekir.
  • Alanlarının yalnızca varsayılan değerleriyle başlatılması gerekir. Bu değer sayısal türler için sıfır, Boolean türler için false ve başvuru türler için null olur.
    • C # 6.0'da yapıların, yapı alanlarını varsayılan olmayan değerlere sıfırlamak için kullanılabilecek varsayılan bir kurucuya sahip olabileceğini unutmayın.
  • Bir temel sınıftan (tüm yapıların devraldığı ValueType dışında) miras almanıza gerek yoktur.
  • Polimorfik davranışa ihtiyacınız yoktur.

Bir yöntemi bir yöntem çağrısından geri almak için birkaç değeri bir araya getirmek istediğimde her zaman bir yapı kullandım, ancak bu değerleri okuduktan sonra herhangi bir şey için kullanmam gerekmeyecek. Aynı şeyleri temiz tutmanın bir yolu olarak. Bir yapıdaki şeyleri "atılabilir" ve sınıftaki şeyleri daha kullanışlı ve "işlevsel" olarak görme eğilimindeyim

5
Ryan Skarin

Eğer bir varlık değişmez olacaksa, bir yapının mı yoksa bir sınıf mı kullanılacağı sorusu genellikle anlambilimden ziyade performanstan biri olacaktır. 32/64-bit bir sistemde, sınıf referansları, sınıftaki bilgi miktarından bağımsız olarak saklamak için 4/8 bayt gerektirir; bir sınıf referansının kopyalanması 4/8 bayt kopyalanacaktır. Öte yandan, her farklı sınıf örneği, elinde tuttuğu bilgilere ve referansların bellek maliyetine ek olarak 8/16 baytlık ek yüke sahip olacaktır. Birinin, her biri dört adet 32 ​​bit tam sayı tutan 500 varlıktan oluşan bir dizi istediğini varsayalım. Varlık bir yapı tipi ise, 500 varlığın hepsinin aynı, hepsi farklı veya aralarında bir yerde olmasına bakılmaksızın dizi, 8.000 bayt gerektirir. Varlık bir sınıf tipi ise, 500 referans dizisi 4.000 bayt alacaktır. Bütün bunlar farklı nesnelere işaret ediyorsa, nesneler her biri için ilave 24 bayt (her biri 500 için 12.000 bayt), toplam 16.000 bayt - bir yapı türünün depolama maliyetinin iki katıdır. Öte yandan, kodun bir nesne örneği oluşturup ardından tüm 500 dizi yuvasına bir başvuru kopyaladığında, toplam maliyet bu örnek için 24 bayt ve dizi için 4.000 olur - toplam 4.024 bayt. Büyük bir tasarruf. Sonuncusu gibi birkaç durum da işe yarayabilir, ancak bazı durumlarda bu tür paylaşımları değerli kılmak için yeterli dizi yuvasına bazı referanslar kopyalamak mümkün olabilir.

Varlığın değişken olması gerekiyorsa, bir sınıf mı yoksa yapı mı kullanılacağı sorusu bazı yönlerden daha kolaydır. "Şey" in, x adında bir tamsayı alanı olan bir yapı veya sınıf olduğunu varsayalım ve biri şu kodu yapar:

 Şey t1, t2; 
 ... 
 T2 = t1; 
 T2.x = 5; 

İkinci ifadenin t1.x'i etkilemesini istiyor mu?

Şey bir sınıf tipi ise, t1 ve t2 eşdeğer olacaktır, yani t1.x ve t2.x de eşdeğer olacaktır. Böylece, ikinci ifade t1.x'i etkileyecektir. Şey bir yapı tipi ise, t1 ve t2 farklı durumlar olacaktır, yani t1.x ve t2.x farklı tam sayılara atıfta bulunacaktır. Böylece, ikinci ifade t1.x'i etkilemeyecektir.

Değişken yapılar ve değişken sınıflar temelde farklı davranışlara sahiptir, ancak .net yapısal mutasyonların kullanımında bazı tuhaflıklar vardır. Biri değer türü davranışını istiyorsa ("t2 = t1", t1 ve t2'yi ayrı durumlar olarak bırakırken verileri t1'den t2'ye kopyalayacağı anlamına gelirse) yapı. Biri değer tipi anlambilim ister ancak .net'in tuhaflıkları, birinin uygulamasında değer tipi anlambiliminde bozulmaya yol açarsa, bir sınıf ve mırıltı kullanın.

4
supercat

Ek olarak yukarıdaki mükemmel cevaplar:

Yapılar değer türleridir.

Asla ayarlanamazlar Hiçbir şey.

Yapı ayarı = Hiçbir şey, tüm değer türlerini varsayılan değerlerine ayarlayacaktır.

3
George Filippakos

gerçekten davranışa ihtiyacınız olmadığında, basit bir dizi veya sözlükten daha fazla yapıya ihtiyacınız olduğunda.

Takip et Genel olarak yapıları böyle düşünüyorum. Yöntemleri olabileceğini biliyorum, ama bu genel zihinsel ayrımı korumayı seviyorum.

2
Jim Deville

@Simon'un dediği gibi, structs "value-type" anlambilimi sağlar, bu yüzden yerleşik bir veri tipine benzer bir davranışa ihtiyacınız varsa, bir struct kullanın. Yapılar kopyayla geçirildiğinden, boyutlarının küçük, yaklaşık 16 bayt olduğundan emin olmak istersiniz.

2
Scott Dorman

Yapılar Yığın üzerinde değil Yığın üzerinde olduğundan, bunlar güvenlidir ve bunlar güvenlidir ve aktarım nesnesi desenini uygularken kullanılmaları gerekir, Yığın üzerinde hiçbir zaman uçucu olan nesneleri kullanmak istemezsiniz, bu durumda Çağrı Yığını'nı kullanmak istiyorsanız, bu yapıyı kullanmak için temel bir durumdur, buradaki cevaplar beni şaşırttı.

1
Jack

Hmm ...

Yapıların ve sınıfların kullanımı için/aleyhinde bir argüman olarak çöp toplama kullanmazdım. Yönetilen yığın bir yığın gibi çalışır - bir nesne oluşturmak onu hemen yığının üstüne koymak kadar hızlı bir şekilde yerleştirir. Ek olarak, eğer bir nesne kısa ömürlü ise ve bir GC döngüsünde hayatta kalmıyorsa, GC sadece erişilebilir durumda olan bellekte çalıştığından, tahsisat serbesttir. (Arama MSDN, .NET bellek yönetimi hakkında bir dizi makale var, onlar için Dig gitmek için sadece tembelim).

Çoğu zaman bir yapı kullandığımda, bunu yapmak için kendimi tekmeleyeceğim, çünkü daha sonra referans anlambilimine sahip olmanın işleri daha da kolaylaştıracağını keşfettim.

Neyse, yukarıda yayınlanan MSDN makalesinde bu dört nokta iyi bir rehber gibi görünüyor.

1
KG