เหตุใดจึงเป็นไปไม่ได้ที่จะแทนที่คุณสมบัติ getter เท่านั้นและเพิ่มตัวตั้งค่า [ปิด]


141

ทำไมรหัส C # ต่อไปนี้ไม่ได้รับอนุญาต:

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' ไม่มี accessor set overridable


9
StackOverflow มีความสามารถ จำกัด ในการตอบคำถามในนามของ Microsoft ลองทบทวนคำถามของคุณใหม่
Jay Bazuzi

1
อนึ่งวิธีการรักษาที่ง่ายสำหรับพฤติกรรมที่น่ารำคาญนี้ใน. net คือการมีคุณสมบัติแบบอ่านอย่างเดียวที่ไม่ใช่เสมือนFooซึ่งไม่ทำอะไรนอกจากห่อหุ้มGetFooวิธีนามธรรมที่ได้รับการป้องกัน ประเภทที่ได้มานามธรรมสามารถเงาทรัพย์สินที่มีคุณสมบัติอ่านเขียนเขียนที่ไม่ใช่เสมือนซึ่งไม่ทำอะไรเลย แต่ห่อGetFooวิธีดังกล่าวพร้อมกับSetFooวิธีการนามธรรม คอนกรีตมาชนิดสามารถทำข้างต้น แต่อุปทานคำจำกัดความและGetFoo SetFoo
supercat

1
เพียงเพื่อเพิ่มเชื้อเพลิงลงในกองไฟ C ++ / CLI จะอนุญาตสิ่งนี้
ymett

1
ความสามารถในการเพิ่ม accessor เมื่อแทนที่คุณสมบัติได้รับการเสนอเป็นการเปลี่ยนแปลงสำหรับรุ่น C # ในอนาคต ( github.com/dotnet/roslyn/issues/9482 )
แบรนดอนบอนด์

1
คำถามนี้มักเกิดขึ้นเมื่อมีความจำเป็นที่จะต้องขยายประเภทไลบรารีที่มีอยู่ซึ่งใช้รูปแบบต่อไปนี้: คลาสพื้นฐานแบบอ่านอย่างเดียวคลาสที่ไม่สามารถแปลงได้ เนื่องจากไลบรารีใช้รูปแบบที่ไม่ดี (เช่น WPF) อยู่แล้วความต้องการจะเกิดขึ้นเพื่อแก้ไขรูปแบบที่ไม่ดีนี้ การบรรยายไม่ได้ช่วยให้ใครคนหนึ่งต้องใช้วิธีการแก้ปัญหาในตอนท้ายของวัน

คำตอบ:


-4

เนื่องจากผู้เขียน Baseclass ได้ประกาศอย่างชัดเจนว่าบาร์จะต้องมีคุณสมบัติแบบอ่านอย่างเดียว มันไม่สมเหตุสมผลเลยที่อนุพันธ์จะทำสัญญานี้และอ่านเขียน

ฉันอยู่กับ Microsoft ในอันนี้
สมมติว่าฉันเป็นโปรแกรมเมอร์คนใหม่ที่ได้รับคำสั่งให้เขียนโค้ดกับ Baseclass ที่มา ฉันเขียนบางอย่างที่ถือว่าแถบนั้นไม่สามารถเขียนได้ (เนื่องจาก Baseclass ระบุไว้อย่างชัดเจนว่าเป็นคุณสมบัติที่ได้รับเท่านั้น) ขณะนี้ด้วยการสืบทอดของคุณรหัสของฉันอาจแตก เช่น

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

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

  public Bar getBar()
  { return _currentBar;  }
}

เนื่องจาก Bar ไม่สามารถตั้งค่าได้ตามอินเตอร์เฟส BaseClass BarProvider จะถือว่าการแคชเป็นสิ่งที่ปลอดภัยที่ต้องทำ - เนื่องจาก Bar ไม่สามารถแก้ไขได้ แต่ถ้าตั้งค่าเป็นไปได้ในการสืบทอดคลาสนี้อาจให้บริการค่าเก่าถ้ามีคนแก้ไขคุณสมบัติของแถบวัตถุ _source ภายนอก ประเด็นคือ ' เปิดกว้างหลีกเลี่ยงการทำสิ่งที่ไม่เหมาะสมและผู้คนที่น่าประหลาดใจ '

อัปเดต : Ilya Ryzhenkov ถามว่า 'ทำไมอินเทอร์เฟซไม่เล่นตามกฎเดียวกันแล้ว' อืมมมมมมมมมกว่าอย่างที่คิด
อินเทอร์เฟซคือสัญญาที่ระบุว่า 'คาดหวังให้มีการนำไปใช้งานเพื่อให้มีคุณสมบัติการอ่านชื่อบาร์' โดยส่วนตัวแล้วฉันมีโอกาสน้อยมากที่จะตั้งสมมติฐานว่าเป็นแบบอ่านอย่างเดียวถ้าฉันเห็นอินเทอร์เฟซ เมื่อฉันเห็นพร็อพเพอร์ตี้เฉพาะบนอินเทอร์เฟซฉันอ่านมันเป็น 'การใช้งานใด ๆ ที่จะเปิดเผยแถบคุณลักษณะนี้' ... ในคลาสฐานที่คลิกเมื่อ 'บาร์เป็นคุณสมบัติแบบอ่านอย่างเดียว' แน่นอนว่าในทางเทคนิคคุณไม่ได้ทำผิดสัญญา .. คุณทำอะไรได้มากกว่านี้ ดังนั้นคุณอยู่ในความรู้สึก .. ฉันจะปิดโดยพูดว่า 'ทำให้มันยากที่สุดสำหรับความเข้าใจผิดที่จะปลูกพืช'


