คุณสมบัตินามธรรมในคลาสฐานเพื่อบังคับให้โปรแกรมเมอร์กำหนดมัน


10

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

ในระดับรัฐฉันมีวิธีนามธรรมหลายอย่าง ถ้าฉันไม่ใช้วิธีนามธรรมในคลาส discrete (คอนกรีต), Visual Studio จะให้ข้อผิดพลาดดังนี้:

... ข้อผิดพลาด 1 'myConcreteState' ไม่ได้ใช้สมาชิกนามธรรมที่สืบทอด 'myAbstractState'

ตอนนี้: ฉันกำลังพยายามสร้างคุณสมบัติสตริงสำหรับแต่ละรัฐชื่อ StateName เมื่อใดก็ตามที่ฉันสร้างคลาสคอนกรีตใหม่ฉันต้องกำหนด StateName ฉันต้องการให้ VS ส่งข้อผิดพลาดหากฉันไม่ได้ใช้ มีวิธีง่าย ๆ ในการทำเช่นนี้?

ฉันได้ลองในชั้นนามธรรม / ฐาน:

public abstract string StateName { get; set; }

แต่ฉันไม่จำเป็นต้องใช้วิธีการรับและตั้งค่าในแต่ละรัฐ

คำถามที่มีการแก้ไข: ในสถานการณ์ที่เหมาะสมอุดมคติแต่ละระดับของรัฐจะต้องกำหนดให้ StateName กำหนดและสืบทอดมาจากคลาสฐานนามธรรม

StateName = "MyState1"; //or whatever the state's name is

หากคำสั่งนั้นหายไป Visual Studio จะสร้างข้อผิดพลาดตามที่อธิบายไว้ข้างต้น เป็นไปได้และถ้าเป็นเช่นนั้นได้อย่างไร


2
ฉันไม่เข้าใจคำถาม
Ben Aaronson

ฉันเดาวิธีที่ "ถูกต้อง" ในการทำเช่นนี้คือการมี Constructor ที่ได้รับการปกป้องในคลาสพื้นฐานซึ่งต้องการชื่อสถานะเป็นพารามิเตอร์
Roman Reiner

@ RomanReiner ฉันคิดเกี่ยวกับการทำเช่นนั้น .. แต่ดูเหมือนซ้ำซ้อนเพราะทุกครั้งที่เปลี่ยน / โทรหารัฐฉันจะต้องพิมพ์ชื่อ
GisMofx

@BenAaronson ฉันได้ชี้แจงคำถามของฉันที่ด้านล่าง
GisMofx

ค่าคงที่ชื่อรัฐต่อชนิดหรือค่าคงที่ต่ออินสแตนซ์หรือไม่
Roman Reiner

คำตอบ:


16

ฉันเดาวิธีที่ "ถูกต้อง" ในการทำเช่นนี้คือการมี Constructor ที่ได้รับการปกป้องในคลาสพื้นฐานซึ่งต้องการชื่อสถานะเป็นพารามิเตอร์

public abstract class State
{
    private readonly string _name;

    protected State(string name)
    {
        if(String.IsNullOrEmpty(name))
            throw new ArgumentException("Must not be empty", "name");

        _name = name;
    }

    public string Name { get { return _name; } }
}

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

public abstract class SomeState : State
{
    public SomeState() : base("The name of this state")
    {
        // ...
    }
}

เนื่องจากคลาสพื้นฐานไม่ได้เปิดเผยตัวสร้างอื่น ๆ (ไม่ได้รับการป้องกันและไม่เปิดเผย) แต่ละคลาสที่สืบทอดจะต้องผ่านตัวสร้างเดียวนี้จึงจำเป็นต้องกำหนดชื่อ

โปรดทราบว่าคุณไม่จำเป็นต้องระบุชื่อเมื่อคุณสร้างอินสแตนซ์สถานะที่เป็นรูปธรรมเนื่องจากคอนสตรัคเตอร์ดูแลสิ่งนั้น:

