Yalnızca alıcıya ait bir özelliği geçersiz kılmak ve ayarlayıcı eklemek neden imkansızdır? [kapalı]


141

Aşağıdaki C # koduna neden izin verilmiyor:

public abstract class BaseClass
{
    public abstract int Bar { get;}
}

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get { return 0; }
        set {}
    }
}

CS0546 'ConcreteClass.Bar.set': 'BaseClass.Bar' geçersiz kılınabilir bir ayar erişimcisi olmadığından geçersiz kılınamıyor


9
StackOverflow, Microsoft adına soruları yanıtlamak için sınırlı yeteneğe sahiptir. Sorunuzu yeniden yazmayı düşünün.
Jay Bazuzi

1
Bu arada, .net'teki bu sinir bozucu davranış için basit bir çözüm, Fookorumalı bir soyut GetFooyöntemi sarmaktan başka bir şey yapmayan sanal olmayan salt okunur bir özelliğe sahip olmaktır . Soyut türetilmiş bir tür, özelliği, GetFoosoyut bir yöntemle birlikte yukarıda bahsedilen yöntemi saran hiçbir şey yapmayan sanal olmayan bir okuma-yazma özelliğiyle gölgeleyebilir SetFoo; somut olarak türetilmiş bir tür yukarıdakileri yapabilir ancak GetFoove için tanımları sağlayabilir SetFoo.
supercat

1
Sadece ateşe yakıt eklemek için C ++ / CLI buna izin verir.
ymett

1
Bir özelliği geçersiz kılarken erişimci ekleme yeteneği, C #' ın gelecekteki bir sürümünde değişiklik olarak önerilmiştir ( github.com/dotnet/roslyn/issues/9482 ).
Brandon Bonds

1
Açıkçası, bu soru genellikle şu kalıbı kullanan mevcut bir kütüphane türünü genişletmeniz gerektiğinde ortaya çıkar: salt okunur temel sınıf, türetilebilir değişken sınıf. Kütüphane zaten kötü bir model (WPF gibi) kullandığından, bu kötü modelin geçici olarak çözülmesi gereği ortaya çıkar. Anlatım, gün sonunda bir geçici çözüm uygulamak zorunda kalmamaktan kurtulamaz.
rwong

Yanıtlar:


-4

Çünkü Baseclass'ın yazarı Bar'ın salt okunur bir özellik olması gerektiğini açıkça ilan etti. Türevlerin bu sözleşmeyi kırması ve okuma-yazma yapması mantıklı değildir.

Bu konuda Microsoft ile beraberim.
Diyelim ki Baseclass türevine karşı kod yazması söylenen yeni bir programcıyım. (Baseclass açıkça bir get özelliği olduğunu belirttiği için) Bar için yazılamaz olduğunu varsayan bir şey yazmak. Şimdi türetmenizle kodum kırılabilir. Örneğin

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

Bar BaseClass arabirimine göre ayarlanamadığından, BarProvider önbelleğe almanın güvenli bir şey olduğunu varsayar - Bar değiştirilemez. Ancak bir türevde set mümkün olsaydı, birisi _source nesnesinin Bar özelliğini harici olarak değiştirdiğinde bu sınıf eski değerler sunabilir. ' Açık olun, gizli şeyler yapmaktan ve şaşırtıcı insanlar yapmaktan kaçınmak '

Güncelleme : Ilya Ryzhenkov soruyor 'Neden arayüzler aynı kurallara göre oynamıyor?' Hmm .. bu düşündüğümde daha çamurlu oluyor.
Arayüz, 'bir uygulamanın Bar adında bir okuma özelliğine sahip olmasını bekleyin' yazan bir sözleşmedir. Şahsen ben bir Arayüz görürsem bu salt okunur varsayımını yapmam daha az olasıdır. Bir arabirimde bir salt okunur özelliği gördüğümde, 'Herhangi bir uygulama bu öznitelik Çubuğu'nu açığa çıkaracak' olarak okudum ... 'Bar salt okunur bir özellik' olarak tıkladığı bir temel sınıfta. Tabii teknik olarak sözleşmeyi bozmuyorsunuz .. daha fazlasını yapıyorsunuz. Yani bir anlamda haklısın .. 'Yanlış anlamaların ortaya çıkmasını mümkün olduğunca zorlaştır' diyerek kapatırdım.


94
Hala ikna olmadım. Açık bir ayarlayıcı olmasa bile, bu özellik her zaman aynı nesneyi döndüreceğini garanti etmez - yine de başka bir yöntemle değiştirilebilir.
ripper234