94
ฉันยังไม่เชื่อ. แม้ว่าจะไม่มีตัวตั้งค่าที่ชัดเจน แต่ก็ไม่ได้รับประกันว่าคุณสมบัติจะส่งคืนออบเจ็กต์เดียวกันเสมอ แต่ก็ยังสามารถเปลี่ยนแปลงได้ด้วยวิธีอื่น
ripper234

34
ไม่ควรเหมือนกันสำหรับอินเทอร์เฟซใช่ไหม คุณสามารถมีคุณสมบัติแบบอ่านอย่างเดียวบนอินเตอร์เฟสและอ่าน / เขียนตามชนิดการนำไปใช้
Ilya Ryzhenkov

49
ฉันไม่เห็นด้วย: คลาสฐานประกาศคุณสมบัติว่าไม่มีตัวตั้งเท่านั้น ที่ไม่เหมือนกับคุณสมบัติที่อ่านได้อย่างเดียว "อ่านอย่างเดียว" เป็นเพียงความหมายที่คุณกำหนด ไม่มีการรับประกันที่เกี่ยวข้องที่สร้างขึ้นใน C # เลย คุณอาจมีคุณสมบัติได้รับเท่านั้นที่มีการเปลี่ยนแปลงทุกครั้งหรือได้รับ / InvalidOperationException("This instance is read-only")ตั้งค่าคุณสมบัติที่หมาพ่น
Roman Starkov

21
ดูที่ getters และ setters เป็นวิธีฉันไม่เห็นว่าทำไมมันควรจะ "ผิดสัญญา" มากกว่าการเพิ่มวิธีการใหม่ สัญญาระดับฐานไม่มีส่วนเกี่ยวข้องกับฟังก์ชันการทำงานที่ไม่ได้แสดงเฉพาะกับสิ่งที่มีอยู่
Dave Cousineau

