it-swarm-tr.com

Yalnızca bir alıcı özelliğini geçersiz kılmak ve bir ayarlayıcı eklemek neden imkansız?

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ınan bir ayar erişimcisi içermediğinden geçersiz kılınamıyor

130
ripper234

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

Bu konuda Microsoft ile birlikteyim.
Baseclass türevine karşı kod yazması söylenen yeni bir programcı olduğumu varsayalım. Bar'ın yazılamayacağını varsayan bir şey yazıyorum (Baseclass açıkça bunun sadece bir özellik olduğunu belirtir) . Şimdi türevinizle kodum bozulabilir. Ö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 arayüzüne göre ayarlanamadığından, BarProvider, önbelleğe almanın güvenli bir şey olduğunu varsayar - Bar değiştirilemediğinden. Ancak bir türetme işleminde küme mümkün olsaydı, eğer biri _source nesnesinin Bar özelliğini harici olarak değiştirdiyse bu sınıf eski değerleri sunabilirdi. Asıl nokta 'Açık olun, sinsi şeyler yapmaktan ve insanları şaşırtmaktan kaçınmak'

Güncelleme : Ilya Ryzhenkov 'Neden arayüzler aynı kurallara göre oynamıyor?' Hmm .. Bu düşündüğüm kadar çamurluyor.
Arayüz 'bir uygulamanın Bar adlı bir okuma özelliğine sahip olmasını bekle' yazan bir sözleşmedir. Şahsen Eğer bir Arayüz görüyorsam bu salt okunur varsayımı yapma ihtimalim çok daha az. Bir arabirimde "salt okunur" bir özellik gördüğümde, "Bar salt okunur bir özelliktir" olarak tıkladığı bir temel sınıfta, "Herhangi bir uygulama, Bar bu niteliğini ortaya çıkarır ..." olarak okurum. Tabii teknik olarak kontratı çiğnemezsin .. daha fazlasını yapıyorsun. Yani bir anlamda haklısın .. 'Yanlış anlaşılmaların artmasını mümkün olduğunca zorlaştır' diyerek kapatırdım.

1
Gishu

Bence asıl sebep, sözdiziminin bunun başka bir şekilde çalışamayacağı kadar açık olmasıdır. Bu kod:

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

get ve set öğelerinin geçersiz kılınması oldukça açıktır. Temel sınıfta set yok, bu nedenle derleyici şikayet ediyor. Tıpkı ana sınıfta tanımlanmayan bir yöntemi geçersiz kılamaz gibi, bir ayarlayıcıyı da geçersiz kılamazsınız.

Derleyicinin niyetini tahmin etmesi gerektiğini ve geçersiz kılmayı yalnızca geçersiz kılınabilecek yönteme (yani, bu durumda alıcıya) uygulayabileceğini söyleyebilirsin, ancak bu C # tasarım ilkelerinden birine aykırıdır - derleyicinin niyetlerini tahmin etmemesi gerekir. , çünkü siz bilmeden yanlış tahmin edebilir.

Bence aşağıdaki söz dizimi güzel olabilir ama 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 uygulanmış özellikler için,

public int MyProperty { override get; set; }
31
Roman Starkov

Bugün aynı soruna rastladım ve bunu istemek için çok geçerli bir neden olduğunu düşünüyorum. 

Öncelikle, salt okunur bir özelliğe sahip olmanın mutlaka salt okunur hale gelmediğini savunmak isterim. Bunu "Bu arabirimden/abtract'dan bu değeri elde edebilirsiniz" olarak yorumluyorum, bu, bu arabirim/soyut sınıfın bir uygulamasının, bu değeri açıkça ayarlamak için kullanıcı/programa ihtiyaç duymayacağı anlamına gelmez. Soyut sınıflar, gerekli işlevselliğin bir bölümünü uygulama amacına hizmet eder. Miras kalan bir sınıfın herhangi bir sözleşmeyi ihlal etmeden bir pasör ekleyememesi için hiçbir neden görmüyorum.

Aşağıdaki, bugün ihtiyaç duyduğum şeyin basitleştirilmiş bir örneğidir. Ben sadece bunun üstesinden gelmek için arayüzüme bir ayarlayıcı eklemek zorunda kaldım. Belirleyiciyi eklemenin ve bir SetProp yönteminin eklenmemesinin nedeni, arabirimin belirli bir uygulamasının Prop serileştirilmesi için DataContract/DataMember'i kullanmasıydı; 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 bir "2 kuruş" yazısı olduğunu biliyorum, ancak orijinal posterle hissediyorum ve bunun iyi bir şey olduğunu rasyonalize etmeye çalışıyorum, özellikle de aynı sınırlamaların doğrudan arayüz.

Ayrıca geçersiz kılmak yerine yeniyi kullanmak hakkındaki sözler burada geçerli değildir, basitçe işe yaramaz ve yapmış olsaydı bile size istenen sonucu vermedi, yani arayüzde tanımlandığı şekilde sanal bir alıcı.