34
Aynı şey arayüzler için de geçerli değil mi? Arayüzde salt okunur özelliğe ve uygulama türünde okuma / yazma özelliğine sahip olabilirsiniz.
Ilya Ryzhenkov

49
Kabul etmiyorum: temel sınıf sadece özelliği hiçbir ayarlayıcı olarak ilan etti; bu salt okunur özellik ile aynı değildir. "Salt Okunur" sadece anlam olduğunu sen ona atayın. C # içinde hiçbir ilgili garanti yoktur. Her seferinde değişen bir get özelliğine veya ayarlayıcının a attığı bir get / set özelliğine sahip olabilirsiniz InvalidOperationException("This instance is read-only").
Roman Starkov

21
Alıcılara ve belirleyicilere yöntem olarak baktığımda, neden yeni bir yöntem eklemekten daha çok bir "sözleşme ihlali" olması gerektiğini anlamıyorum. Bir temel sınıf sözleşme işlevselliği ne ile hiçbir ilgisi yoktur değil sadece ne mevcut olan mevcut.
Dave Cousineau

37
-1 Bu cevaba katılmıyorum ve yanlış yönlendirici buluyorum. Bir temel sınıf, herhangi bir sınıfın uyması gereken davranışı tanımladığından (Bkz. Liskov'un İkame Prensibi), ancak davranış eklemek için kısıtlamaz (ve olmamalıdır). Bir temel sınıf yalnızca davranışın ne olması gerektiğini tanımlayabilir ve 'davranışın ne olmaması gerektiğini' (yani 'somut seviyelerde yazılabilir olmamalıdır') belirleyemez (ve etmemelidir).
Marcel Valdez Orozco

39

Bence asıl sebep, sözdiziminin bunun başka bir şekilde çalışması için çok açık olmasıdır. Bu kod:

public override int MyProperty { get { ... } set { ... } }

hem getve hem setde geçersiz kılınan oldukça açıktır . setTemel sınıfta hiç yok , bu yüzden derleyici şikayet ediyor. Tıpkı temel sınıfta tanımlı olmayan bir yöntemi geçersiz kılamamanız gibi, bir ayarlayıcıyı da geçersiz kılamazsınız.

Derleyicinin niyetinizi tahmin etmesi ve yalnızca geçersiz kılma yöntemine geçersiz kılma yöntemini (yani bu durumda alıcı) uygulamanız gerektiğini söyleyebilirsiniz, ancak bu C # tasarım ilkelerinden birine aykırıdır - derleyici niyetlerinizi tahmin etmemelidir , çünkü bilmeden yanlış tahmin edebilir.

Aşağıdaki sözdiziminin iyi yapabileceğini düşünüyorum, ancak Eric Lippert'in söylediği gibi, bunun gibi küçük bir özelliği bile uygulamak hala büyük bir çaba ...

public int MyProperty
{
    override get { ... }
    set { ... }
}

veya otomatik olarak uygulanan özellikler için,

public int MyProperty { override get; set; }

19

Bugün aynı problemle karşılaştım ve bence bunu istemek için çok geçerli bir neden var.

Öncelikle, salt get özelliğine sahip olmanın mutlaka salt okunur hale gelmediğini iddia etmek istiyorum. Ben bu arabirim / soyut sınıfın bazı uygulama bu değeri açıkça ayarlamak için kullanıcı / program gerekmez alışkanlık anlamına gelmez "Bu arabirim / abtract bu değeri alabilirsiniz" olarak yorumlayın. Soyut sınıflar, gerekli işlevselliğin bir kısmını uygulama amacına hizmet eder. Devralınan bir sınıfın herhangi bir sözleşmeyi ihlal etmeden belirleyici ekleyememesi için hiçbir neden göremiyorum.

Aşağıda bugün ihtiyacımın basitleştirilmiş bir örneği var. Sadece bu sorunu çözmek için arayüzüme bir ayarlayıcı eklemek zorunda kaldım. Setter eklemenin ve bir SetProp yönteminin eklenmemesinin nedeni, arabirimin belirli bir uygulamasının, Prop için serileştirilmesi için DataContract / DataMember'i kullanmasıdır; serileştirme.

interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

Bunun sadece "2 sentim" bir yazı olduğunu biliyorum, ama orijinal posterle hissediyorum ve bunun iyi bir şey olduğunu rasyonelleştirmeye çalışıyorum, özellikle de doğrudan bir mirastan miras alırken aynı sınırlamaların geçerli olmadığını düşünerek arayüz.

Ayrıca, geçersiz kılma yerine yeni kullanımla ilgili sözler burada geçerli değildir, işe yaramaz ve yapsa bile, arabirim tarafından açıklandığı gibi sanal bir alıcı, istenen sonucu vermez.


2
Veya, benim durumumda, {get; özel set; }. Set özel kaldığı ve sadece türev sınıfıyla ilgili olduğu için herhangi bir sözleşmeyi ihlal etmiyorum.
Machtyn

16

Mümkün

tl; dr - İsterseniz bir ayarlayıcıyla salt get yöntemini geçersiz kılabilirsiniz. Temel olarak sadece:

  1. Bir oluşturun newa hem sahiptir özelliğini getve setaynı adı kullanarak.

  2. Başka bir şey yapmazsanız get, türetilmiş sınıf temel türü üzerinden çağrıldığında eski yöntem çağrılır. Bunu düzeltmek için, yeni yöntemin sonucunu döndürmeye zorlamak üzere eski yöntemde abstractkullanılan bir ara katman ekleyin .overridegetget

Bu, özellikleri temel tanımlarında eksik olsalar bile get/ ile özellikleri geçersiz kılmamızı sağlar set.

Bonus olarak, isterseniz dönüş türünü de değiştirebilirsiniz.

  • Temel tanım get-sadece ise, daha türetilmiş bir dönüş türü kullanabilirsiniz.

  • Temel tanım set-sadece ise, daha az türetilmiş bir dönüş türü olabilirsiniz.

  • Temel tanım zaten get/ setise:

    • Eğer daha türetilmiş dönüş türü kullanabilirsiniz eğer bunu yapmak set-sadece;

    • Eğer daha az türetilmiş dönüş türü kullanabilirsiniz eğer bunu yapmak get-sadece.

Her durumda, isterseniz aynı dönüş türünü koruyabilirsiniz. Aşağıdaki örnekler basitlik için aynı dönüş türünü kullanır.

Durum: Önceden var olan get-sadece mülk

Değiştiremeyeceğiniz bir sınıf yapınız var. Belki de sadece bir sınıftır ya da önceden var olan bir miras ağacıdır. Durum ne olursa olsun, setbir mülke yöntem eklemek istersiniz , ancak yapamazsınız.

public abstract class A                     // Pre-existing class; can't modify
{
    public abstract int X { get; }          // You want a setter, but can't add it.
}
public class B : A                          // Pre-existing class; can't modify
{
    public override int X { get { return 0; } }
}

Sorun: Yapamam -sadece ile /overridegetgetset

overrideBir get/ setözelliği ile istersiniz , ancak derlenmez.

public class C : B
{
    private int _x;
    public override int X
    {
        get { return _x; }
        set { _x = value; }   //  Won't compile
    }
}

Çözüm: abstractAra katman kullanın

Doğrudan overridebir get/ setmülkle yapamasanız da şunları yapabilirsiniz :

  1. Aynı ada sahip bir new get/ setözellik oluşturun .

  2. overrideeski getyöntemi gettutarlılık sağlamak için yeni yöntem bir erişimci ile .

Yani, önce abstractara katmanı yazıyorsunuz :

public abstract class C : B
{
    //  Seal off the old getter.  From now on, its only job
    //  is to alias the new getter in the base classes.
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}

Sonra, daha önce derlenmeyecek sınıfı yazarsınız. Bu aslında çünkü bu sefer derlersiniz override'ing get-sadece özelliği; bunun yerine, newanahtar kelimeyi kullanarak değiştirebilirsiniz .

public class D : C
{
    private int _x;
    public new virtual int X { get { return this._x; } set { this._x = value; } }

    //  Ensure base classes (A,B,C) use the new get method.
    protected sealed override int XGetter { get { return this.X; } }
}

Sonuç: Her şey işe yarıyor!

Açıkçası, bu amaçlandığı gibi çalışıyor D.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

test.X = 7;
Print(test.X);      // Prints "7", as intended.

DTemel sınıflarından biri olarak görüntülerken , her şey hala amaçlandığı gibi çalışır , örneğin Aveya B. Ancak, çalışmasının nedeni biraz daha az belirgin olabilir.

var test = new D() as B;
//test.X = 7;       // This won't compile, because test looks like a B,
                    // and B still doesn't provide a visible setter.

Bununla birlikte, temel sınıf tanımı getsonuçta türetilmiş sınıfın tanımı ile geçersiz kılınır get, bu yüzden hala tamamen tutarlıdır.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

var baseTest = test as A;
Print(test.X);      // Prints "7", as intended.

Tartışma

Bu yöntem, -only özelliklerine setyöntem eklemenizi sağlar get. Ayrıca, aşağıdakileri yapmak için de kullanabilirsiniz:

  1. Temel sınıfta ne olursa olsun, herhangi bir özelliği get-sadece, set-sadece veya get-ve- setözellik olarak değiştirin.

  2. Türetilmiş sınıflarda bir yöntemin dönüş türünü değiştirin.

Ana dezavantajları, daha fazla kodlama abstract classve miras ağacında ekstra bir şey olmasıdır . Bu, parametre alan yapıcılar için biraz can sıkıcı olabilir, çünkü bunların ara katmana kopyalanması / yapıştırılması gerekir.


Bunu kendi sorusunu verdi: stackoverflow.com/questions/22210971
Nat

Güzel yaklaşım (+1). Ancak bunun varlık çerçevesi ile sorunsuz çalışıp çalışmayacağından şüpheliyim.
Mike de Klerk

9

Türetilmiş tipte bir alıcıyı geçersiz kılamamanın bir anti-desen olduğunu kabul ediyorum. Salt Okunur, saf işlevin bir sözleşmesini değil (en yüksek oylama cevabının ima ettiği) uygulama eksikliğini belirtir.

Microsoft'un bu yanlışlığa ya da aynı yanlış anlama teşvik edildiğinden ya da belki de dilbilgisini basitleştirdiği için sahip olduğundan şüpheleniyorum; Ancak, şimdi bu kapsam bireysel olarak almak veya ayarlamak için uygulanabilir, belki de geçersiz kılmanın da olabileceğini umabiliriz.

Üst oylama cevabının gösterdiği yanlış anlama, salt okunur bir özelliğin bir şekilde bir okuma / yazma özelliğinden daha "saf" olması saçmadır. Sadece çerçevedeki birçok yaygın salt okunur özelliğe bakın; değer sabit / tamamen işlevsel değildir; örneğin, DateTime.Now salt okunur, ancak saf işlevsel bir değer dışında bir şey. Bir dahaki sefere aynı değeri döndüreceğini varsayarak salt okunur bir özelliğin değerini 'önbelleğe alma' girişimi risklidir.

Her durumda, bu sınırlamanın üstesinden gelmek için aşağıdaki stratejilerden birini kullandım; her ikisi de mükemmel olmaktan daha az, ancak bu dil eksikliğinin ötesine geçmenize izin verecektir:

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }

DateTime.Now tamamen işlevseldir, çünkü herhangi bir yan etkisi yoktur (yürütmek için zaman alması düşünülebilir bir şekilde yan etki olarak adlandırılabilir, ancak yürütme süresi - en azından sınırlı ve kısa olduğunda - dikkate alınmaz başka bir yöntemin bir yan etkisi, ben de burada bir tane olarak kabul etmem).
supercat

1
Merhaba supercat. Bir fonksiyonun saf olmasını önleyen harici olarak gözlemlenebilir yan etkiler konusunda haklısınız, ancak diğer kısıtlama saf fonksiyon sonuç değerinin sadece parametrelere bağlı olması gerektiğidir . Dolayısıyla, bir işlev olarak saf bir statik özellik değişmez ve sabit olmalıdır. Gelişmiş bir derleyici, saf bir işleve sonraki tüm çağrıları, hiçbir parametre olmadan, ilk çağrı tarafından döndürülen sonuçla değiştirebilir.
T.Tobler

Terminolojimin kesin olmadığını doğru söylüyorsunuz, ancak bir yöntemden ziyade salt okunur bir özellik olan bir şeyin uygunluğunun saflıktan ziyade yan etkilerin eksikliğine bağlı olduğunu düşünüyorum.
supercat

2

Belki de yeni bir mülk oluşturarak sorunu çözebilirsiniz:

public new int Bar 
{            
    get { return 0; }
    set {}        
}

int IBase.Bar { 
  get { return Bar; }
}

4
Ancak bunu sadece bir arayüz uygularken yapabilirsiniz, bir temel sınıftan miras alırken değil. Başka bir isim seçmelisin.
svick

Örnek sınıfın IBase (IBase {int Bar {get;}} genel arayüzü gibi) uyguladığını varsayarım, aksi takdirde sınıfın uygulanmasında int IBase.Bar {...} 'a izin verilmez. Sonra sadece get ve set ile normal bir özellik Bar oluşturabilirsiniz, bu durumda arabirimdeki Bar özelliği bir küme gerektirmez.
İbrahim ben Salah

1

Tüm puanlarınızı anlayabiliyorum, ancak etkili bir şekilde, C # 3.0'ın otomatik özellikleri bu durumda işe yaramaz.

Böyle bir şey yapamazsınız:

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get;
        private set;
    }
}

IMO, C # bu tür senaryoları kısıtlamamalıdır. Buna göre kullanmak geliştiricinin sorumluluğundadır.


Ne demek istediğini anlamıyorum. C # 'ın bir ayarlayıcı eklemeye izin vermesi gerektiği konusunda hemfikir misiniz, yoksa bu soruyu cevaplayan diğer insanların çoğu ile birlikte misiniz?
ripper234