37
-1 ฉันไม่เห็นด้วยกับคำตอบนี้และพบว่ามันเข้าใจผิด เนื่องจากคลาสพื้นฐานกำหนดพฤติกรรมที่คลาสใดต้องปฏิบัติ (ดูหลักการการทดแทนของ Liskov) แต่ไม่ จำกัด (และไม่ควร) เพื่อเพิ่มพฤติกรรม คลาสพื้นฐานอาจกำหนดพฤติกรรมที่ต้องเป็นเท่านั้นและไม่สามารถ (และไม่ควร) ระบุพฤติกรรมที่ 'ต้องไม่เป็น' (เช่น 'ต้องไม่สามารถเขียนได้ในระดับคอนกรีต)'
Marcel Valdez Orozco

39

ฉันคิดว่าเหตุผลหลักก็คือว่าไวยากรณ์นั้นชัดเจนเกินไปสำหรับการทำงานอื่น ๆ รหัสนี้:

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

ค่อนข้างชัดเจนว่าทั้งgetและsetถูกแทนที่ ไม่มีsetในคลาสพื้นฐานดังนั้นคอมไพเลอร์บ่น เช่นเดียวกับที่คุณไม่สามารถแทนที่วิธีที่ไม่ได้กำหนดในคลาสพื้นฐานคุณไม่สามารถแทนที่ตัวตั้งค่าได้เช่นกัน

คุณอาจบอกว่าคอมไพเลอร์ควรคาดเดาความตั้งใจของคุณและใช้การแทนที่เฉพาะกับวิธีการที่สามารถแทนที่ได้ (เช่น getter ในกรณีนี้) แต่สิ่งนี้ขัดกับหลักการออกแบบ C # ข้อใดข้อหนึ่ง - ว่าผู้แปลต้องเดาความตั้งใจของคุณ เพราะมันอาจเดาผิดโดยที่คุณไม่รู้

ฉันคิดว่าไวยากรณ์ต่อไปนี้อาจทำได้เป็นอย่างดี แต่อย่างที่ Eric Lippert พูดต่อไปการใช้งานแม้แต่คุณสมบัติเล็กน้อยเช่นนี้ยังคงเป็นความพยายามที่สำคัญ ...

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

หรือสำหรับคุณสมบัติการนำไปใช้โดยอัตโนมัติ

public int MyProperty { override get; set; }

19

ฉันเจอปัญหาเดียวกันนี้วันนี้และฉันคิดว่ามีเหตุผลที่ถูกต้องมากสำหรับการต้องการสิ่งนี้

ก่อนอื่นฉันต้องการยืนยันว่าการมีคุณสมบัติแบบ get-only ไม่จำเป็นต้องแปลเป็นแบบอ่านอย่างเดียว ฉันแปลมันเป็น "จากอินเทอร์เฟซนี้ / abtract คุณสามารถรับค่านี้" นั่นไม่ได้หมายความว่าการใช้งานอินเทอร์เฟซ / คลาสนามธรรมบางอย่างนั้นไม่ต้องการให้ผู้ใช้ / โปรแกรมตั้งค่านี้อย่างชัดเจน คลาสนามธรรมมีจุดประสงค์เพื่อนำไปใช้เป็นส่วนหนึ่งของฟังก์ชันที่จำเป็น ฉันไม่เห็นเหตุผลใด ๆ ที่ว่าทำไมคลาสที่สืบทอดจะไม่สามารถเพิ่มเซทเทอร์โดยไม่ละเมิดสัญญาใด ๆ

ต่อไปนี้เป็นตัวอย่างที่เรียบง่ายของสิ่งที่ฉันต้องการในวันนี้ ในที่สุดฉันก็ต้องเพิ่มตัวตั้งค่าในอินเทอร์เฟซของฉันเพื่อแก้ไขปัญหานี้ เหตุผลในการเพิ่มตัวเซ็ตเตอร์และไม่เพิ่ม, พูด, เมธอด SetProp ก็คือการใช้งานอินเทอร์เฟซที่ใช้ DataContract / DataMember สำหรับการจัดลำดับของ Prop ซึ่งจะทำให้ซับซ้อนโดยไม่จำเป็นหากฉันต้องเพิ่มคุณสมบัติอื่นเพื่อจุดประสงค์ ของการเป็นอันดับ

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

ฉันรู้ว่านี่เป็นเพียงการโพสต์ "2 เซ็นต์ของฉัน" แต่ฉันรู้สึกกับโปสเตอร์ต้นฉบับและพยายามหาเหตุผลเข้าข้างตนเองว่านี่เป็นสิ่งที่ดีดูเหมือนแปลกสำหรับฉันโดยเฉพาะอย่างยิ่งการพิจารณาว่าข้อ จำกัด เดียวกันไม่ได้ใช้เมื่อสืบทอดโดยตรงจาก อินเตอร์เฟซ.

นอกจากนี้การกล่าวถึงการใช้ใหม่แทนการแทนที่ไม่ได้ใช้ที่นี่มันใช้งานไม่ได้และแม้ว่ามันจะไม่ให้ผลลัพธ์ที่คุณต้องการนั่นคือ virtual getter เสมือนที่อธิบายโดยอินเตอร์เฟส


2
หรือในกรณีของฉัน {รับ; ชุดส่วนตัว } ฉันไม่ได้ละเมิดสัญญาใด ๆ เนื่องจากเซตยังคงเป็นส่วนตัวและเกี่ยวข้องกับชั้นอนุพันธ์เท่านั้น
Machtyn

16

มันเป็นไปได้

tl; dr - คุณสามารถแทนที่เมธอด get-only ด้วย setter หากคุณต้องการ มันเป็นเพียงแค่:

  1. สร้างnewคุณสมบัติที่มีทั้ง a getและ a setโดยใช้ชื่อเดียวกัน

  2. ถ้าคุณไม่ทำอะไรอย่างอื่นgetวิธีเก่าจะยังคงถูกเรียกเมื่อคลาสที่ได้รับนั้นถูกเรียกผ่านประเภทฐาน ในการแก้ไขปัญหานี้ให้เพิ่มabstractเลเยอร์กลางที่ใช้overrideกับgetวิธีเก่าเพื่อบังคับให้ส่งคืนgetผลลัพธ์ของวิธีการใหม่

สิ่งนี้ช่วยให้เราสามารถแทนที่คุณสมบัติด้วยget/ setแม้ว่าพวกเขาจะขาดหนึ่งในคำจำกัดความฐานของพวกเขา

เป็นโบนัสคุณสามารถเปลี่ยนประเภทผลตอบแทนได้หากต้องการ

  • หากคำจำกัดความฐานคือget- เท่านั้นคุณสามารถใช้ประเภทผลตอบแทนที่ได้รับมากขึ้น

  • หากคำจำกัดความพื้นฐานคือset- เพียงอย่างเดียวคุณสามารถส่งคืนชนิดที่ได้รับน้อยลง

  • ถ้าคำจำกัดความพื้นฐานเป็นอยู่แล้วget/ setดังนั้น:

    • คุณสามารถใช้ประเภทผลตอบแทนที่ได้รับมากขึ้นหากคุณทำให้มันset- เท่านั้น;

    • คุณสามารถใช้ประเภทผลตอบแทนที่ได้รับน้อยลงหากคุณทำแบบนี้getเท่านั้น

ในทุกกรณีคุณสามารถใช้ประเภทผลตอบแทนเดิมได้หากต้องการ ตัวอย่างด้านล่างใช้ประเภทผลตอบแทนเดียวกันเพื่อความเรียบง่าย

สถานการณ์: getคุณสมบัติที่มีอยู่แล้วเท่านั้น

คุณมีโครงสร้างคลาสที่คุณไม่สามารถแก้ไขได้ อาจเป็นเพียงคลาสเดียวหรือเป็นแผนผังการสืบทอดที่มีอยู่แล้ว คุณต้องการเพิ่มsetวิธีการในคุณสมบัติ แต่ไม่สามารถทำได้

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

ปัญหา: ไม่สามารถoverrideใช้getร่วมกับget/set

คุณต้องการoverrideด้วยget/ a setคุณสมบัติ แต่มันจะไม่รวบรวม

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

วิธีแก้ไข: ใช้abstractเลเยอร์ระดับกลาง

แม้ว่าคุณจะไม่สามารถoverrideใช้ a get/ setproperty โดยตรงแต่คุณสามารถ :

  1. สร้าง a new get/ setproperty ที่มีชื่อเดียวกัน

  2. overridegetวิธีเก่าที่มี accessor กับgetวิธีการใหม่เพื่อให้แน่ใจว่าสอดคล้อง

ดังนั้นก่อนอื่นให้คุณเขียนabstractเลเยอร์กลาง:

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

จากนั้นคุณเขียนชั้นเรียนที่จะไม่รวบรวมก่อนหน้านี้ มันจะคอมไพล์ในครั้งนี้เพราะคุณไม่ได้อยู่overrideในgetคุณสมบัติ - เพียงอย่างเดียว คุณจะแทนที่ด้วยnewคำหลักแทน

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

ผลลัพธ์: ทุกอย่างใช้งานได้!

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.

ทุกอย่างยังคงทำงานได้ตามที่ตั้งใจไว้เมื่อดูDเป็นหนึ่งในชั้นเรียนฐานเช่นหรือA Bแต่สาเหตุที่ใช้งานอาจไม่ชัดเจนนัก

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.

อย่างไรก็ตามนิยามคลาสพื้นฐานของgetยังคงถูกแทนที่ในที่สุดโดยนิยามของคลาสที่ได้รับgetดังนั้นจึงยังคงสอดคล้องกันอย่างสมบูรณ์

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.

อภิปรายผล

วิธีนี้ช่วยให้คุณสามารถเพิ่มsetวิธีการget- คุณสมบัติเท่านั้น คุณยังสามารถใช้เพื่อทำสิ่งต่าง ๆ เช่น:

  1. เปลี่ยนคุณสมบัติใด ๆ ให้เป็นget-only set-only หรือget-and- setproperty โดยไม่คำนึงถึงสิ่งที่อยู่ในคลาสฐาน

  2. เปลี่ยนชนิดการส่งคืนของเมธอดในคลาสที่ได้รับ

ข้อเสียเปรียบหลักคือมีการเข้ารหัสที่ต้องทำและพิเศษabstract classในแผนผังการสืบทอด สิ่งนี้อาจสร้างความรำคาญให้กับคอนสตรัคเตอร์ที่รับพารามิเตอร์เนื่องจากสิ่งเหล่านั้นจะต้องคัดลอก / วางในเลเยอร์กลาง


ให้คำถามนี้กับตนเอง: stackoverflow.com/questions/22210971
Nat

วิธีการที่ดี (+1) ฉันสงสัยว่าสิ่งนี้จะทำงานได้อย่างราบรื่นกับกรอบงานเอนทิตี้หรือไม่
Mike de Klerk

9

ฉันยอมรับว่าการไม่สามารถแทนที่ผู้ทะเยอทะยานในรูปแบบที่ได้รับนั้นเป็นรูปแบบการต่อต้าน อ่านอย่างเดียวระบุการขาดการใช้งานไม่ใช่สัญญาของฟังก์ชั่นที่บริสุทธิ์ (โดยนัยโดยคำตอบการโหวตสูงสุด)

ฉันสงสัยว่า Microsoft มีข้อ จำกัด นี้เช่นกันเนื่องจากมีการส่งเสริมความเข้าใจผิดอย่างเดียวกันหรืออาจเป็นเพราะการทำให้ไวยากรณ์ง่ายขึ้น แม้ว่าตอนนี้ขอบเขตนั้นสามารถนำไปใช้เพื่อรับหรือตั้งค่าเป็นรายบุคคลบางทีเราสามารถหวังว่าการลบล้างก็สามารถทำได้เช่นกัน

ความเข้าใจผิดที่ระบุโดยคำตอบการลงคะแนนสูงสุดว่าคุณสมบัติแบบอ่านอย่างเดียวควรเป็น "บริสุทธิ์" มากกว่าคุณสมบัติการอ่าน / เขียนที่ไร้สาระ เพียงดูคุณสมบัติอ่านอย่างเดียวทั่วไปจำนวนมากในกรอบงาน ค่าไม่คงที่ / ทำงานได้อย่างหมดจด ตัวอย่างเช่น DateTime.Now เป็นแบบอ่านอย่างเดียว แต่มีค่าอื่น ๆ ความพยายามในการ 'แคช' ค่าของคุณสมบัติแบบอ่านอย่างเดียวโดยสมมติว่ามันจะส่งคืนค่าเดิมในครั้งต่อไปที่มีความเสี่ยง

ไม่ว่าในกรณีใดฉันได้ใช้หนึ่งในกลยุทธ์ต่อไปนี้เพื่อเอาชนะข้อ จำกัด นี้ ทั้งคู่น้อยกว่าสมบูรณ์แบบ แต่จะช่วยให้คุณปวกเปียกเกินการขาดภาษานี้:

   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 ตอนนี้ทำงานได้อย่างหมดจดโดยไม่มีผลข้างเคียง (ความจริงที่ว่าต้องใช้เวลาในการดำเนินการอาจเรียกได้ว่าเป็นผลข้างเคียง แต่เนื่องจากเวลาดำเนินการ - อย่างน้อยเมื่อมีขอบเขตและสั้น - ไม่ถือว่าเป็น ผลข้างเคียงของวิธีอื่นฉันไม่คิดว่ามันจะเป็นอย่างใดอย่างหนึ่งที่นี่)
supercat

1
สวัสดีจ้า คุณอยู่ที่ถูกต้องเกี่ยวกับผลข้างเคียงที่สังเกตภายนอกฟังก์ชั่นการป้องกันจากการถูกบริสุทธิ์ แต่ข้อ จำกัด อื่น ๆ ที่มีมูลค่าผลฟังก์ชั่นที่บริสุทธิ์ต้องเท่านั้นขึ้นอยู่กับพารามิเตอร์ คุณสมบัติสแตติกบริสุทธิ์ในฐานะฟังก์ชันจึงต้องเป็นทั้งไม่เปลี่ยนรูปและคงที่ คอมไพเลอร์ที่ซับซ้อนสามารถแทนที่การเรียกที่ตามมาทั้งหมดไปยังฟังก์ชันบริสุทธิ์โดยไม่มีพารามิเตอร์ด้วยผลลัพธ์ที่ส่งคืนโดยการโทรครั้งแรก
T.Tobler

คุณถูกต้องว่าคำศัพท์ของฉันไม่แน่ชัด แต่ฉันคิดว่าความเหมาะสมของบางสิ่งบางอย่างที่เป็นคุณสมบัติแบบอ่านอย่างเดียวมากกว่าวิธีหนึ่งขึ้นอยู่กับการขาดผลข้างเคียงมากกว่าความบริสุทธิ์
supercat

2

คุณสามารถแก้ไขปัญหาด้วยการสร้างพื้นที่ใหม่:

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

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

4
แต่คุณสามารถทำได้เมื่อใช้งานอินเตอร์เฟสเท่านั้นไม่ใช่เมื่อรับมรดกจากคลาสพื้นฐาน คุณต้องเลือกชื่ออื่น
svick

ฉันสมมติว่าตัวอย่างคลาสใช้ IBase (คล้ายอินเทอร์เฟซสาธารณะ IBase {int Bar {get;}}) ไม่เช่นนั้นในการใช้คลาสไม่อนุญาตให้ใช้ int IBase.Bar {... } จากนั้นคุณสามารถสร้างแถบคุณสมบัติปกติที่มีทั้งการรับและการตั้งค่าโดยที่คุณสมบัติของแถบจากอินเทอร์เฟซไม่จำเป็นต้องมีการตั้งค่า
อิบราฮิมเบ็

1

ฉันสามารถเข้าใจคะแนนทั้งหมดของคุณ แต่อย่างมีประสิทธิภาพคุณสมบัติอัตโนมัติของ C # 3.0 ทำให้ไร้ประโยชน์ในกรณีนี้

คุณไม่สามารถทำอะไรเช่นนั้น:

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

IMO, C # ไม่ควร จำกัด สถานการณ์ดังกล่าว เป็นความรับผิดชอบของนักพัฒนาที่จะใช้งานได้ตามนั้น


ฉันไม่เข้าใจประเด็นของคุณ คุณเห็นด้วยหรือไม่ว่า C # ควรอนุญาตให้เพิ่มเซทเทอร์หรือคุณกับคนอื่น ๆ ส่วนใหญ่ที่ตอบคำถามนี้หรือไม่?
ripper234

2
อย่างที่ฉันพูด IMO, C # ควรอนุญาตให้เพิ่มตัวตั้งค่า เป็นความรับผิดชอบของนักพัฒนาที่จะใช้งานได้ตามนั้น
Thomas Danecker

1

ปัญหาคือไม่ว่าด้วยเหตุผลใดก็ตาม Microsoft ตัดสินใจว่าควรมีคุณสมบัติที่แตกต่างกันสามประเภท: อ่านอย่างเดียวเขียนอย่างเดียวและอ่านเขียนซึ่งอาจมีอยู่เพียงหนึ่งรายการเท่านั้นที่มีลายเซ็นที่กำหนดในบริบทที่กำหนด คุณสมบัติอาจถูกเขียนทับโดยคุณสมบัติที่ประกาศเหมือนกันเท่านั้น ในการทำสิ่งที่คุณต้องการคุณจำเป็นต้องสร้างคุณสมบัติสองรายการที่มีชื่อและลายเซ็นเหมือนกัน - หนึ่งในนั้นเป็นแบบอ่านอย่างเดียวและหนึ่งในนั้นคืออ่าน - เขียน

โดยส่วนตัวฉันหวังว่าแนวคิดทั้งหมดของ "คุณสมบัติ" อาจถูกยกเลิกยกเว้นว่าไวยากรณ์ของคุณสมบัติ -h สามารถใช้เป็นน้ำตาลประโยคเพื่อเรียกวิธีการ "รับ" และ "ชุด" สิ่งนี้ไม่เพียงอำนวยความสะดวกให้กับตัวเลือก 'เพิ่มชุด' แต่ยังอนุญาตให้ 'รับ' เพื่อส่งคืนประเภทที่แตกต่างจาก 'ชุด' แม้ว่าความสามารถดังกล่าวจะไม่ถูกนำมาใช้บ่อยครั้งมาก แต่บางครั้งก็มีประโยชน์ที่จะให้เมธอด 'get' ส่งคืนออบเจกต์ wrapper ในขณะที่ 'set' สามารถยอมรับทั้ง wrapper หรือข้อมูลจริงได้


0

นี่คือการแก้ไขเพื่อให้บรรลุสิ่งนี้โดยใช้ Reflection:

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

วิธีนี้เราสามารถตั้งค่าวัตถุบนคุณสมบัติที่อ่านได้อย่างเดียว อาจไม่ทำงานในทุกสถานการณ์แม้ว่า!


1
คุณแน่ใจหรือไม่ว่าสามารถเรียกตัวตั้งค่าผ่านการสะท้อนที่ไม่มีอยู่จริง (ตามที่เกิดขึ้นในคุณสมบัติเฉพาะผู้อธิบายเท่านั้น)
หรือผู้ทำแผนที่

0

คุณควรเปลี่ยนชื่อคำถามของคุณเป็นรายละเอียดว่าคำถามของคุณนั้นเกี่ยวกับการเอาชนะคุณสมบัตินามธรรมหรือว่าคำถามของคุณเกี่ยวกับการเอาชนะคุณสมบัติแบบรับเท่านั้นของชั้นเรียน


ถ้าอดีต (แทนที่คุณสมบัตินามธรรม)

รหัสนั้นไร้ประโยชน์ คลาสพื้นฐานเพียงอย่างเดียวไม่ควรบอกคุณว่าคุณถูกบังคับให้แทนที่คุณสมบัติ Get-Only (อาจเป็นอินเตอร์เฟส) คลาสพื้นฐานมีฟังก์ชันการทำงานทั่วไปซึ่งอาจต้องการอินพุตเฉพาะจากคลาสที่ใช้งาน ดังนั้นฟังก์ชั่นทั่วไปอาจทำการเรียกคุณสมบัติหรือวิธีการที่เป็นนามธรรม ในกรณีที่กำหนดวิธีการทำงานทั่วไปควรจะขอให้คุณแทนที่วิธีนามธรรมเช่น:

public int GetBar(){}

แต่ถ้าคุณไม่สามารถควบคุมมันได้และฟังก์ชั่นการทำงานของคลาสฐานจะอ่านจากคุณสมบัติสาธารณะของตัวเอง (แปลก) ดังนั้นให้ทำดังนี้

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

ฉันต้องการชี้ให้เห็นความคิดเห็น (แปลก): ฉันจะบอกว่าวิธีปฏิบัติที่ดีที่สุดสำหรับชั้นเรียนที่จะไม่ใช้คุณสมบัติสาธารณะของตัวเอง แต่ใช้เขตข้อมูลส่วนตัว / ป้องกันเมื่อมีอยู่ ดังนั้นนี่เป็นรูปแบบที่ดีกว่า:

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

ถ้าหลัง (แทนที่คุณสมบัติ get-only ของคลาส)

สถานที่ให้บริการที่ไม่ใช่นามธรรมทุกคนมี setter ไม่อย่างนั้นมันไร้ประโยชน์และคุณไม่ควรใช้มัน Microsoft ไม่จำเป็นต้องอนุญาตให้คุณทำสิ่งที่คุณต้องการ เหตุผล: เซ็ตเตอร์มีอยู่ในรูปแบบใดรูปแบบหนึ่งและคุณสามารถบรรลุสิ่งที่คุณต้องการได้อย่างง่ายดายVeerryy

คลาสฐานหรือคลาสใด ๆ ที่คุณสามารถอ่านคุณสมบัติด้วย{get;}มีsetter ที่เปิดเผยบางส่วนสำหรับคุณสมบัตินั้น ข้อมูลเมตาจะมีลักษณะดังนี้:

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

แต่การดำเนินการจะมีสองด้านของสเปกตรัมของความซับซ้อน:

คอมเพล็กซ์น้อยที่สุด:

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

คอมเพล็กซ์มากที่สุด:

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

ประเด็นของฉันคือไม่ว่าคุณจะเปิดทางใดก็ตาม ในกรณีของคุณคุณอาจต้องการแทนที่int Barเพราะคุณไม่ต้องการให้คลาสฐานจัดการมันไม่มีสิทธิ์เข้าถึงเพื่อตรวจสอบวิธีจัดการกับมันหรือมอบหมายให้ hax บางรหัสอย่างรวดเร็ว .

ทั้งในยุคสุดท้ายและอดีต (บทสรุป)

เรื่องสั้นสั้น: ไม่จำเป็นต้องให้ Microsoft เปลี่ยนแปลงอะไร คุณสามารถเลือกวิธีตั้งค่าคลาสการใช้งานของคุณและสร้างคอนสตรัคชันใช้คลาสพื้นฐานทั้งหมดหรือไม่


0

โซลูชันสำหรับกรณีการใช้งานเพียงเล็กน้อยเท่านั้น แต่อย่างไรก็ตาม: ใน setter C # 6.0 "แบบอ่านอย่างเดียว" จะถูกเพิ่มโดยอัตโนมัติสำหรับคุณสมบัติแบบ getter เท่านั้นที่แทนที่

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

เพราะนั่นจะทำลายแนวคิดของการห่อหุ้มและซ่อนการนำไปปฏิบัติ พิจารณากรณีเมื่อคุณสร้างชั้นเรียนจัดส่งแล้วผู้บริโภคในชั้นเรียนของคุณทำให้ตัวเองสามารถตั้งค่าคุณสมบัติที่คุณให้ทะเยอทะยานในตอนแรกเท่านั้น มันจะรบกวนผู้เรียนในชั้นเรียนของคุณได้อย่างมีประสิทธิภาพซึ่งคุณสามารถพึ่งพาได้ในการนำไปใช้


1
-1: การมีตัวตั้งค่าไม่เปิดใช้งานการสืบทอดโดยอัตโนมัติเพื่อทำลายค่าคงที่ใด ๆ ผู้ตั้งค่ายังสามารถเปลี่ยนแปลงสิ่งต่าง ๆ ที่ผู้สืบทอดสามารถเปลี่ยนแปลงได้อยู่แล้วเท่านั้น
Roman Starkov

@ RomanStarkov นั่นเป็นความจริง แต่คำถามของโพสต์นี้คือ; เหตุใดจึงไม่สามารถเพิ่มตัวตั้งค่าลงในสิ่งที่เป็นนามธรรมซึ่งสัญญากับคุณไม่ได้ ความคิดเห็นของคุณจะเหมาะสมในคำถามตรงข้าม; เหตุใด Microsoft อนุญาตให้คุณสร้างคุณสมบัติ ReadOnly ในนามธรรมถ้าลูกหลานมีการควบคุมแบบเต็มของเขตข้อมูลส่วนตัว
Suamere

+1: คำถามนี้เกี่ยวกับสาเหตุที่ Microsoft ไม่อนุญาตให้คุณทำผิดสัญญา แม้ว่าเหตุผลของการลงคะแนนของคุณเป็นเรื่องเกี่ยวกับสาเหตุที่ Microsoft อนุญาตให้ทำสัญญาเพื่อรวมคุณสมบัติแบบอ่านอย่างเดียวเท่านั้น ดังนั้นคำตอบนี้ถูกต้องในบริบท
Suamere

@ ในกรณีที่สัญญาไม่ได้ระบุว่าไม่สามารถเปลี่ยนแปลงทรัพย์สินได้ ทั้งหมดที่ระบุคือมีคุณสมบัติที่สามารถอ่านได้ การเพิ่มตัวตั้งค่าจะไม่ทำให้สัญญานี้เสียหาย คุณสามารถตีความว่ามันเป็นความตั้งใจที่จะสร้างคุณสมบัติแบบอ่านอย่างเดียว แต่คุณไม่สามารถรับประกันได้ใน C # 6 ว่าไม่มีทางที่จะเปลี่ยนมันดังนั้นสัญญาที่คุณคิดว่าไม่ได้อยู่ที่นั่นจริงๆ คิดเกี่ยวกับอินเทอร์เฟซ: มันสามารถนำเสนอสัญญาในคุณสมบัติ gettable (ไม่ได้รับเท่านั้น!) อินเทอร์เฟซนี้สามารถนำไปใช้โดยรับชุดคุณสมบัติ
Roman Starkov

คุณกำลังเข้าใกล้สิ่งนี้จากมุมมองของผู้เขียนที่เป็นรูปธรรม เขาสามารถเปลี่ยนได้ตามที่เขาพอใจ ฉันกำลังเข้าใกล้มันจากมุมมองของผู้เขียนคลาสพื้นฐาน -> การเข้าใกล้ในฐานะผู้บริโภคผลลัพธ์สุดท้ายของการใช้คลาส ผู้เขียนคลาสพื้นฐานไม่ต้องการให้ผู้บริโภคของคลาสคอนกรีตในอนาคตสามารถเปลี่ยนแปลงคุณสมบัตินั้นได้โดยไม่ต้องมีผู้เขียนคลาสคอนกรีตเปิดเผยวิธีพิเศษในการจัดการแบบเป็นกรณี ๆ ไป เนื่องจากในคลาสฐานนั้นคุณสมบัตินั้นจะถือว่าไม่เปลี่ยนรูปและนั่นจะถูกสื่อสารโดยการเปิดเผยเท่านั้น
Suamere

-1

มันเป็นไปไม่ได้ คุณเพียงแค่ต้องใช้คำหลัก "ใหม่" ในพื้นที่ของคุณ ตัวอย่างเช่น,

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

ดูเหมือนว่าวิธีการนี้สอดคล้องกับทั้งสองด้านของการสนทนานี้ การใช้ "ใหม่" แบ่งสัญญาระหว่างการใช้งานคลาสฐานและการใช้งานคลาสย่อย สิ่งนี้จำเป็นเมื่อ Class สามารถมีสัญญาได้หลายสัญญา (ผ่านทางอินเตอร์เฟสหรือคลาสพื้นฐาน)

หวังว่านี่จะช่วยได้


29
คำตอบนี้ผิดเพราะคุณสมบัติที่ทำเครื่องหมายไว้จะnewไม่แทนที่ฐานเสมือนอีกต่อไป โดยเฉพาะอย่างยิ่งถ้าคุณสมบัติพื้นฐานเป็นนามธรรมเนื่องจากอยู่ในโพสต์ดั้งเดิมเป็นไปไม่ได้ที่จะใช้newคำหลักในคลาสย่อยที่ไม่ใช่นามธรรมเนื่องจากคุณต้องแทนที่สมาชิกนามธรรมทั้งหมด
Timwi

สิ่งนี้เป็นปรากฏการณ์ที่อันตรายเพราะการได้ผลลัพธ์ต่อไปนี้กลับมาแตกต่างกันแม้ว่าจะเป็นวัตถุเดียวกัน: Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)ให้ 5, Console.WriteLine(b.BaseProperty)ให้ 0 กับคุณและเมื่อผู้สืบทอดของคุณต้องแก้ไขข้อบกพร่องนี้
Mark Sowul

