Getiriciler, ayarlayıcılar ve özelliklerle ilgili en iyi uygulamalar. Java ve C #


93

Şu anda bir C # dersi alıyorum ve bir şeyler yapmanın en iyi yolunu bulmaya çalışıyorum. Java geçmişinden geliyorum ve bu nedenle yalnızca Java'nın en iyi uygulamalarına aşinayım; Ben bir C # acemiyim!

Java'da özel mülküm varsa, bunu yapıyorum;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

C # 'da bunu yapmanın birçok yolu olduğunu görüyorum.

Java gibi yapabilirim:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Ya da şu şekilde yapabilirim:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

Veya:

public string Name { get; set; }

Hangisini kullanmalıyım ve her yaklaşımla ilgili uyarılar veya incelikler nelerdir? Sınıflar oluştururken, Java'dan bildiğim genel en iyi uygulamaları takip ediyorum (özellikle Etkili Java'yı okuyorum). Örneğin, ben değişmezliği destekliyorum (yalnızca gerekli olduğunda ayarlayıcılar sağlamak). Bu uygulamaların, C # 'da ayarlayıcılar ve alıcılar sağlamanın çeşitli yollarına nasıl uyduğunu merak ediyorum; esasen, Java dünyasındaki en iyi uygulamaları C # 'a nasıl çevirebilirim?

DÜZENLE

Bunu Jon Skeet'in cevabına yorum olarak gönderiyordum ama sonra uzadı:

Önemsiz olmayan bir özelliğe ne dersiniz (yani, önemli işleme ve doğrulama ile)? Hala bir kamu malı yoluyla ancak kapsüllü mantığı ile korumasız kalmasına neden getve set? Bunu neden adanmış ayarlayıcı ve alıcı yöntemlerine (ilişkili işleme ve doğrulama mantığıyla) sahip olmak yerine yapmalıyım / yapmalıyım?

Yanıtlar:


89

Ön-C # 6

Önemsiz bir mülk için bunların sonunu kullanırdım. Bunu halka açık olarak adlandıracağımı unutmayınHem alıcılar hem de ayarlayıcılar halka açık olduğu için malı olarak .

Değişmezlik, otomatik olarak uygulanan özelliklere sahip bir parça sıkıntıdır - yalnızca alıcıya sahip bir otomatik özellik yazamazsınız; gelebileceğin en yakın şey:

public string Foo { get; private set; }

bu gerçekten değişmez değil ... sadece sınıfınızın dışında değişmez. Bu nedenle, bunun yerine gerçek bir salt okunur özelliği kullanmak isteyebilirsiniz :

private readonly string foo;
public string Foo { get { return foo; } }

Kesinlikle yazmak istemiyorum getName()ve setName(). Gelen bazı durumlarda bunların pahalı olabilir, özellikle yazma Get / Set yöntemlerden ziyade kullanarak özelliklerine mantıklı ve bunu vurgulamak istiyoruz. Bununla birlikte, yöntemler için PascalCase'in .NET adlandırma kuralını takip etmek istersiniz ve bunun gibi önemsiz bir özelliğin normal yöntemlerle uygulanmasını istemezsiniz - burada bir özellik çok daha deyimseldir.

C # 6

Yaşasın, nihayet salt okunur otomatik olarak uygulanan özelliklere sahibiz:

// This can only be assigned to within the constructor
public string Foo { get; }

Benzer şekilde, bazı işler yapması gereken salt okunur özellikler için üye gövdeli özellikleri kullanabilirsiniz:

public double Area => height * width;

6
Daha kesin: herhangi bir kod yeniden gözden geçirme, java yolunun geçerli dil VE (!) Çalışma zamanı yapılarını atlayan ve mülk olarak özelliğin kullanımını ortadan kaldıran (yani, object.property = "değer") bir hack olduğunu gösterecektir. Bazı takımlarda bu tutum hakkında güzel bir konuşma ile sonuçlanırdı - kıdeme bağlı olarak, bu tutumu bir rakipte kullanmak için bir teşvikle birleştirilirdi. Cidden, DİLLE MÜCADELE ETMEYİN. Özellikle java "yolu", emlak desteği için dili değiştirmemek için seçilmiş bir hack olduğu için.
TomTom

1
Sanırım iyi haber, cevabım bahsettiğiniz hiçbir şeyle çelişmiyor gibi görünüyor. Kötü haber, parmakların benimkilerden çok daha hızlı. Harika bilgiler ve eklenen ayrıntılar için teşekkürler.
jeremyalan

4
Düzenlemenize cevap vermek için: get / set yöntemlerini, içinde istediğiniz miktarda mantıkla kullanabilirsiniz. Bunu özellikle doğrulama için sık sık yapıyoruz. Bununla birlikte, en iyi uygulama, özelliklerde çok fazla yavaş mantık (örneğin, veritabanı erişimi), tehlikeli mantık (istisna atma) veya mutasyon (çok sayıda durumu değiştirme) içermemektir. Bir mülkün aşağı yukarı basit bir durum gibi davranması beklenir. Daha fazlası, bunun yerine bir işlev kullanılarak belirtilmelidir.
CodexArcanum