2
Dediğim gibi, IMO, C # bir ayarlayıcı eklemeye izin vermelidir. Buna göre kullanmak geliştiricinin sorumluluğundadır.
Thomas Danecker

1

Sorun şu ki, Microsoft her ne sebeple olursa olsun, üç farklı özellik türü olması gerektiğine karar vermiştir: salt okunur, salt yazılabilir ve okuma-yazma; bunlardan yalnızca biri belirli bir bağlamda belirli bir imzayla var olabilir; özellikler yalnızca aynı şekilde bildirilen özellikler tarafından geçersiz kılınabilir. İstediğinizi yapmak için, biri salt okunur, biri de okuma-yazma olan aynı ad ve imzayla iki özellik oluşturmanız gerekir.

Şahsen, keşke "get" ve "set" yöntemlerini çağırmak için sözdizimsel şeker olarak kullanılabilmesi dışında tüm "özellikler" kavramının kaldırılabilmesini diliyorum. Bu sadece 'set ekle' seçeneğini kolaylaştırmakla kalmayacak, aynı zamanda “get” in “set” ten farklı bir tür döndürmesine izin verecektir. Böyle bir yetenek çok sık kullanılmasa da, bazen 'get' yönteminin bir sarmalayıcı nesnesini döndürmesi yararlı olabilirken, 'set' bir sarmalayıcıyı veya gerçek verileri kabul edebilir.


0

Reflection kullanarak bunu başarmak için bir çözüm bulabilirsiniz:

var UpdatedGiftItem = // object value to update;

foreach (var proInfo in UpdatedGiftItem.GetType().GetProperties())
{
    var updatedValue = proInfo.GetValue(UpdatedGiftItem, null);
    var targetpropInfo = this.GiftItem.GetType().GetProperty(proInfo.Name);
    targetpropInfo.SetValue(this.GiftItem, updatedValue,null);
}

Bu şekilde, salt okunur bir özellik üzerinde nesne değeri ayarlayabiliriz. Yine de tüm senaryolarda çalışmayabilir!


1
Bir ayarlayıcıyı var olmayan yansıma ile çağırabileceğinizden emin misiniz (açıklanan yalnızca alıcı özelliklerinde olduğu gibi)?
VEYA Haritacı

0

Soru başlığınızı, sorunuzun yalnızca soyut bir mülkün geçersiz kılınmasıyla ilgili olduğu veya sorunuzun genellikle bir sınıfın salt özellikli mülkünü geçersiz kılma konusundaki ayrıntısına göre değiştirmeniz gerekir.


Önceki (soyut bir özelliği geçersiz kılma)

Bu kod işe yaramaz. Yalnızca bir temel sınıf size Get-Get özelliğini (Belki de bir Arabirim) geçersiz kılmak zorunda olduğunuzu söylememelidir. Temel sınıf, bir uygulama sınıfından belirli girdiler gerektirebilecek ortak işlevsellik sağlar. Bu nedenle, ortak işlevsellik soyut özelliklere veya yöntemlere çağrı yapabilir. Belirli bir durumda, ortak işlevsellik yöntemleri, aşağıdaki gibi soyut bir yöntemi geçersiz kılmanızı istemelidir:

public int GetBar(){}

Ancak bunun üzerinde kontrolünüz yoksa ve temel sınıfın işlevselliği kendi ortak mülkünden (garip) okursa, bunu yapın:

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    private int _bar;
    public override int Bar
    {
        get { return _bar; }
    }
    public void SetBar(int value)
    {
        _bar = value;
    }
}

(Tuhaf) yorumu belirtmek istiyorum: En iyi uygulama, bir sınıfın kendi genel özelliklerini kullanmaması, ancak özel / korunan alanlarını mevcut olduğunda kullanmasıdır. Yani bu daha iyi bir model:

public abstract class BaseClass {
    protected int _bar;
    public int Bar { get { return _bar; } }
    protected void DoBaseStuff()
    {
        SetBar();
        //Do something with _bar;
    }
    protected abstract void SetBar();
}

public class ConcreteClass : BaseClass {
    protected override void SetBar() { _bar = 5; }
}

İkincisi ise (bir sınıfın salt özellik özelliğini geçersiz kılma)

Soyut olmayan her mülk bir ayarlayıcıya sahiptir. Aksi takdirde işe yaramaz ve onu kullanmamanız gerekir. Microsoft'un istediğinizi yapmanıza izin vermesi gerekmez. Sebep: ayarlayıcı bir şekilde var olabilir ve Veerryy istediğinizi kolayca başarabilirsiniz .