ฉันเห็นด้วยกับมาร์คนี่เป็นสิ่งที่น่ารังเกียจอย่างยิ่ง หากการประยุกต์ใช้ทั้งสอง BaseProperty ได้รับการสนับสนุนโดยตัวแปรเดียวกันมันจะดีแม้ว่า IMHO IE: ทำให้ _baseProperty ได้รับการป้องกันและยกเลิก _testBaseProperty นอกจากนี้ AFAIK การใช้งานในฐานไม่จำเป็นต้องเป็นเสมือนจริงเพราะคุณไม่ได้เอาชนะมัน
Steazy

คำตอบสำหรับคำถามส่วนใหญ่สามารถมีความคิดเห็นและอินพุตที่ถูกต้อง แต่นอกเหนือจากความคิดเห็นก่อนหน้าการวิจารณ์หลักของฉันในคำตอบนี้คือฉันเชื่อว่าคำถามจริงอาจเกี่ยวกับคุณสมบัตินามธรรมใน codebase ที่ผู้บริโภคไม่สามารถควบคุมได้ ดังนั้นการวิจารณ์ของฉันคือคำตอบนี้อาจไม่เหมาะกับคำถาม
Suamere

-1

คุณสมบัติแบบอ่านอย่างเดียวในคลาสฐานระบุว่าคุณสมบัตินี้แสดงค่าที่สามารถกำหนดได้จากภายในคลาสเสมอ (ตัวอย่างเช่นค่า enum ที่ตรงกับบริบท (db-) ของวัตถุ) ดังนั้นความรับผิดชอบในการพิจารณาค่ายังคงอยู่ภายในคลาส

