Mengapa tidak mungkin menimpa properti pengambil-saja dan menambahkan setter? [Tutup]

141

Mengapa kode C # berikut tidak diizinkan:

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': tidak dapat menimpa karena 'BaseClass.Bar' tidak memiliki set accessor yang dapat ditimpa

ripper234
sumber
9
StackOverflow memiliki kemampuan terbatas untuk menjawab pertanyaan atas nama Microsoft. Coba ulangi pertanyaan Anda.
Jay Bazuzi
1
Secara kebetulan, obat sederhana untuk perilaku menjengkelkan ini di .net adalah memiliki properti baca-saja non-virtual Fooyang tidak melakukan apa pun selain membungkus GetFoometode abstrak yang dilindungi . Tipe turunan abstrak dapat membayangi properti dengan properti baca-tulis non-virtual yang tidak melakukan apa pun selain membungkus GetFoometode tersebut bersama dengan SetFoometode abstrak ; jenis turunan beton dapat melakukan hal di atas tetapi memberikan definisi untuk GetFoodan SetFoo.
supercat
1
Hanya untuk menambah bahan bakar ke api, C ++ / CLI memang memungkinkan ini.
ymett
1
Kemampuan untuk menambahkan accessor ketika mengganti properti telah diusulkan sebagai perubahan untuk versi C # di masa mendatang ( github.com/dotnet/roslyn/issues/9482 ).
Brandon Bonds
1
Sejujurnya, pertanyaan ini sering diajukan ketika ada kebutuhan untuk memperluas tipe perpustakaan yang ada yang menggunakan pola berikut: kelas dasar read-only, turunan kelas yang dapat diubah. Karena pustaka sudah menggunakan pola buruk (seperti WPF), perlu timbul untuk mengatasi pola buruk ini. Ceramah tidak menyelamatkan seseorang dari keharusan menerapkan solusi pada akhir hari.
rwong

Jawaban:

-4

Karena penulis Baseclass secara eksplisit menyatakan bahwa Bar harus menjadi properti hanya-baca. Tidak masuk akal jika derivasi melanggar kontrak ini dan membuatnya baca-tulis.

Saya dengan Microsoft yang satu ini.
Katakanlah saya seorang programmer baru yang telah diberi tahu kode terhadap derivasi Baseclass. saya menulis sesuatu yang mengasumsikan bahwa Bar tidak dapat dituliskan (karena Baseclass secara eksplisit menyatakan bahwa itu adalah properti get only). Sekarang dengan derivasi Anda, kode saya mungkin rusak. misalnya

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

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

  public Bar getBar()
  { return _currentBar;  }
}

Karena Bar tidak dapat diatur sesuai antarmuka BaseClass, BarProvider mengasumsikan bahwa caching adalah hal yang aman untuk dilakukan - Karena Bar tidak dapat dimodifikasi. Tetapi jika set dimungkinkan dalam derivasi, kelas ini bisa menyajikan nilai basi jika seseorang memodifikasi properti Bar objek _source secara eksternal. Intinya adalah ' Terbuka, hindari melakukan hal-hal licik dan mengejutkan orang '

Pembaruan : Ilya Ryzhenkov bertanya 'Mengapa antarmuka tidak bermain dengan aturan yang sama?' Hmm .. ini jadi lebih kacau saat kupikirkan.
Antarmuka adalah kontrak yang mengatakan 'mengharapkan implementasi memiliki properti baca bernama Bar.' Secara pribadi saya jauh lebih kecil kemungkinannya untuk membuat asumsi read-only jika saya melihat sebuah Interface. Ketika saya melihat properti get-only pada antarmuka, saya membacanya sebagai 'Implementasi apa pun akan mengekspos bar atribut ini' ... pada kelas dasar itu klik sebagai 'Bar adalah properti read-only'. Tentu saja secara teknis Anda tidak melanggar kontrak .. Anda melakukan lebih banyak. Jadi Anda benar dalam arti tertentu .. Saya akan menutup dengan mengatakan 'buat sesulit mungkin untuk kesalahpahaman muncul'.