2
@rtindru: Evet, bunun farkındayım. Salt okunur bir özellik yazmak tamamen mümkündür. Ancak , ayarlı bir erişimci olmadan otomatik olarak uygulanan bir özelliği bildiremezsiniz .
Jon Skeet


17

İhtiyacınız olan tek şey, bazı verileri depolamak için bir değişkendir:

public string Name { get; set; }

Salt okunur görünmesini mi istiyorsunuz?

public string Name { get; private set; }

Ya da daha iyisi ...

private readonly string _name;

...

public string Name { get { return _name; } }

Mülkü atamadan önce bazı değer kontrolü yapmak ister misiniz?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

Genel olarak, GetXyz () ve SetXyz () yalnızca belirli durumlarda kullanılır ve doğru hissettiğinde bağırsağınızı kullanmanız gerekir. Genel olarak, çoğu get / set özelliğinin çok fazla mantık içermemesini ve varsa çok az sayıda beklenmedik yan etkiye sahip olmasını beklediğimi söyleyebilirim. Bir özellik değerini okumak, talep ettiğim nesneyi oluşturmak için bir hizmeti çağırmayı veya bir kullanıcıdan girdi almayı gerektiriyorsa, onu bir yönteme sarar ve buna benzer bir şey BuildXyz()çağırırdım GetXyz().


2
Özelliklerde istisnalar atmam, bu tür belirli davranışları belirtmek için sözleşmeli yöntem belirleyicileri kullanmayı tercih ederim. Bir özellik int türünde ise, her int'in uygun olmasını beklerim. İstisna atma gerektiren bir şey bence basit değil , INotifyPropertyChanged türü çağrılar bana göre bu satırda daha fazla.
flindeberg

3
özel ayarlayıcı! = değişmez
piedar

piedar haklı. Özel bir ayarlayıcı, atamalar yapamayacağım anlamına gelir, ancak yine de kullanabilirim myList.Add(), örneğin (nesne değişime açık olduğu sürece, değiştirilebilir).

1
@flindeberg Üzgünüm ama katılmıyorum ... Bir Min/ Maxdeğerine sahip bir ilerleme çubuğunuz varsa ne olacak ? Bunu sağlamak ister misin Max > Min? Bir SetRange(Min, Max)mantıklı olabilir ama o zaman bu değerleri nasıl geri okuyacaksınız? Salt okunur Min / Maks özellikleri? Geçersiz girdi için istisnalar atmak, bunu halletmenin en temiz yolu gibi görünüyor.
Temel

12

Get / set yöntemlerini değil, C # 'teki özellikleri kullanın. Size kolaylık sağlamak için oradalar ve deyimsel.

İki C # örneğinize gelince, biri diğeri için basitçe sözdizimsel şekerdir. Tek ihtiyacınız olan şey bir örnek değişkeni etrafında basit bir sarmalayıcıysa, alıcıya ve / veya ayarlayıcıya mantık eklemeniz gerektiğinde tam sürümü kullanın.


5

C # 'da get ve / veya set için özel alanları göstermeye yönelik özellikleri tercih edin. Bahsettiğiniz thie formu, get ve set'in sizin için otomatik olarak gizli bir pivot destek alanı oluşturduğu bir otomatik mülkiyettir.

Mümkün olduğunda otomatik özellikleri tercih ederim, ancak C # 'da asla set / get yöntem çifti yapmamalısınız.


5
public string Name { get; set; }

Bu, otomatik olarak uygulanan bir özelliktir ve teknik olarak normal bir mülkle aynıdır. Derleme sırasında bir destek alanı oluşturulacaktır.

Tüm özellikler sonunda işlevlere dönüştürülür, bu nedenle sonunda derlenen gerçek uygulama Java'da alışkın olduğunuzla aynıdır.

Destek alanında belirli işlemler yapmanız gerekmediğinde otomatik olarak uygulanan özellikleri kullanın. Aksi takdirde sıradan bir mülk kullanın. İşlemin yan etkileri olduğunda veya hesaplama açısından pahalı olduğunda get ve set işlevlerini kullanın, aksi takdirde özellikleri kullanın.


4

C # 'da hangi yolu seçerseniz seçin, sonuç aynıdır. Ayrı alıcı ve ayarlayıcı yöntemlerine sahip bir destek değişkeni alacaksınız. Özellikleri kullanarak en iyi uygulamaları takip ediyorsunuz ve bu yüzden ne kadar ayrıntılı bilgi almak istediğinize bağlı.

Kişisel olarak public string Name { get; set; }, en az yer kapladıkları için son sürüm olan otomatik özellikleri seçerdim . Ve doğrulama gibi bir şey eklemeniz gerekirse, bunları gelecekte her zaman genişletebilirsiniz.