การเพิ่มตัวเซ็ตเตอร์จะทำให้เกิดปัญหาที่น่าอึดอัดใจที่นี่: ข้อผิดพลาดในการตรวจสอบควรเกิดขึ้นหากคุณตั้งค่าเป็นสิ่งอื่นนอกเหนือจากค่าที่เป็นไปได้เดียวที่มีอยู่แล้ว

กฎมักจะมีข้อยกเว้น มันเป็นไปได้อย่างมากที่ตัวอย่างเช่นในคลาสที่ได้รับบริบทจะ จำกัด ค่า enum ที่เป็นไปได้ให้เหลือ 3 จาก 10 แต่ผู้ใช้ของวัตถุนี้ยังคงต้องตัดสินใจว่าอันไหนถูกต้อง คลาสที่ได้รับนั้นต้องการมอบหมายความรับผิดชอบในการกำหนดค่าให้กับผู้ใช้ของวัตถุนี้ สิ่งสำคัญที่ต้องตระหนักคือผู้ใช้ของวัตถุนี้ควรตระหนักถึงข้อยกเว้นนี้อย่างดีและรับผิดชอบในการตั้งค่าที่ถูกต้อง

โซลูชันของฉันในสถานการณ์แบบนี้คือปล่อยให้คุณสมบัติอ่านอย่างเดียวและเพิ่มคุณสมบัติอ่านเขียนใหม่ให้กับคลาสที่ได้รับเพื่อรองรับข้อยกเว้น การแทนที่ของคุณสมบัติดั้งเดิมจะส่งคืนค่าของคุณสมบัติใหม่ คุณสมบัติใหม่สามารถมีชื่อที่ถูกต้องซึ่งระบุบริบทของข้อยกเว้นนี้ได้อย่างเหมาะสม