Taban sınıfı veya bir özelliğini okuyabilir herhangi sınıfı {get;}, sahip BAZI o özellik için maruz setter çeşit. Meta veriler şöyle görünecektir:

public abstract class BaseClass
{
    public int Bar { get; }
}

Ancak uygulamanın karmaşıklık spektrumunun iki ucu olacaktır:

En Küçük Kompleks:

public abstract class BaseClass
{
    private int _bar;
    public int Bar { 
        get{
            return _bar;
        }}
    public void SetBar(int value) { _bar = value; }
}

En Karmaşık:

public abstract class BaseClass
{
    private int _foo;
    private int _baz;
    private int _wtf;
    private int _kthx;
    private int _lawl;

    public int Bar
    {
        get { return _foo * _baz + _kthx; }
    }
    public bool TryDoSomethingBaz(MyEnum whatever, int input)
    {
        switch (whatever)
        {
            case MyEnum.lol:
                _baz = _lawl + input;
                return true;
            case MyEnum.wtf:
                _baz = _wtf * input;
                break;
        }
        return false;
    }
    public void TryBlowThingsUp(DateTime when)
    {
        //Some Crazy Madeup Code
        _kthx = DaysSinceEaster(when);
    }
    public int DaysSinceEaster(DateTime when)
    {
        return 2; //<-- calculations
    }
}
public enum MyEnum
{
    lol,
    wtf,
}

Demek istediğim, her iki durumda da ayarlayıcıyı açığa çıkarıyorsunuz. Sizin durumunuzda, int Bartemel sınıfın işlemesini istemediğiniz, nasıl işlediğini gözden geçirmek için erişiminiz olmadığından veya iradenize karşı gerçek quick'n'dirty bazı kodları taklit etmek istendiğinden geçersiz kılmak isteyebilirsiniz .

Hem Sonunda hem de Eski'de (Sonuç)

Uzun Öykü Kısa: Microsoft'un hiçbir şeyi değiştirmesi gerekmez. Uygulayıcı sınıfınızın nasıl ayarlanacağını seçebilir ve yapıcıya temel sınıfın tamamını veya hiçbirini kullanamazsınız.


0

Kullanım durumlarının sadece küçük bir alt kümesi için çözüm, ancak yine de: C # 6.0'da geçersiz kılınan yalnızca alıcı özellikleri için "salt okunur" ayarlayıcı otomatik olarak eklenir.

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    public override int Bar { get; }

    public ConcreteClass(int bar)
    {
        Bar = bar;
    }
}

-1

Çünkü bu, kapsülleme ve uygulama gizleme kavramını kıracaktır. Bir sınıf oluşturduğunuzda, onu gönderdiğinizde ve sınıfınızın tüketicisi sadece başlangıçta bir alıcı sağladığınız bir mülk ayarlayabildiğini düşünün. Uygulamanızda güvenebileceğiniz sınıfınızın değişmezlerini etkili bir şekilde bozar.


1
-1: Bir ayarlayıcıya sahip olmak, bir torunun otomatik olarak değişmezleri kırmasını sağlamaz. Ayarlayıcı yine de sadece soyundan gelen zaten değiştirilebilen şeyleri değiştirebilir.
Roman Starkov

@RomanStarkov Bu doğru. Ancak bu yazının sorusu; Sizi sözleşme yapmayan bir soyutlamaya neden ayarlayıcı ekleyemiyorsunuz? Yorumunuz karşı soru için uygun olacaktır; Neden soyundan gelen özel alanlar üzerinde tam denetime sahipse Microsoft neden bir soyutlamada bir ReadOnly özelliği oluşturmanıza izin veriyor?
Suamere

+1: Bu soru, Microsoft'un neden bir sözleşmeyi ihlal etmenize izin vermediğiyle ilgili. Olumlu oylarınızın nedeni, Microsoft'un bir sözleşmenin neden salt okunur özellikleri içermesine izin verdiği hakkında daha fazla olmasına rağmen. Dolayısıyla bu cevap bağlamda doğrudur.
Suamere

@Suamere sözleşmesi, mülkün değiştirilemeyeceğini belirtmez. Tek ifade edilebilecek bir özellik var. Bir ayarlayıcı eklemek bu sözleşmeyi ihlal etmez. Sen edebilirsiniz yorumlamak bir olarak niyet salt okunur özelliğini oluşturmak için, ancak bunu değiştirmek için hiçbir şekilde, değil aslında orada konum düşünme dolayısıyla sözleşme size orada olduğunu C # 6'da garanti edemez. Bir arayüz düşünün: bir gettable (sadece get!) Mülk üzerinde bir sözleşme sunabilir. Bu arayüz gayet iyi bir get + set özelliği ile uygulanabilir.
Roman Starkov