4

Mümkün olduğunda string Name { get; set; }kısa ve kolay okunabilir olduğu için halkı tercih ederim . Ancak, bunun gerekli olduğu zamanlar olabilir.

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

2
bu tür bir şeyin ne zaman ve neden gerekli olduğunu açıklayabilir misin?
Jesper

Şu anda 2. sürümü kullanmak için bir neden düşünemiyorum. Sadece 'bunun gerekli olduğu zamanlar olabilir' dedim. Olumlu olmadıkça kesinlik kullanmaktan nefret ederim.
SquidScareMe

3
Ve bu neden 'lolz'? Olumlu oy vererek yorum için desteğinizi gösterebilirsiniz.
SquidScareMe

1
Açık bir destek alanı kullanmanın bir nedeni, değere karşı atomik / iş parçacığı güvenli işlemler yapmak istediğiniz
Basic

4

C # tercih edilen bir yol özellikleri aracılığıyla yerine olan getX()ve setX()yöntemler. Ayrıca, C # 'nin özelliklerin hem bir get hem de bir küme olmasını gerektirmediğini unutmayın; yalnızca-alma özelliklerine ve yalnızca ayar özelliklerine sahip olabilirsiniz.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}

4

Önce yazdıklarınızı açıklamama izin verin:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Bu yapı günümüzde de kullanılmaktadır, ancak bazı ekstra işlevler yapmak istiyorsanız en uygun olanıdır, örneğin bir değer belirlendiğinde onu büyük harf yapmak için ayrıştırabilir ve dahili kullanımı değiştirmek için özel üyeye kaydedebilirsiniz.

.Net framework 3.0 ile

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

C # sana daha iyi şanslar dilerim


3

Belirtildiği gibi, tüm bu yaklaşımlar aynı sonucu doğurur. En önemli şey, bir kongre seçip ona bağlı kalmanızdır. Son iki özellik örneğini kullanmayı tercih ederim.


2

Buradaki yanıtların çoğu gibi Otomatik özellikleri kullanın. Sezgisel, daha az kod satırı ve daha temiz. Sınıfınızı serileştirmeniz gerekiyorsa, sınıfı [Serializable]/ [DataConract]özelliği ile işaretleyin . Ve kullanıyorsanız [DataContract]üyeyi işaretleyin

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

Özel veya genel ayarlayıcı tercihinize bağlıdır.

Ayrıca, otomatik özelliklerin hem alıcılar hem de ayarlayıcılar (genel veya özel) gerektirdiğini unutmayın.

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

Alternatif olarak, yalnızca almak / ayarlamak istiyorsanız, kendiniz bir destek alanı oluşturun


0

Hangisini kullanmalıyım ve her yaklaşımla ilgili uyarılar veya incelikler nelerdir?

Özelliklerle devam ederken, henüz bahsedilmeyen bir uyarı vardır: Özelliklerle alıcılarınızın veya ayarlayıcılarınızın herhangi bir parametrizasyonuna sahip olamazsınız.

Örneğin, bir liste öğelerini geri almak ve aynı zamanda bir filtre uygulamak istediğinizi düşünün. Get-method ile şöyle bir şey yazabilirsiniz:

obj.getItems(filter);

Bunun aksine, bir mülkle önce tüm öğeleri iade etmek zorunda kalırsınız

obj.items

ve ardından bir sonraki adımda filtreyi uygulayın veya farklı kriterlere göre filtrelenmiş öğeleri ortaya çıkaran özel özellikler eklemeniz gerekir, bu da kısa sürede API'nizi şişirir:

obj.itemsFilteredByX
obj.itemsFilteredByY

Bazen bir sıkıntı olabilir, örneğin bir mülkle başladığınızda obj.itemsve daha sonra alıcı veya ayarlayıcı parametrelendirmenin gerekli olduğunu veya sınıf API kullanıcısı için işleri kolaylaştıracağını keşfettiğinizde olabilir. Şimdi API'nizi yeniden yazmanız ve kodunuzda bu özelliğe erişen tüm bu yerleri değiştirmeniz veya alternatif bir çözüm bulmanız gerekir. Bunun aksine, bir get-yöntemi ile, örneğin obj.getItems(), yönteminizin imzasını isteğe bağlı bir "yapılandırma" nesnesini kabul edecek şekilde genişletebilirsiniz , örneğin , yönteminizi obj.getItems(options)çağıran tüm bu yerleri yeniden yazmak zorunda kalmadan.

Bununla birlikte, C # içerisindeki (otomatik uygulanan) özellikler, çoğu zaman parametreleştirmeye ihtiyaç duyulmayabileceğinden, hala çok yararlı kısayollardır (burada bahsedilen çeşitli nedenlerden dolayı) - ancak bu uyarı geçerli.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.