สิ่งนี้ยังสนับสนุนคำพูดที่ถูกต้อง: "ทำให้ยากที่สุดเท่าที่จะเป็นไปได้สำหรับความเข้าใจผิดในการปลูกพืช" โดย Gishu


1
ไม่มีความอึดอัดจะส่อให้เห็นโดยReadableMatrixชั้น (มีสองอาร์กิวเมนต์อ่านอย่างเดียวสถานที่ให้บริการการจัดทำดัชนี) มีชนิดย่อยและImmutableMatrix MutableMatrixรหัสที่ต้องการอ่านเมทริกซ์เท่านั้นและไม่สนใจว่ามันอาจจะเปลี่ยนแปลงในอนาคตอาจยอมรับพารามิเตอร์ประเภทReadableMatrixและมีความสุขอย่างสมบูรณ์กับชนิดย่อยที่ไม่แน่นอนหรือไม่เปลี่ยนรูป
supercat

setter เพิ่มเติมสามารถปรับเปลี่ยนตัวแปรส่วนตัวภายในชั้นเรียน คุณสมบัติที่เรียกว่า "อ่านอย่างเดียว" จากคลาสฐานจะยังคงพิจารณาค่า "จากภายในคลาส" (โดยการอ่านตัวแปรส่วนตัวใหม่) ฉันไม่เห็นปัญหาที่นั่น ตัวอย่างเช่นลองดูสิ่งที่List<T>.Countทำ: มันไม่สามารถกำหนดได้ แต่นั่นไม่ได้หมายความว่ามันเป็น "แบบอ่านอย่างเดียว" ซึ่งผู้ใช้ไม่สามารถแก้ไขได้ มันสามารถปรับเปลี่ยนได้เป็นอย่างดีแม้ว่าจะเป็นทางอ้อมโดยการเพิ่มและลบรายการ
หรือผู้ทำแผนที่