Gishu
sumber
94
Saya masih belum yakin. Bahkan jika tidak ada penyetel eksplisit, ini tidak menjamin bahwa properti akan selalu mengembalikan objek yang sama - itu masih dapat diubah oleh beberapa metode lain.
ripper234
34
Bukankah seharusnya hal yang sama berlaku untuk antarmuka? Anda dapat memiliki properti readonly pada antarmuka dan membaca / menulis pada tipe implementasi.
Ilya Ryzhenkov
49
Saya tidak setuju: kelas dasar hanya menyatakan properti tidak memiliki setter; itu tidak sama dengan properti yang hanya-baca. "Hanya-baca" hanya arti yang Anda tetapkan untuk itu. Tidak ada jaminan yang relevan dibangun ke dalam C # sama sekali. Anda bisa memiliki properti get-only yang berubah setiap kali, atau properti get / set di mana setter melempar InvalidOperationException("This instance is read-only").
Roman Starkov
21
Melihat getter dan setter sebagai metode, saya tidak melihat mengapa itu harus menjadi "pelanggaran kontrak" seperti menambahkan metode baru. Kontrak kelas dasar tidak ada hubungannya dengan fungsi apa yang tidak ada, hanya dengan apa yang ada.
Dave Cousineau
37
-1 Saya tidak setuju dengan jawaban ini, dan menganggapnya salah. Karena kelas dasar mendefinisikan perilaku yang harus dipatuhi oleh kelas mana pun (Lihat Prinsip Substitusi Liskov), tetapi tidak (dan tidak boleh) membatasi untuk menambahkan perilaku. Kelas dasar mungkin hanya mendefinisikan apa perilaku itu, dan tidak bisa (dan tidak seharusnya) menentukan perilaku apa yang 'tidak boleh' (yaitu 'tidak boleh ditulis pada tingkat yang nyata)'.
Marcel Valdez Orozco
39

Saya pikir alasan utamanya adalah karena sintaksinya terlalu eksplisit untuk dapat bekerja dengan cara lain. Kode ini:

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

cukup eksplisit bahwa keduanya getdan setyang ditimpa. Tidak ada setdi kelas dasar, jadi kompiler mengeluh. Sama seperti Anda tidak bisa mengganti metode yang tidak didefinisikan di kelas dasar, Anda juga tidak bisa menimpa setter.

Anda mungkin mengatakan bahwa kompiler harus menebak niat Anda dan hanya menerapkan override ke metode yang dapat diganti (yaitu pengambil dalam kasus ini), tetapi ini bertentangan dengan salah satu prinsip desain C # - bahwa kompiler tidak boleh menebak niat Anda , karena mungkin salah menebak tanpa Anda sadari.

Saya pikir sintaks berikut mungkin bekerja dengan baik, tetapi seperti Eric Lippert terus katakan, mengimplementasikan bahkan fitur kecil seperti ini masih merupakan upaya besar ...

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

atau, untuk properti yang diimplementasikan secara otomatis,

public int MyProperty { override get; set; }
Roman Starkov
sumber
19

Saya menemukan masalah yang sama hari ini dan saya pikir memiliki alasan yang sangat valid untuk menginginkan ini.

Pertama saya ingin berdebat bahwa memiliki properti get-only tidak harus diterjemahkan menjadi read-only. Saya menafsirkannya sebagai "Dari antarmuka ini / abtract Anda bisa mendapatkan nilai ini", itu tidak berarti bahwa beberapa implementasi dari antarmuka / kelas abstrak tidak memerlukan pengguna / program untuk mengatur nilai ini secara eksplisit. Kelas abstrak melayani tujuan penerapan bagian dari fungsionalitas yang diperlukan. Saya sama sekali tidak melihat alasan mengapa kelas yang diwariskan tidak dapat menambahkan setter tanpa melanggar kontrak apa pun.

Berikut ini adalah contoh sederhana dari apa yang saya butuhkan hari ini. Saya akhirnya harus menambahkan setter di antarmuka saya hanya untuk menyiasati ini. Alasan untuk menambahkan setter dan tidak menambahkan, katakanlah, metode SetProp adalah bahwa salah satu implementasi khusus antarmuka menggunakan DataContract / DataMember untuk serialisasi Prop, yang akan dibuat rumit tanpa perlu jika saya harus menambahkan properti lain hanya untuk tujuan tersebut serialisasi.

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

Saya tahu ini hanya posting "2 sen" saya, tetapi saya merasa dengan poster asli dan mencoba merasionalisasi bahwa ini adalah hal yang baik tampaknya aneh bagi saya, terutama mengingat keterbatasan yang sama tidak berlaku ketika mewarisi langsung dari sebuah warisan antarmuka.

Juga menyebutkan tentang menggunakan baru alih-alih menimpa tidak berlaku di sini, itu hanya tidak berfungsi dan bahkan jika itu tidak akan memberi Anda hasil yang diinginkan, yaitu pengambil virtual seperti yang dijelaskan oleh antarmuka.

JJJ
sumber
2
Atau, dalam kasus saya, {get; set pribadi; }. Saya tidak melanggar kontrak apa pun karena set tetap pribadi dan hanya peduli dengan kelas turunan.
Machtyn
16

Itu mungkin

tl; dr - Anda dapat mengganti metode get-only dengan setter jika Anda mau. Ini pada dasarnya hanya:

  1. Buat newproperti yang memiliki a getdan a setmenggunakan nama yang sama.

  2. Jika Anda tidak melakukan hal lain, maka getmetode lama akan tetap dipanggil ketika kelas turunan dipanggil melalui tipe dasarnya. Untuk memperbaikinya, tambahkan abstractlapisan perantara yang menggunakan metode overridelama getuntuk memaksanya mengembalikan hasil getmetode baru .

Ini memungkinkan kami untuk menimpa properti dengan get/ setbahkan jika mereka tidak memiliki satu dalam definisi dasar mereka.

Sebagai bonus, Anda juga dapat mengubah jenis pengembalian jika diinginkan.

  • Jika definisi dasar adalah get-hanya, maka Anda dapat menggunakan tipe pengembalian yang lebih diturunkan.

  • Jika definisi dasar adalah set-hanya, maka Anda dapat menggunakan tipe pengembalian yang kurang diturunkan.

  • Jika definisi dasar sudah get/ set, maka:

    • Anda dapat menggunakan tipe pengembalian yang lebih diturunkan jika Anda membuatnya set-hanya;

    • Anda bisa menggunakan tipe pengembalian yang kurang diturunkan jika Anda membuatnya get-hanya.

Dalam semua kasus, Anda dapat menyimpan tipe pengembalian yang sama jika Anda mau. Contoh di bawah ini menggunakan tipe pengembalian yang sama untuk kesederhanaan.

Situasi: getProperti -hanya ada sebelumnya

Anda memiliki beberapa struktur kelas yang tidak dapat Anda modifikasi. Mungkin hanya satu kelas, atau itu pohon warisan yang sudah ada sebelumnya. Apa pun masalahnya, Anda ingin menambahkan setmetode ke properti, tetapi tidak bisa.

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

Masalah: Tidak bisa overridedengan get-hanya dengan get/set

Anda ingin overridedengan get/ setproperti, tetapi tidak dapat dikompilasi.

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

Solusi: Gunakan abstractlapisan perantara

Meskipun Anda tidak dapat langsung overridedengan get/ setproperti, Anda dapat :

  1. Buat new get/ setproperti dengan nama yang sama.

  2. overridegetmetode lama dengan accessor ke getmetode baru untuk memastikan konsistensi.

Jadi, pertama Anda menulis abstractlapisan perantara:

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

Kemudian, Anda menulis kelas yang tidak akan dikompilasi sebelumnya. Ini akan mengkompilasi kali ini karena Anda tidak benar override- benar menggunakan getproperti -hanya; alih-alih, Anda menggantinya menggunakan newkata kunci.

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

Hasil: Semuanya berfungsi!

Jelas, ini berfungsi seperti yang dimaksudkan untuk 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.

Semuanya masih berfungsi sebagaimana dimaksudkan saat melihat Dsebagai salah satu kelas dasar, misalnya Aatau B. Tapi, alasan mengapa itu bekerja mungkin sedikit kurang jelas.

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.

Namun, definisi kelas dasar getmasih pada akhirnya ditimpa oleh definisi kelas turunan get, sehingga masih sepenuhnya konsisten.

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.

Diskusi

Metode ini memungkinkan Anda untuk menambahkan setmetode ke getproperti -hanya. Anda juga dapat menggunakannya untuk melakukan hal-hal seperti:

  1. Ubah properti apa pun menjadi properti get-only, set-only, atau get-and- set, terlepas dari apa itu di kelas dasar.

  2. Ubah tipe pengembalian metode dalam kelas turunan.

Kelemahan utama adalah bahwa ada lebih banyak pengkodean untuk dilakukan dan tambahan abstract classdi pohon warisan. Ini bisa sedikit menjengkelkan dengan konstruktor yang mengambil parameter karena harus disalin / ditempelkan di lapisan perantara.

Nat
sumber
Berikan pertanyaannya sendiri: stackoverflow.com/questions/22210971
Nat
Pendekatan yang bagus (+1). Namun saya ragu apakah ini akan bekerja dengan lancar dengan kerangka kerja entitas.
Mike de Klerk
9

Saya setuju bahwa tidak bisa mengganti pengambil dalam tipe turunan adalah anti-pola. Read-Only menentukan kurangnya implementasi, bukan kontrak fungsional murni (tersirat oleh jawaban suara teratas).

Saya menduga Microsoft memiliki batasan ini baik karena kesalahpahaman yang sama dipromosikan, atau mungkin karena menyederhanakan tata bahasa; meskipun, sekarang ruang lingkup itu dapat diterapkan untuk mendapatkan atau mengatur secara individual, mungkin kita bisa berharap menimpanya juga.

Kesalahpahaman yang ditunjukkan oleh jawaban suara terbanyak, bahwa properti baca-saja entah bagaimana harus lebih "murni" daripada properti baca / tulis itu konyol. Cukup lihat banyak properti read only yang umum dalam framework; nilainya tidak konstan / murni fungsional; misalnya, DateTime. Sekarang hanya bisa dibaca, tetapi apa pun kecuali nilai fungsional murni. Upaya 'cache' nilai properti hanya baca dengan asumsi akan mengembalikan nilai yang sama lain kali berisiko.

Bagaimanapun, saya telah menggunakan salah satu strategi berikut untuk mengatasi keterbatasan ini; keduanya kurang sempurna, tetapi akan memungkinkan Anda pincang melampaui kekurangan bahasa ini:

   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;
   }