Buna somut yazar perspektifinden yaklaşıyorsunuz. İstediği gibi değiştirebilir. Temel sınıf yazarının bakış açısıyla yaklaşıyorum -> Uygulayıcı sınıfın nihai sonucu olarak yaklaşmak. Temel sınıfın yazarı, gelecekteki somut sınıflardan herhangi bir tüketicinin, somut sınıfın yazarı, duruma göre işlemek için özel bir yöntem göstermeden bu özelliği değiştirmesini istemez. Çünkü temel sınıfta, bu özellik değişmez olarak ele alınır ve bu sadece bu şekilde teşhir edilerek iletilir.
Suamere

-1

Bu imkansız değil. Mülkünüzde "yeni" anahtar kelimeyi kullanmanız yeterlidir. Örneğin,

namespace {
    public class Base {
        private int _baseProperty = 0;

        public virtual int BaseProperty {
            get {
                return _baseProperty;
            }
        }

    }

    public class Test : Base {
        private int _testBaseProperty = 5;

        public new int BaseProperty {
            get {
                return _testBaseProperty;
            }
            set {
                _testBaseProperty = value;
            }
        }
    }
}

Görünüşe göre bu yaklaşım tartışmanın her iki tarafını da tatmin ediyor. "Yeni" kullanmak, temel sınıf uygulaması ile alt sınıf uygulaması arasındaki sözleşmeyi ihlal eder. Bu, bir Sınıfın birden fazla sözleşmesi olabileceği zaman gereklidir (arabirim veya temel sınıf üzerinden).

Bu yardımcı olur umarım


29
Bu yanıt yanlıştır çünkü işaretlenen özellik newsanal temeli geçersiz kılmamaktadır. Özellikle, base özelliği soyutsa , orijinal newyayında olduğu gibi, tüm soyut üyeleri geçersiz kılmak zorunda olduğunuz için anahtar kelimeyi soyut olmayan bir alt sınıfta kullanmak imkansızdır .
Timwi

Bu olağanüstü derecede tehlikelidir çünkü aşağıdakiler aynı nesne olmasına rağmen farklı sonuçlar döndürür: Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)size 5 Console.WriteLine(b.BaseProperty)verir, size 0 verir ve halefiniz bu hata ayıklamak zorunda kaldığında çok daha uzaklara gitmeniz daha iyi olur.
Mark Sowul

Mark w / katılıyorum, bu son derece kötü. Her iki BaseProperty uygulaması da aynı değişken tarafından desteklenmiş olsaydı, IMHO'ya rağmen iyi olurdu. IE: _baseProperty'yi korumalı hale getirin ve _testBaseProperty'yi kaldırın. Ayrıca, AFAIK'ın Base'deki uygulamanın sanal olması gerekmez, çünkü aslında onu geçersiz kılmazsınız.
Steazy

Soruların cevaplarının çoğu geçerli görüş ve girdilere sahip olabilir. Ancak önceki yorumların yanı sıra, bu cevaba ilişkin ana eleştirim, asıl sorunun, kelimenin tam anlamıyla tüketicinin üzerinde kontrol sahibi olmadığı bir kod tabanındaki soyut özellikler hakkında olabileceğine inanıyorum. Benim eleştirim, bu cevabın soruya uymayabileceği yönündedir.
Suamere

-1

Temel sınıftaki salt okunur bir özellik, bu özelliğin her zaman sınıf içinden belirlenebilen bir değeri temsil ettiğini gösterir (örneğin, bir nesnenin (db-) bağlamıyla eşleşen bir enum değeri). Dolayısıyla değeri belirlemenin sorumluluğu sınıf içinde kalır.

Bir ayarlayıcı eklemek burada garip bir soruna neden olur: Değeri zaten sahip olduğu olası tek bir değerden başka bir değere ayarlarsanız bir doğrulama hatası oluşmalıdır.

Yine de kuralların istisnaları vardır. Örneğin türetilmiş bir sınıfta bağlamın olası enum değerlerini 10 üzerinden 3'e kadar daraltması çok mümkündür, ancak bu nesnenin kullanıcısının hala hangisinin doğru olduğuna karar vermesi gerekir. Türetilmiş sınıfın, değeri bu nesnenin kullanıcısına belirleme sorumluluğunu devretmesi gerekir. Farkına varmak önemlidir , bu nesnenin kullanıcısının bu istisnanın farkında olması gerekir ve doğru değeri ayarlamak için responsibillity varsayalım.