-2

เนื่องจากที่ระดับ IL คุณสมบัติการอ่าน / เขียนจะแปลเป็นสองวิธี (ทะเยอทะยานและตัวตั้งค่า)

เมื่อเอาชนะคุณจะต้องสนับสนุนอินเทอร์เฟซพื้นฐาน หากคุณสามารถเพิ่มตัวตั้งค่าคุณจะต้องเพิ่มวิธีการใหม่อย่างมีประสิทธิภาพซึ่งจะยังคงมองไม่เห็นกับโลกภายนอกตราบใดที่อินเทอร์เฟซของคลาสของคุณเกี่ยวข้อง

จริงการเพิ่มวิธีการใหม่จะไม่ทำลายความเข้ากันได้ต่อกัน แต่เนื่องจากมันจะยังคงซ่อนอยู่การตัดสินใจไม่อนุญาตสิ่งนี้จึงสมเหตุสมผล


2
ฉันไม่สนใจอะไรมากเกี่ยวกับ "โลกภายนอก" ที่นี่ ฉันต้องการเพิ่มฟังก์ชันการทำงานให้กับชั้นเรียนซึ่งสามารถมองเห็นได้เฉพาะโค้ดที่รู้คลาสเฉพาะและไม่ใช่ฐาน
ripper234