T.Tobler
sumber
DateTime. Sekarang berfungsi murni, karena tidak memiliki efek samping (fakta bahwa perlu waktu untuk mengeksekusi mungkin bisa disebut efek samping, tetapi karena waktu eksekusi - setidaknya ketika dibatasi dan pendek - tidak dianggap efek samping dari metode lain, saya tidak akan menganggapnya sebagai salah satu di sini).
supercat
1
Hai supercat. Anda benar tentang efek samping yang dapat diamati secara eksternal mencegah fungsi menjadi murni, tetapi kendala lainnya adalah bahwa nilai hasil fungsi murni hanya harus bergantung pada parameter. Properti statis murni, sebagai suatu fungsi, oleh karena itu harus tidak berubah dan konstan. Kompiler canggih dapat mengganti semua panggilan berikutnya ke fungsi murni tanpa parameter dengan hasil yang dikembalikan oleh panggilan pertama.
T.Tobler
Anda benar bahwa terminologi saya tidak tepat, tetapi saya pikir kesesuaian dari sesuatu menjadi properti hanya-baca daripada metode tergantung pada kurangnya efek samping, daripada kemurnian.
supercat
2

Anda mungkin dapat mengatasi masalah dengan membuat properti baru:

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

int IBase.Bar { 
  get { return Bar; }
}
Hallgrim
sumber
4
Tapi Anda bisa melakukan ini hanya ketika mengimplementasikan antarmuka, bukan ketika mewarisi dari kelas dasar. Anda harus memilih nama lain.
svick
Saya berasumsi contoh kelas mengimplementasikan IBase (sesuatu seperti antarmuka publik IBase {int Bar {get;}}), jika tidak dalam mengimplementasikan kelas, int IBase.Bar {...} tidak diperbolehkan. Kemudian Anda cukup membuat Bar properti biasa dengan get dan set, meskipun properti Bar dari antarmuka tidak memerlukan set.
Ibrahim ben Salah
1