19
JJJ

Mümkün

tl; dr - İsterseniz bir salt okunur yöntemle bir salt okunur yöntemi geçersiz kılabilirsiniz. Temelde sadece:

  1. Aynı adı kullanarak hem new hem de get olan bir set özelliği oluşturun.

  2. Başka bir şey yapmazsanız, eski get yöntemi türetilmiş sınıf temel türünden çağrıldığında yine de çağrılır. Bunu düzeltmek için, yeni abstract yönteminin sonucunu döndürmeye zorlamak için eski override yönteminde get kullanan bir get ara katmanı ekleyin.

Bu, temel tanımlarında bir tane olmasalar bile getset ile özellikleri geçersiz kılmamızı sağlar.

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

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

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

  • Temel tanım zaten get/set ise, o zaman:

    • daha türetilmiş bir dönüş türü kullanabilirsiniz _/ifbunu yaparsanız set- sadece;

    • daha az türetilmiş bir dönüş türü kullanabilirsinizifsadece get- yaparsınız.

Her durumda, isterseniz aynı dönüş türünü tutabilirsiniz. Aşağıdaki örnekler sadelik için aynı iade türünü kullanmaktadır.

Durum: Önceden var olan get- only

Değiştiremeyeceğiniz bir sınıf yapınız var. Belki sadece bir sınıftır, ya da önceden var olan bir miras ağacı. Durum ne olursa olsun, bir özelliğe set yöntemi eklemek istiyorsunuz, 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: override __ get- sadece • getset ile

override __ get/set özelliği ile istiyor, ancak derleme yapmıyor.

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

Çözüm: abstract ara katman kullanın

override özelliğini get/set özelliği ile doğrudan alamazsanız, _/can:

  1. Aynı ada sahip bir newget/set özelliği oluşturun.

  2. override, tutarlılığı sağlamak için yeni get yöntemine erişim sağlayan eski get yöntemi.

Yani, önce abstract ara katmanı yazmalısınız:

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 bir sınıf yazarsın. Bu zamanı derleyecektir çünkü aslında override 'in get- only özelliğini kullanmıyorsunuz; bunun yerine, new anahtar sözcüğünü kullanarak değiştiriyorsunuz.

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 çalışıyor!

Açıkçası, bu D için amaçlandığı gibi çalışır.

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.

Her şey D'yi temel sınıflarından biri olarak görüntülerken amaçlandığı gibi çalışır, örn. A veya 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, get'nin temel sınıf tanımı, türetilmiş sınıfın get tanımı ile nihayetinde geçersiz kılınır, bu yüzden hala tamamen tutarlı.

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, set-only özelliklerine get yöntemleri eklemenizi sağlar. Ayrıca böyle şeyler yapmak için de kullanabilirsiniz:

  1. Herhangi bir özelliği, bir temel sınıfta ne olduğuna bakılmaksızın, get- only, set-, veya get- ve -set özelliği olarak değiştirin.

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

En büyük dezavantajı, miras ağacında yapılacak daha fazla kodlama ve fazladan bir abstract class olması. Bu, parametreleri alan yapılara biraz sinir bozucu gelebilir, çünkü bunların ara tabakaya kopyalanması/yapıştırılması gerekir.

12
Nat

Türetilmiş bir türdeki bir alıcıyı geçersiz kılmamanın bir kalıp karşıtı olduğu konusunda hemfikirim. Salt Okunur, uygulama işlevsizliğini belirtir (salt oylama cevabı ile ima edilen) bir saf işlev sözleşmesi değildir.

Microsoft'un aynı kısıtlamanın ya da aynı zamanda gramerin basitleştirilmesi nedeniyle tanıtılmasından dolayı bu sınırlamaya sahip olduğundan şüpheleniyorum; Ancak, şimdi bu kapsam ayrı ayrı almak veya ayarlamak için uygulanabilir, belki geçersiz kılmanın da olabileceğini umabiliriz.

En iyi oylamada verilen yanlış anlama, salt okunur bir özelliğin bir şekilde okuma/yazma özelliğinden daha "saf" olması gerektiği şeklinde ifade edilir. Basitçe çerçevedeki birçok salt okunur özelliğe bakın; değer sabit/tamamen işlevsel değildir; örneğin, DateTime.Now salt okunurdur, ancak saf bir işlevsel değerden başka bir şey değildir. Bir dahaki sefere aynı değeri döndüreceğini varsayarak salt okunur bir özelliğin değerini 'önbelleğe alma' denemesi risklidir.

Her durumda, bu sınırlamayı aşmak için aşağıdaki stratejilerden birini kullandım; Her ikisi de mükemmel olmaktan az, ancak bu dil eksikliğinin ötesine geçmenizi sağlayacak:

   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;
   }
8
T.Tobler