คำตอบ @ ripper234 ดูแมตต์กลอน: ถ้าคุณต้องการที่จะทำให้คุณสมบัติใหม่ที่มองเห็นได้เฉพาะกับผู้ใช้ของชั้นเรียนมาที่คุณควรใช้แทนnew override
Dan Berindei

1
มันไม่ได้ถูกซ่อนอยู่ คุณชอบที่จะบอกว่าคุณไม่สามารถเพิ่มวิธีการใหม่ให้กับคลาสเด็กได้เนื่องจากวิธีการใหม่จะ "ซ่อนอยู่จากโลกภายนอก" แน่นอนว่ามันยังคงซ่อนอยู่จากมุมมองระดับฐาน แต่สามารถเข้าถึงได้จากลูกค้าของชั้นเรียนเด็ก (กล่าวคือจะส่งผลกระทบต่อสิ่งที่ผู้ทะเยอทะยานควรกลับคืนมา)
Sheepy

Sheepy นั่นไม่ใช่สิ่งที่ฉันพูดทั้งหมด ฉันหมายความว่าอย่างที่คุณชี้ให้เห็นมันจะยังคงซ่อนอยู่ "จากมุมมองระดับฐาน" นั่นเป็นเหตุผลที่ฉันผนวก "เท่าที่อินเตอร์เฟซชั้นเรียนของคุณเป็นห่วง" ถ้าคุณกำลังคลาสคลาสย่อยคลาสเกือบจะแน่นอนว่า "โลกภายนอก" จะอ้างอิงถึงอินสแตนซ์ของวัตถุของคุณด้วยประเภทคลาสพื้นฐาน หากพวกเขาจะใช้คลาสที่เป็นรูปธรรมของคุณโดยตรงคุณจะไม่จำเป็นต้องเข้ากันได้กับคลาสพื้นฐานและคุณสามารถใช้newร่วมกับส่วนเพิ่มเติมของคุณได้
Ishmaeel

-2

เนื่องจากคลาสที่มีคุณสมบัติแบบอ่านอย่างเดียว (ไม่มีตัวตั้งค่า) อาจมีเหตุผลที่ดีสำหรับมัน อาจไม่มีที่เก็บข้อมูลพื้นฐานตัวอย่างเช่น ช่วยให้คุณสร้าง setter แบ่งสัญญาที่กำหนดโดยชั้นเรียน มันเป็นแค่ OOP ที่ไม่ดี


Up-โหวต นี่มันรวบรัดมาก เหตุใด Microsoft ควรอนุญาตให้ผู้บริโภคระดับฐานหยุดสัญญา ฉันเห็นด้วยกับคำตอบสั้น ๆ ที่สับสนและสับสนของฉัน ฮ่า ๆ. หากคุณเป็นผู้บริโภคระดับพื้นฐานคุณไม่ควรต้องการทำลายสัญญา แต่คุณสามารถใช้งานได้หลายวิธี
Suamere
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.