Tài sản trừu tượng trong lớp cơ sở để buộc lập trình viên xác định nó


10

Tôi đang mã hóa với một mẫu trạng thái cho một thiết bị nhúng. Tôi có một lớp cơ sở / trừu tượng được gọi là Trạng thái và sau đó mỗi lớp trạng thái riêng biệt (cụ thể) thực hiện Lớp trạng thái trừu tượng.

Trong lớp Nhà nước tôi có một số Phương thức Trừu tượng. Nếu tôi không triển khai các phương thức trừu tượng trong lớp rời rạc (cụ thể), Visual Studio sẽ đưa ra một lỗi như thế này:

... Lỗi 1 'myConcittleState' không triển khai thành viên trừu tượng được kế thừa 'myAbTHERState'

Bây giờ: Tôi đang cố gắng tạo một thuộc tính Chuỗi cho mỗi Bang được gọi là StateName. Bất cứ khi nào tôi tạo một lớp cụ thể mới, tôi cần xác định StateName. Tôi muốn VS đưa ra lỗi nếu tôi không sử dụng nó. Có một cách đơn giản để làm điều này?

Tôi đã thử điều này trong lớp trừu tượng / cơ sở:

public abstract string StateName { get; set; }

Nhưng tôi không cần phải thực hiện các phương thức Nhận và Đặt ở mỗi Bang.

Câu hỏi sửa đổi: Trong một tình huống lý tưởng, mỗi Lớp trạng thái sẽ được yêu cầu phải có StateName định nghĩa và được kế thừa từ lớp cơ sở trừu tượng.

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

Nếu câu lệnh đó bị thiếu thì Visual Studio sẽ phát sinh lỗi như mô tả ở trên. Điều này có thể và nếu vậy, làm thế nào?


2
Tôi không hiểu câu hỏi.
Ben Aaronson

Tôi đoán cách "chính xác" để làm điều này là có một hàm tạo được bảo vệ trên lớp cơ sở yêu cầu tên trạng thái làm tham số.
Roman Reiner

@RomanReiner Tôi cũng nghĩ về việc đó..nhưng nó có vẻ dư thừa vì mỗi lần thay đổi / gọi một trạng thái, tôi sẽ phải gõ tên.
GisMofx

@BenAaronson Tôi đã làm rõ câu hỏi của tôi ở gần cuối.
GisMofx

Là tên trạng thái không đổi trên mỗi loại hoặc hằng số cho mỗi trường hợp?
Roman Reiner

Câu trả lời:


16

Tôi đoán cách "chính xác" để làm điều này là có một hàm tạo được bảo vệ trên lớp cơ sở yêu cầu tên trạng thái làm tham số.

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

Các trạng thái cụ thể sau đó cung cấp một hàm tạo công khai, ngầm gọi hàm tạo của lớp cơ sở với tên thích hợp.

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

Vì lớp cơ sở không để lộ bất kỳ hàm tạo nào khác (không được bảo vệ cũng không công khai), mỗi lớp kế thừa cần phải đi qua hàm tạo duy nhất này và do đó cần xác định tên.

Lưu ý rằng bạn không cần cung cấp tên khi bạn khởi tạo trạng thái cụ thể vì hàm tạo của nó xử lý vấn đề đó:

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

1
Cảm ơn! Trong thực tế, trình xây dựng lớp cơ sở của tôi bây giờ có một đối số bổ sung; vì vậy tôi có hai đầu vào. Ngay khi tôi thiết lập điều này, tôi nhận được các lỗi mà tôi cần một đối số bổ sung trong các hàm tạo của lớp nhà nước ...:base(arg1, arg2)! Đây là một giải pháp mà tôi đang tìm kiếm. Điều này thực sự giúp giữ cho mã hóa Nhà nước của tôi phù hợp hơn.
GisMofx

3

Kể từ C # 6 (tôi tin - C #: C # 6.0 mới và được cải tiến ), bạn có thể tạo các thuộc tính chỉ getter. Vì vậy, bạn có thể khai báo lớp cơ sở của bạn như vậy -

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

    // Your other functions....
}

Và sau đó trong lớp học của bạn, bạn có thể thực hiện Statenhư vậy -

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

Hoặc thậm chí gọn gàng hơn bằng cách sử dụng toán tử lambda -

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

Cả hai sẽ luôn trả về "Trạng thái_1" và không thay đổi.


1
điều này có thể được viết public override string name { get; } = "State_1";mà sau đó không cần phải đánh giá lại giá trị mỗi lần.
Dave Cousineau

2

Yêu cầu của bạn là một mâu thuẫn:

Tôi đang cố gắng tạo một thuộc tính Chuỗi cho mỗi Bang được gọi là StateName.

so với

Nhưng tôi không cần phải thực hiện tất cả những điều đó ở mỗi Bang.

Không có tính năng ngôn ngữ nào cho phép bạn ép buộc sự tồn tại của một thành viên chỉ trong một vài lớp phụ. Rốt cuộc, quan điểm của việc sử dụng một siêu lớp là dựa vào thực tế là tất cả các lớp con sẽ có tất cả các thành viên của siêu hạng (và có thể nhiều hơn nữa). Nếu bạn muốn tạo các lớp hoạt động như một Statenhưng không có tên, thì theo định nghĩa, chúng không nên (/ có thể) không phải là các lớp con của State.

Bạn có thể phải thay đổi yêu cầu của mình hoặc sử dụng một cái gì đó ngoài kế thừa đơn giản.

Một giải pháp khả thi với mã vì nó có thể là làm cho phương thức Statekhông trừu tượng và trả về một chuỗi rỗng.


Tôi nghĩ rằng bạn đã đưa tuyên bố thứ hai ra khỏi bối cảnh, nhưng tôi sẽ làm rõ. Tôi không cần các phương thức Nhận / Đặt, tôi chỉ đơn giản muốn StateName = "MyState1"ở mỗi Bang. Nếu tôi không có câu lệnh đó trong lớp trạng thái, thì lý tưởng là Visual Studio sẽ phát sinh lỗi.
GisMofx

3
@GisMofx Nếu bạn muốn buộc tên trạng thái trong mỗi lớp con, hãy tạo bản tóm tắt nhưng không yêu cầu trạng thái. Sau đó, mỗi lớp con sẽ phải cung cấp return "MyState1"dưới dạng triển khai của chúng và có thể thêm bộ nhớ có thể thay đổi cho giá trị nếu chúng cần. Nhưng bạn cần làm rõ các yêu cầu trong câu hỏi - có phải mọi loại trạng thái đều yêu cầu StateName có thể đọc được và có loại trạng thái nào yêu cầu nó bị đột biến sau khi tạo không?
Pete Kirkham

@PeteKirkham mỗi tiểu bang yêu cầu một tên, nó không nên bị thay đổi sau khi tạo. Tên của các trạng thái là tĩnh / bất biến.
GisMofx
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.