2010-04-09 32 views
7

Được rồi, như bạn có thể biết, kế thừa tĩnh là không thể trong C#. Tôi hiểu rằng, tuy nhiên tôi bị mắc kẹt với sự phát triển của chương trình của tôi.C# Thiếu thừa kế tĩnh - Tôi nên làm gì?

Tôi sẽ cố gắng làm cho nó đơn giản nhất có thể. Giả sử mã của chúng ta cần quản lý các đối tượng đang trình bày máy bay ở một số sân bay. Các yêu cầu như sau:

  • Có thành viên và phương pháp được chia sẻ cho tất cả các máy bay

  • Có rất nhiều loại máy bay, mỗi loại có thể có phương pháp thêm riêng của mình và các thành viên. Có thể có nhiều trường hợp cho mỗi loại máy bay.

  • Mỗi loại máy bay phải có tên thân thiện với loại này và thông tin chi tiết hơn về loại này. Ví dụ một lớp có tên F16 sẽ có một thành viên tĩnh FriendlyName với giá trị của "Lockheed Martin F-16 Fighting Falcon".

  • Các lập trình viên khác sẽ có thể thêm nhiều máy bay hơn, mặc dù chúng phải được thực thi để tạo ra các chi tiết tĩnh tương tự về các loại máy bay. Trong một số GUI, cần có một cách để cho phép người dùng thấy danh sách các loại có sẵn (với các chi tiết như FriendlyName) và thêm hoặc loại bỏ các cá thể của máy bay, được lưu, cho phép nói, với một số tệp XML .

Vì vậy, về cơ bản, nếu tôi có thể thực thi các lớp thừa kế để thực hiện các thành viên và phương pháp tĩnh, tôi sẽ thực thi các loại máy bay để có thành viên tĩnh như FriendlyName. Đáng buồn là tôi không thể làm điều đó.

Vì vậy, thiết kế nào tốt nhất cho kịch bản này?

+0

Lưu ý: Vì mọi loại có thể có các thành viên khác nhau, GUI có thể trông khác nhau đối với từng loại. Đó là lý do tại sao mọi loại máy bay nên có một phương pháp tĩnh (hoặc cái gì khác) mà trả về một tập hợp các phần tử (hộp văn bản, hộp kiểm, vv - nvm cách nó được đóng gói). Một lần nữa - Tôi không muốn tạo các phiên bản trống cho tất cả các loại mỗi lần tôi muốn đọc chi tiết của chúng hoặc nhận các phần tử GUI. – yellowblood

Trả lời

8

Một câu trả lời là để trang trí mỗi lớp với các thuộc tính (metadata):

[Description("Lockheed Martin F-16 Fighting Falcon")] 
public class F16 : Aircraft 
{ 
    // ... 
} 

này được sử dụng DescriptionAttribute đã có trong System.ComponentModel.

Bạn có thể lấy siêu dữ liệu như thế này:

Type t = typeof(F16); 
DescriptionAttribute attr = (DescriptionAttribute)Attribute.GetCustomAttribute(t, 
    typeof(DescriptionAttribute)); 
string description = (attr != null) ? attr.Description : t.Name; 

Điều này sẽ giúp bạn có được văn bản mô tả từ một tham chiếu đến lớp F16.

+0

Lưu ý: Nếu bạn thấy mình thường xuyên làm điều này, bạn có thể tạo một phương thức mở rộng cho phép bạn viết 'typeof (F16) .GetDescription()'. – Aaronaught

+0

Cảm ơn, điều này trông giống như một cách tốt, nhưng bạn không thể thực sự thực thi nó, tôi có phải không? Ý tôi là, một nhà phát triển có thể tạo ra một loại máy bay không có thuộc tính này - và tôi thực sự muốn tìm cách thực thi nó. – yellowblood

+0

@yellowblood: Có, nhà phát triển có thể tạo loại không có thuộc tính. Nếu về lý thuyết có thể có các phương thức tĩnh có thể overridable, một nhà phát triển cũng có thể chọn trả về một chuỗi rỗng hoặc 'null' từ nó hoặc thậm chí ném một ngoại lệ. Dù bằng cách nào, bạn muốn mã phòng thủ và sử dụng mặc định tốt - trong ví dụ của tôi tôi đã mặc định sử dụng tên lớp nếu mô tả không có sẵn. Tôi hiểu rằng có một sự khác biệt ngữ nghĩa, nhưng tiếc là bạn không thể ép buộc các thuộc tính được định nghĩa. Những gì bạn * có thể làm là tạo một quy tắc FxCop để xác định các quy tắc còn thiếu. – Aaronaught

3

Không sử dụng các phương pháp tĩnh. sử dụng các phương thức thể hiện thay thế.

Ngoài ra, tóm tắt hàng đầu có thể đưa ra một phương pháp trừu tượng sẽ trả về tên cụ thể của máy bay.

public abstract class Aircraft 
{ 
    public abstract string Name { get; } 
    public abstract string FriendlyName { get; } 
} 
+1