Bu tür durumlarda benim çözüm özelliği salt okunur bırakmak ve istisna desteklemek için türetilmiş sınıfa yeni bir okuma-yazma özelliği eklemek olacaktır. Orijinal özelliğin geçersiz kılınması, yeni özelliğin değerini döndürür. Yeni özelliğin, bu özel durumun bağlamını doğru şekilde belirten uygun bir adı olabilir.

Bu aynı zamanda geçerli ifadeyi de desteklemektedir: Gishu'nun “yanlış anlamaların ortaya çıkmasını mümkün olduğunca zorlaştırın”.


1
Alt türlerine ReadableMatrixsahip bir sınıf (iki bağımsız değişkenli salt okunur dizinli özellik ile) ImmutableMatrixve tarafından hiçbir gariplik ima edilmez MutableMatrix. Sadece bir matrisi okuması gereken ve gelecekte bir süre değişip değişmeyeceği umurumda olmayan kod, bir tür parametreyi kabul edebilir ReadableMatrixve değişebilir veya değişmez alt tiplerden mükemmel bir şekilde memnun olabilir.
supercat

Ek bir ayarlayıcı sınıf içindeki özel bir değişkeni değiştirebilir. Temel sınıftan "salt okunur" özelliği, yine de "sınıfın içinden" değerini belirler (yeni özel değişkeni okuyarak). Sorunu orada görmüyorum. Ayrıca, örneğin aşağıdakilere de bakın List<T>.Count: Atanamaz, ancak sınıfın kullanıcıları tarafından değiştirilemeyeceği için "salt okunur" anlamına gelmez. Liste öğeleri ekleyip kaldırarak dolaylı da olsa çok iyi değiştirilebilir.
VEYA Haritacı

-2

Çünkü IL düzeyinde, bir okuma / yazma özelliği iki (alıcı ve ayarlayıcı) yöntemine dönüşür.

Geçersiz kılınırken, alttaki arayüzü desteklemeye devam etmelisiniz. Bir ayarlayıcı ekleyebilseydiniz, sınıflarınızın arayüzü söz konusu olduğunda, dış dünyaya görünmez kalacak yeni bir yöntem eklersiniz.

Doğru, yeni bir yöntem eklemek kendi başına uyumluluğu bozmaz, ancak gizli kalacağı için buna izin vermeme kararı mükemmel olur.


2
Burada "dış dünyayı" umursamıyorum. Ben her zaman sadece belirli sınıfı bilen kodu için görünür olan bir sınıfa işlevsellik eklemek istiyorum.
ripper234

@ ripper234 Bkz. Matt bolt'un yanıtı: Yeni özelliği yalnızca türetilmiş sınıfın kullanıcıları newyerine görünür kılmak istiyorsanız override.
Dan Berindei

1
Gizli kalmaz. Bir çocuk sınıfına yeni bir yöntem ekleyemeyeceğinizi söylemek gibisiniz, çünkü yeni yöntem "dış dünyadan gizli kalacaktır". Tabii ki, temel sınıf perspektifinden gizli kalıyor, ancak çocuk sınıfın istemcilerinden erişilebilir. (Yani
alıcının

Sheepy, ben öyle demiyorum. Demek istediğim, tam da belirttiğin gibi, "temel sınıf perspektifinden" gizli kalacaktı. Bu yüzden "sınıflarınızın arayüzü söz konusu olduğunda" ekledim. Bir temel sınıfın alt sınıfını alıyorsanız, "dış dünya" nın temel sınıf türüne sahip nesne örneklerinize atıfta bulunacağı neredeyse kesindir. Somut sınıfınızı doğrudan kullanırlarsa, temel sınıfla uyumluluğa ihtiyacınız olmazdı ve elbette neweklemelerinizle kullanabilirsiniz .
Ishmaeel

-2

Çünkü salt okunur bir özelliğe (ayarlayıcı olmadan) sahip bir sınıfın muhtemelen bunun için iyi bir nedeni vardır. Örneğin, altta yatan herhangi bir veri deposu olmayabilir. Bir ayarlayıcı oluşturmanıza izin vermek, sınıf tarafından belirlenen sözleşmeyi ihlal eder. Sadece kötü OOP.


Yukarı-Oy. Bu çok kısa ve öz. Microsoft neden bir temel sınıf tüketicisinin sözleşmeyi ihlal etmesine izin vermeli? Çok uzun ve kafa karıştırıcı cevabımın bu çok kısa versiyonuna katılıyorum. Lol. Bir temel sınıfın tüketicisiyseniz, sözleşmeyi bozmak istememelisiniz, ancak bunu birkaç farklı şekilde uygulayabilirsiniz.
Suamere
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.