Saya dapat memahami semua poin Anda, tetapi secara efektif, properti otomatis C # 3.0 menjadi tidak berguna dalam hal ini.

Anda tidak dapat melakukan hal seperti itu:

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

IMO, C # seharusnya tidak membatasi skenario seperti itu. Merupakan tanggung jawab pengembang untuk menggunakannya sesuai kebutuhan.

Thomas Danecker
sumber
Saya tidak mengerti maksud Anda. Apakah Anda setuju bahwa C # harus mengizinkan penambahan setter, atau apakah Anda dengan sebagian besar orang lain yang menjawab pertanyaan ini?
ripper234
2
Seperti yang saya katakan, IMO, C # harus memungkinkan penambahan setter. Merupakan tanggung jawab pengembang untuk menggunakannya sesuai kebutuhan.
Thomas Danecker
1

Masalahnya adalah bahwa untuk alasan apa pun Microsoft memutuskan bahwa harus ada tiga jenis properti: baca-saja, tulis-saja, dan baca-tulis, hanya satu yang mungkin ada dengan tanda tangan yang diberikan dalam konteks tertentu; properti hanya dapat diganti oleh properti yang dideklarasikan secara identik. Untuk melakukan apa yang Anda inginkan, Anda perlu membuat dua properti dengan nama dan tanda tangan yang sama - satu di antaranya hanya baca, dan satu di antaranya adalah baca-tulis.