var someState = new SomeState(); // No need to define the name here
var name = someState.Name; // returns "The name of this state"

1
ขอบคุณ! ในความเป็นจริงตัวสร้างคลาสฐานของฉันใช้อาร์กิวเมนต์เพิ่มเติม ดังนั้นฉันมีสองอินพุต ทันทีที่ฉันตั้งค่านี้ฉันได้รับข้อผิดพลาดที่ฉันต้องการอาร์กิวเมนต์เพิ่มเติมในตัวสร้างคลาสของรัฐ...:base(arg1, arg2)! นี่คือทางออกที่ฉันกำลังมองหา สิ่งนี้จะช่วยให้การเข้ารหัสของฉันสอดคล้องกันมากขึ้น
GisMofx

3

ในฐานะของ C # 6 (ฉันเชื่อว่า - C #: ใหม่และปรับปรุง C # 6.0 ) คุณจะสามารถสร้างคุณสมบัติ getter เท่านั้น ดังนั้นคุณสามารถประกาศคลาสฐานของคุณเช่น -

public abstract class State
{
    public abstract string name { get; }

    // Your other functions....
}

และจากนั้นในชั้นย่อยของคุณคุณสามารถใช้Stateเช่น -

public class SomeState : State
{
    public override string name { get { return "State_1"; } }
}

หรือแม้กระทั่งผู้ใช้แลมบ์ดา -

public class SomeState : State
{
    public override string name => "State_1";
}

ทั้งสองจะกลับมา "State_1" และไม่เปลี่ยนรูปเสมอ


1
สามารถเขียนได้public override string name { get; } = "State_1";ซึ่งไม่จำเป็นต้องประเมินค่าใหม่ทุกครั้ง
Dave Cousineau

2

ความต้องการของคุณขัดแย้งกัน:

ฉันกำลังพยายามสร้างคุณสมบัติสตริงสำหรับแต่ละรัฐชื่อ StateName

เมื่อเทียบกับ

แต่ฉันไม่จำเป็นต้องดำเนินการทั้งหมดในแต่ละรัฐ

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

คุณต้องเปลี่ยนความต้องการของคุณหรือใช้สิ่งอื่นที่ไม่ใช่มรดกง่าย

วิธีการแก้ปัญหาที่เป็นไปได้ด้วยรหัสตามที่อาจเป็นไปได้ที่จะทำให้วิธีการในStateนามธรรมที่ไม่ใช่และกลับสตริงที่ว่างเปล่า


ฉันคิดว่าคุณเอาคำแถลงที่สองนั้นออกไปจากบริบท แต่ฉันจะชี้แจง ฉันไม่ต้องการวิธีการตั้งค่าฉันเพียงต้องการStateName = "MyState1"ในแต่ละรัฐ หากฉันไม่ได้มีคำสั่งนั้นในระดับรัฐแล้วนึกคิด Visual Studio จะสร้างข้อผิดพลาด
GisMofx

3
@GisMofx หากคุณต้องการบังคับชื่อรัฐในแต่ละคลาสย่อยให้รับบทคัดย่อ แต่ไม่ต้องการสถานะ จากนั้นแต่ละคลาสย่อยจะต้องจัดให้มีreturn "MyState1"การใช้งานและสามารถเพิ่มที่เก็บข้อมูลที่ไม่แน่นอนสำหรับค่าหากพวกเขาต้องการ แต่คุณต้องชี้แจงข้อกำหนดในคำถาม - รัฐทุกประเภทต้องการ StateName ซึ่งสามารถอ่านได้หรือไม่และรัฐประเภทใดต้องการให้กลายพันธุ์หลังจากการสร้างหรือไม่?
Pete Kirkham

@PeteKirkham แต่ละรัฐต้องการชื่อมันไม่ควรกลายพันธุ์หลังจากการสร้าง ชื่อของรัฐเป็นแบบคงที่ / ไม่เปลี่ยนรูป
GisMofx
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.