Nói chung, có, nhưng ông giải thích rõ ràng trường hợp sử dụng trong câu hỏi của mình - anh ta cần có khả năng cung cấp danh sách các loại có sẵn mà không thực sự tạo chúng trước tiên. Rất phổ biến trong các kiến ​​trúc giống như plugin. Việc tạo một cá thể có thể tốn kém và nếu có 100 loại khác nhau, sẽ không có ý nghĩa gì khi tạo ra tất cả chỉ để có được mô tả cho từng loại. – Aaronaught

+0

@Aaronaught - để liệt kê anh ta có thể sử dụng các thuộc tính như bạn đã gợi ý, và để khởi tạo - hoặc tạo mã hoặc có hashtable trong đó key = Type và Value = Delegate đến constructor. Chạy đại biểu là rẻ hơn sau đó Reflection. – Vitaly

+0

Không thể giải thích rõ hơn, nhờ Aaronaught (và cho Mendy và David cho câu trả lời của họ) – yellowblood

4

Tại sao bạn cần các thuộc tính này là tĩnh?

public class Aircraft 
{ 
protected string AircraftName { get; protected set; } 
} 

public class F16 : Aircraft 
{ 
    public F16() 
    { 
    AircraftName="F16 Falcon"; 
    } 
} 
1

Sử dụng điều tra cho tên thân thiện và tạo thành viên thể hiện loại đó cho tên thân thiện. Yêu cầu khởi tạo thành viên này trong quá trình xây dựng.

2

Đây là trường hợp bạn có thể hưởng lợi từ mẫu Nhà máy.Thay vì nhập khẩu các loại Máy bay cụ thể, hãy cung cấp một giao diện IAircraftFactory tiêu chuẩn xác định những gì mọi Nhà máy Máy bay cần làm cho bạn. Đây là nơi bạn có thể trả về các mô tả, thông tin giao diện người dùng, vv Nhà máy máy bay sau đó chịu trách nhiệm tạo ra một Máy bay cụ thể. Bởi vì khách hàng của bạn phải tạo một Nhà máy tùy chỉnh để phơi bày Máy bay của họ, họ buộc phải thực hiện giao diện và nhắc nhở (thông qua các thành viên của mình) rằng họ có hợp đồng để thực hiện.

Cái gì như:

public interface IAircraft 
{ 
    //Aircraft instance details... 
} 

public interface IAircraftFactory 
{ 
    //Can include parameters if needed... 
    IAircraft BuildAircraft(); 

    //And other useful meta-data... 
    string GetDescription(); 
} 

//In some other Client-provided DLL... 
public class MyAircraftFactory : IAircraftFactory 
{ 
    IAircraft BuildAircraft() 
    { 
     return new MyAircraft(); 
    } 

    //... 
} 
+0

Cảm ơn bạn đã trả lời. Mặc dù Pattern Factory sẽ giúp tôi tách siêu dữ liệu khỏi các chi tiết cá thể, nhưng tôi vẫn phải tạo một cá thể (của MyAircraftFactory) để đọc siêu dữ liệu. Giả sử tôi đã sử dụng sự phản chiếu để đọc DLL, tôi có nên tránh nó không (tạo một cá thể cho mỗi kiểu)? – yellowblood

+0

Ý tưởng ở đây là bạn chỉ bao giờ tạo một phiên bản Nhà máy cho từng loại máy bay. Bạn chỉ phải sử dụng Reflection để tìm nhà máy. Ngẫu nhiên, điều này phù hợp rất tự nhiên với các hệ thống mở rộng như MEF, nơi bạn có thể tìm thấy tất cả các triển khai của một giao diện cụ thể và khởi tạo chúng một cách tự động. –

+0

Một ưu điểm khác của phương pháp này là khách hàng của bạn có quyền kiểm soát nhiều hơn quá trình xây dựng của Máy bay, cho họ sự linh hoạt để làm những việc đằng sau hậu trường, như hệ thống con có thể sử dụng nhiều máy bay mà không phải dựa vào trạng thái tĩnh. Bất kỳ trạng thái chia sẻ nào cũng có thể được đóng gói độc đáo trong nhà máy hoặc các thành viên của nó. –

0

@Aaronaught nhấn đinh trên đầu với các plugin giống như kiến ​​trúc nhận xét.

Điều tôi đã làm lần cuối cùng tôi gặp phải, là có loại "Mô tả" không đắt tiền để tạo và giữ dữ liệu meta trong trường mẫu.

public class F16Descriptor : AircraftDescriptor 
{ 
    public override string Name { get { return "Lockheed Martin F-16 Fighting Falcon"; } } 
    public override Type AircraftType { get { return typeof(F16); } } 
} 

public class F16 : AircraftBase 
{ 
    ... 
} 
0

Một cách thú vị để giải quyết vấn đề này là để nhận ra rằng các loại máy bay cũng là một quan trọng khái niệm trong việc thiết kế và tạo ra chúng như riêng biệt lớp, mà trường đóng vai trò như loại của máy bay. Điều này được gọi là type object pattern (pdf), và nó cho phép thiết kế rất linh hoạt.