Secara pribadi, saya berharap bahwa seluruh konsep "properti" dapat dihapuskan, kecuali bahwa sintaksis properti-ish dapat digunakan sebagai gula sintaksis untuk memanggil metode "get" dan "set". Ini tidak hanya memfasilitasi opsi 'add set', tetapi juga akan memungkinkan 'get' untuk mengembalikan tipe yang berbeda dari 'set'. Sementara kemampuan seperti itu tidak akan sering digunakan, kadang-kadang bisa berguna untuk memiliki metode 'dapatkan' mengembalikan objek pembungkus sementara 'set' dapat menerima pembungkus atau data aktual.

supercat
sumber
0

Berikut ini solusi untuk mencapai hal ini menggunakan Refleksi:

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

Dengan cara ini kita dapat menetapkan nilai objek pada properti yang hanya bisa dibaca. Mungkin tidak akan berfungsi di semua skenario!

pengguna2514880
sumber
1
Apakah Anda yakin dapat memanggil penyetel melalui refleksi yang tidak ada (seperti yang terjadi pada properti khusus pengambil yang dijelaskan)?
ATAU Mapper
0

Anda harus mengubah judul pertanyaan Anda menjadi detail bahwa pertanyaan Anda semata-mata berkaitan dengan mengesampingkan properti abstrak, atau bahwa pertanyaan Anda adalah sehubungan dengan umumnya mengesampingkan properti hanya-mendapatkan kelas.


Jika yang pertama (mengesampingkan properti abstrak)

Kode itu tidak berguna. Kelas dasar saja seharusnya tidak memberi tahu Anda bahwa Anda dipaksa untuk menimpa properti Get-Only (Mungkin Antarmuka). Kelas dasar menyediakan fungsionalitas umum yang mungkin memerlukan input spesifik dari kelas pelaksana. Oleh karena itu, fungsi umum dapat membuat panggilan ke properti atau metode abstrak. Dalam kasus yang diberikan, metode fungsionalitas umum harus meminta Anda untuk mengganti metode abstrak seperti:

public int GetBar(){}

Tetapi jika Anda tidak memiliki kendali atas hal itu, dan fungsionalitas kelas dasar membaca dari milik publiknya sendiri (aneh), maka lakukan saja ini:

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

Saya ingin menunjukkan komentar (aneh): Saya akan mengatakan praktik terbaik adalah untuk kelas tidak menggunakan properti publiknya sendiri, tetapi untuk menggunakan bidang privat / dilindungi ketika ada. Jadi ini adalah pola yang lebih baik:

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

Jika yang terakhir (menimpa properti get-only kelas)