Sorun, hangi nedenle olursa olsun, Microsoft, üç farklı özellik türü olması gerektiğine karar vermiştir: salt okunur, salt okunur ve salt okunur, yalnızca birinin belirli bir bağlamda belirli bir imzayla bulunabileceği; özellikleri yalnızca aynı şekilde bildirilen özellikler tarafından geçersiz kılınabilir. İstediğinizi yapmak için, aynı adı ve imzası olan, biri salt okunur, diğeri salt okunur yazma olan iki özellik oluşturmak gerekir.

Şahsen, "özellik" kavramının tümünün kaldırılmasını, ancak özellik-ish sözdiziminin "get" ve "set" yöntemlerini çağırmak için sözdizimsel şeker olarak kullanılabilmesi dışında keşke. Bu sadece 'set set' opsiyonunu kolaylaştırmakla kalmayacak, aynı zamanda 'set' ten farklı bir tip getirmesini de sağlayacaktır. Böyle bir yetenek çok sık kullanılmasa da, bazen bir 'get' yönteminin bir sarmalayıcı nesnesini döndürmesi faydalı olabilirken, 'set' bir sarmalayıcıyı ya da gerçek verileri kabul edebilir.

1
supercat

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

Böyle bir şey yapamazsın:

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

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

1
Thomas Danecker

Belki de yeni bir özellik oluşturarak problemi çözebilirsiniz:

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

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

Yalnızca küçük kullanım durumları alt kümesi için çözüm, ancak yine de: C # 6.0'da "salt okunur" ayarlayıcı, geçersiz kılan yalnızca alıcı özellikleri için 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;
    }
}
0
lxa

Soru başlığınızı, sorunuzun yalnızca soyut bir mülkiyeti geçersiz kılmakla ilgili olduğu ya da sorunuzun genellikle bir sınıfın salt elde edilebilir mülkünü geçersiz kılma ile ilgili olduğu şeklinde değiştirmelisiniz.


Eski ise (soyut mülkiyeti geçersiz kılma)

Bu kod işe yaramaz. Yalnız bir ana sınıf, bir Get-Only özelliğini (Belki de bir Arayüz) geçersiz kılmaya zorlandığınızı söylememelidir. Bir temel sınıf, bir uygulama sınıfından belirli bir girdi gerektirebilecek ortak işlevler sağlar. Bu nedenle, ortak işlevsellik soyut özelliklere veya yöntemlere çağrı yapabilir. Bu durumda, ortak işlevsellik yöntemleri sizden bir soyut yöntemi geçersiz kılmanızı ister:

public int GetBar(){}

Ancak, bunun üzerinde hiçbir kontrolünüz yoksa ve temel sınıfın işlevselliği kendi kamu mülkiyetinden (garip) okursa, o zaman şunu 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;
    }
}

(Garip) yorumu işaret etmek isterim: Bir sınıf için kendi kamu mülklerini kullanmamak, ancak var olduğunda özel/korunan alanlarını kullanmak için en iyi uygulamanın olduğunu söyleyebilirim. Yani bu daha iyi bir örnek:

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; }
}

Eğer sonuncusu (bir sınıfın salt okunur özelliğini geçersiz kılma)

Her soyut olmayan mülkün bir belirleyicisi vardır. Aksi takdirde faydasızdır ve kullanmaya aldırış etmemelisiniz. Microsoft, istediğinizi yapmanıza izin vermek zorunda değildir. Sebep: pasör bir şekilde veya başka bir şekilde var olur ve ne istediğinizi başarabilirsiniz Veerryy .

Temel sınıf veya {get;} özelliğine sahip bir özelliği okuyabileceğiniz herhangi bir sınıf,IÇIN BAZItür açıklayıcı ayarlayıcıya sahiptir. Meta veriler şöyle görünecek:

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

Ancak uygulama karmaşıklık yelpazesinin iki ucuna sahip olacak:

En Az 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 şekilde de, belirleyiciyi açığa vurdun. Sizin durumunuzda, int Bar işlevini geçersiz kılmak isteyebilirsiniz, çünkü temel sınıfın onu ele almasını istemiyorsunuz, onun nasıl işlediğini gözden geçirme erişimine sahip olmuyorsunuz ya da aleyhinize gerçek bir kodla kodlamak için görevlendiriliyorsunuz irade.

İkincisi ve Eski Halinde (Sonuç)

Uzun Öykü Kısa: Microsoft'un bir şeyi değiştirmesi gerekli değildir. Uygulama sınıfınızın nasıl kurulduğunu seçebilir ve yapıcı tarafından aranabilir, temel sınıfın tümünü veya hiçbirini kullanamazsınız.

0
Suamere

Yansıma kullanarak bunu başarmak için bir çalışma var:

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 yolla, nesne değerini salt okunur olan bir özelliğe ayarlayabiliriz. Yine de tüm senaryolarda çalışmayabilir!

0
user2514880