Setiap properti non-abstrak memiliki setter. Kalau tidak, itu tidak berguna dan Anda tidak perlu peduli untuk menggunakannya. Microsoft tidak harus mengizinkan Anda untuk melakukan apa yang Anda inginkan. Alasannya: setter ada dalam beberapa bentuk atau lainnya, dan Anda dapat mencapai apa yang Anda inginkan dengan mudah kepada Veerryy .

Kelas dasar, atau kelas mana pun tempat Anda dapat membaca properti {get;}, memiliki BEBERAPA jenis penyetel terbuka untuk properti itu. Metadata akan terlihat seperti ini:

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

Tetapi implementasinya akan memiliki dua ujung spektrum kompleksitas:

Kompleks Paling Sedikit:

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

Paling Kompleks:

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

Maksud saya, bagaimanapun, Anda memiliki setter terbuka. Dalam kasus Anda, Anda mungkin ingin menimpa int Barkarena Anda tidak ingin kelas dasar menanganinya, tidak memiliki akses untuk meninjau bagaimana penanganannya, atau ditugaskan untuk membajak beberapa kode nyata dengan cepat dan tidak bertentangan dengan keinginan Anda .

Dalam Latter dan Former (Kesimpulan)

Cerpen Panjang: Tidak perlu bagi Microsoft untuk mengubah apa pun. Anda dapat memilih bagaimana kelas implementasi Anda diatur dan, tanpa konstruktor, gunakan semua atau tidak ada kelas dasar.

Suamere
sumber
0

Solusi hanya untuk sebagian kecil kasus penggunaan, namun demikian: di C # 6.0 setter "readonly" secara otomatis ditambahkan untuk properti pengambil-override saja.

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

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

    public ConcreteClass(int bar)
    {
        Bar = bar;
    }
}
lxa
sumber
-1

Karena itu akan mematahkan konsep enkapsulasi dan implementasi persembunyian. Pertimbangkan kasus ketika Anda membuat kelas, kirimkan, dan kemudian konsumen kelas Anda membuat dirinya dapat mengatur properti yang awalnya Anda sediakan hanya untuk pengambil. Ini akan secara efektif mengganggu setiap invarian dari kelas Anda yang dapat Anda andalkan dalam implementasi Anda.

petr k.
sumber
1
-1: Memiliki setter tidak secara otomatis memungkinkan keturunan untuk memecahkan invarian. Penyetel masih bisa hanya mengubah hal-hal yang sudah bisa diubah oleh keturunannya.
Roman Starkov
@ RomanStarkov Itu benar. Tetapi pertanyaan dari posting ini adalah; Mengapa tidak bisa menambahkan setter ke abstraksi yang mengontrak Anda untuk tidak melakukannya. Komentar Anda akan sesuai untuk pertanyaan sebaliknya; Mengapa Microsoft mengizinkan Anda untuk membuat properti ReadOnly dalam abstraksi jika turunan memiliki kontrol penuh terhadap bidang privasinya?
Suamere
+1: Pertanyaan ini tentang mengapa Microsoft tidak mengizinkan Anda memutuskan kontrak. Meskipun alasan untuk downvotes Anda lebih lanjut tentang mengapa Microsoft mengizinkan kontrak untuk memasukkan properti hanya baca saja. Jadi jawaban ini benar dalam konteksnya.
Suamere
@Suamere kontrak tidak menyatakan bahwa properti tidak dapat diubah. Semua yang dinyatakannya adalah bahwa ada properti yang dapat dibaca. Menambahkan setter tidak akan merusak kontrak ini. Anda dapat menafsirkannya sebagai niat untuk membuat properti hanya-baca, tetapi Anda tidak dapat menjamin dalam C # 6 bahwa tidak ada cara untuk mengubahnya, maka kontrak yang Anda pikirkan sebenarnya tidak ada di sana. Pikirkan tentang sebuah antarmuka: ia dapat menawarkan kontrak pada properti gettable (bukan get-only!). Antarmuka ini dapat diimplementasikan oleh properti get + set saja.
Roman Starkov
Anda mendekati ini dari perspektif penulis konkret. Dia bisa mengubahnya sesukanya. Saya mendekatinya dari perspektif penulis kelas dasar -> Mendekati sebagai konsumen hasil akhir dari kelas pelaksana. Penulis kelas dasar tidak ingin konsumen dari kelas beton masa depan dapat mengubah properti itu tanpa penulis kelas beton mengekspos metode khusus untuk menangani kasus per kasus. Karena di kelas dasar, properti itu diperlakukan tidak berubah, dan yang dikomunikasikan dengan hanya mengeksposnya seperti itu.
Suamere
-1

Ini bukan tidak mungkin. Anda cukup menggunakan kata kunci "baru" di properti Anda. Sebagai contoh,

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

Tampaknya pendekatan ini memuaskan kedua belah pihak dari diskusi ini. Menggunakan "baru" mematahkan kontrak antara implementasi kelas dasar dan implementasi subkelas. Ini diperlukan ketika suatu Kelas dapat memiliki beberapa kontrak (baik melalui antarmuka atau kelas dasar).

Semoga ini membantu

mbolt35
sumber
29
Jawaban ini salah karena properti yang ditandai newtidak lagi mengesampingkan basis virtual. Secara khusus, jika properti dasar adalah abstrak , seperti di posting asli, tidak mungkin untuk menggunakan newkata kunci dalam subkelas non-abstrak karena Anda harus mengganti semua anggota abstrak.
Timwi
Ini sangat berbahaya karena berikut ini memberikan hasil yang berbeda walaupun memiliki objek yang sama: Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)memberi Anda 5, Console.WriteLine(b.BaseProperty)memberi Anda 0, dan ketika penerus Anda harus men-debug ini, Anda sebaiknya pindah jauh, jauh.
Mark Sowul
Saya setuju dengan Mark, ini sangat tidak menyenangkan. Jika kedua implementasi BaseProperty didukung oleh variabel yang sama itu akan baik-baik saja meskipun IMHO. IE: Jadikan _baseProperty terlindungi dan hapus _testBaseProperty. Juga, AFAIK implementasi di Base tidak perlu virtual karena Anda tidak benar-benar menimpanya.
Steazy
Sebagian besar jawaban untuk pertanyaan dapat memiliki opini dan masukan yang valid. Tetapi terlepas dari komentar sebelumnya, kritik utama saya pada jawaban ini adalah bahwa saya percaya pertanyaan aktual mungkin secara harfiah tentang sifat abstrak dalam basis kode yang konsumen tidak memiliki kendali atas. Jadi kritik saya adalah bahwa jawaban ini mungkin tidak cocok dengan pertanyaan itu.
Suamere
-1

Properti read-only di kelas dasar menunjukkan bahwa properti ini mewakili nilai yang selalu dapat ditentukan dari dalam kelas (misalnya nilai enum yang cocok dengan konteks (db-) objek). Jadi, tanggung jawab menentukan nilai tetap berada di dalam kelas.

Menambahkan setter akan menyebabkan masalah canggung di sini: Kesalahan validasi harus terjadi jika Anda menetapkan nilai ke hal lain selain dari nilai tunggal yang mungkin sudah dimiliki.

Namun, aturan sering kali memiliki pengecualian. Sangat mungkin bahwa misalnya dalam satu kelas turunan konteks mempersempit nilai enum yang mungkin turun menjadi 3 dari 10, namun pengguna objek ini masih perlu memutuskan mana yang benar. Kelas turunan perlu mendelegasikan tanggung jawab menentukan nilai kepada pengguna objek ini. Penting untuk disadari adalah bahwa pengguna objek ini harus menyadari pengecualian ini dan memikul tanggung jawab untuk menetapkan nilai yang benar.

Solusi saya dalam situasi seperti ini adalah meninggalkan properti read-only dan menambahkan properti read-write baru ke kelas turunan untuk mendukung pengecualian. Override dari properti asli hanya akan mengembalikan nilai properti baru. Properti baru dapat memiliki nama yang tepat yang menunjukkan konteks pengecualian ini dengan benar.

Ini juga mendukung pernyataan yang sah: "buat sesulit mungkin untuk kesalahpahaman muncul" oleh Gishu.

Remco te Wierik
sumber
1
Tidak ada kecanggungan yang akan tersirat oleh ReadableMatrixkelas (dengan properti terindeks read-only dua argumen) yang memiliki subtipe ImmutableMatrixdan MutableMatrix. Kode yang hanya perlu membaca matriks, dan tidak peduli apakah itu mungkin berubah beberapa waktu di masa depan, dapat menerima parameter jenis ReadableMatrixdan sangat senang dengan subtipe yang bisa berubah atau tidak berubah.
supercat
Setter tambahan dapat memodifikasi variabel pribadi di dalam kelas. Properti yang disebut "read-only" dari kelas dasar akan tetap menentukan nilai "dari dalam kelas" (dengan membaca variabel pribadi baru). Saya tidak melihat masalah di sana. Lihat juga, misalnya, apa yang List<T>.Countdilakukan: Ini tidak dapat ditetapkan, tetapi itu tidak berarti "hanya-baca" karena tidak dapat dimodifikasi oleh pengguna kelas. Itu dapat dimodifikasi dengan sangat baik, meskipun secara tidak langsung, dengan menambahkan dan menghapus item daftar.
ATAU Mapper
-2

Karena pada tingkat IL, properti baca / tulis diterjemahkan menjadi dua metode (pengambil dan penyetel).

Saat mengganti, Anda harus tetap mendukung antarmuka yang mendasarinya. Jika Anda bisa menambahkan setter, Anda akan secara efektif menambahkan metode baru, yang akan tetap tidak terlihat oleh dunia luar, sejauh menyangkut antarmuka kelas Anda.

Benar, menambahkan metode baru tidak akan melanggar kompatibilitas per se, tetapi karena itu akan tetap tersembunyi, keputusan untuk melarang ini masuk akal.

Ishmaeel
sumber
2
Saya tidak terlalu peduli tentang "dunia luar" di sini. Saya ingin menambahkan fungsionalitas ke kelas, yang selalu terlihat hanya dengan kode yang mengetahui kelas tertentu dan bukan basis.
ripper234
@ jawaban ripper234 See Matt baut ini: jika Anda ingin membuat properti baru terlihat hanya untuk pengguna kelas turunan Anda harus menggunakan newbukan override.
Dan Berindei
1
Itu tidak tetap tersembunyi. Anda seperti mengatakan Anda tidak dapat menambahkan metode baru ke kelas anak-anak karena metode baru akan "tetap tersembunyi dari dunia luar". Tentu saja itu tetap tersembunyi dari perspektif kelas dasar, tetapi dapat diakses dari klien kelas anak. (Yaitu untuk memengaruhi apa yang harus dikembalikan oleh rajin)
Sheepy
Sheepy, bukan itu yang aku katakan sama sekali. Maksudku, persis seperti yang Anda tunjukkan, itu akan tetap tersembunyi "dari perspektif kelas dasar". Itu sebabnya saya menambahkan "sejauh menyangkut antarmuka kelas Anda ". Jika Anda mensubkelas kelas dasar, hampir pasti bahwa "dunia luar" akan merujuk ke instance objek Anda dengan tipe kelas dasar. Jika mereka akan langsung menggunakan kelas beton Anda, Anda tidak akan membutuhkan kompatibilitas dengan kelas dasar dan Anda bisa, dengan segala cara, penggunaan newdengan tambahan Anda.
Ishmaeel
-2

Karena kelas yang memiliki properti baca-saja (tanpa setter) mungkin memiliki alasan yang bagus untuk itu. Mungkin tidak ada datastore yang mendasarinya, misalnya. Mengizinkan Anda membuat setter mematahkan kontrak yang ditetapkan oleh kelas. Itu hanya OOP yang buruk.

Joel Coehoorn
sumber
Up-Vote. Ini sangat ringkas. Mengapa Microsoft harus mengizinkan konsumen kelas dasar untuk memutus kontrak? Saya setuju dengan versi singkat dari jawaban saya yang terlalu panjang dan membingungkan ini. Lol. Jika Anda adalah konsumen kelas dasar, Anda seharusnya tidak ingin melanggar kontrak, tetapi Anda BISA menerapkannya dalam beberapa cara yang berbeda.
Suamere