Có được phép sử dụng triển khai giao diện rõ ràng để ẩn các thành viên trong C # không?


14

Tôi hiểu cách làm việc với các giao diện và triển khai giao diện rõ ràng trong C #, nhưng tôi tự hỏi liệu nó có bị coi là hình thức xấu để che giấu một số thành viên không được sử dụng thường xuyên không. Ví dụ:

public interface IMyInterface
{
    int SomeValue { get; set; }
    int AnotherValue { get; set; }
    bool SomeFlag { get; set; }

    event EventHandler SomeValueChanged;
    event EventHandler AnotherValueChanged;
    event EventHandler SomeFlagChanged;
}

Tôi biết trước rằng các sự kiện, trong khi hữu ích, sẽ rất hiếm khi được sử dụng. Vì vậy, tôi nghĩ sẽ tốt hơn nếu giấu chúng khỏi một lớp triển khai thông qua triển khai rõ ràng để tránh sự lộn xộn của IntelliSense.


3
Nếu bạn đang tham chiếu ví dụ của mình qua giao diện thì dù sao nó cũng sẽ không ẩn chúng, nó sẽ chỉ ẩn nếu tham chiếu của bạn thuộc loại cụ thể.
Andy

1
Tôi đã làm việc với một người thường xuyên làm việc này. Nó lái tôi hoàn toàn hạt dẻ.
Joel Etherton

... và bắt đầu sử dụng tổng hợp.
mikalai

Câu trả lời:


39

Vâng, nó thường là hình thức xấu.

Vì vậy, tôi nghĩ sẽ tốt hơn nếu giấu chúng khỏi một lớp triển khai thông qua triển khai rõ ràng để tránh sự lộn xộn của IntelliSense.

Nếu IntelliSense của bạn quá bừa bộn, lớp của bạn quá lớn. Nếu lớp học của bạn quá lớn, có thể bạn sẽ làm quá nhiều thứ và / hoặc được thiết kế kém.

Khắc phục sự cố của bạn, không phải là triệu chứng của bạn.


Có thích hợp để sử dụng nó để loại bỏ các cảnh báo trình biên dịch về các thành viên không sử dụng?
Gusdor

11
"Khắc phục sự cố của bạn, không phải là triệu chứng của bạn." +1
FreeAsInBeer

11

Sử dụng tính năng cho những gì nó được dự định. Giảm sự lộn xộn không gian tên không phải là một trong số đó.

Lý do hợp pháp không phải là về "che giấu" hoặc tổ chức, họ là để giải quyết sự mơ hồ và chuyên môn hóa. Nếu bạn triển khai hai giao diện xác định "Foo", ngữ nghĩa cho Foo có thể khác nhau. Thực sự không phải là một cách tốt hơn để giải quyết điều này ngoại trừ các giao diện rõ ràng. Cách khác là không cho phép thực hiện các giao diện chồng chéo hoặc tuyên bố rằng các giao diện không thể liên kết ngữ nghĩa (dù sao đây cũng chỉ là một khái niệm). Cả hai đều là lựa chọn thay thế nghèo nàn. Đôi khi thực tế hơn hẳn sự thanh lịch.

Một số tác giả / chuyên gia coi đó là một tính năng của một tính năng (các phương thức không thực sự là một phần của mô hình đối tượng của loại). Nhưng giống như tất cả các tính năng, ở đâu đó, ai đó cần nó.

Một lần nữa, tôi sẽ không sử dụng nó để ẩn hoặc tổ chức. Có nhiều cách để che giấu các thành viên khỏi intellisense.

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

"Có những tác giả / chuyên gia nổi tiếng coi đó là một tính năng của một tính năng." Ai là? Đề xuất thay thế là gì? Có ít nhất một vấn đề (nghĩa là chuyên môn hóa các phương thức giao diện trong lớp triển khai / lớp thực hiện) sẽ yêu cầu một số tính năng khác được thêm vào C # để giải quyết nó trong trường hợp không triển khai rõ ràng (xem ADO.NET và các bộ sưu tập chung) .
jhominal

@jhominal - CLR thông qua C # của Jeffrey Richter đặc biệt sử dụng từ kydge. C ++ hòa hợp với nhau mà không có nó, lập trình viên phải tham khảo rõ ràng việc thực hiện phương thức cơ sở để định hướng hoặc sử dụng một diễn viên. Tôi đã đề cập rằng các lựa chọn thay thế không hấp dẫn lắm. Nhưng đó là một khía cạnh của kế thừa và giao diện duy nhất, không phải là OOP nói chung.
codenheim

Bạn cũng có thể đã đề cập rằng Java cũng không có triển khai rõ ràng, mặc dù có cùng một thiết kế "kế thừa duy nhất của triển khai + giao diện". Tuy nhiên, trong Java, kiểu trả về của một phương thức overriden có thể là chuyên biệt, làm giảm đi một phần nhu cầu thực hiện rõ ràng. (Trong C #, một quyết định thiết kế khác đã được đưa ra, có lẽ một phần là do bao gồm các thuộc tính).
jhominal

@jhominal - Chắc chắn rồi, khi tôi có vài phút sau tôi sẽ cập nhật. Cảm ơn.
codenheim

Việc ẩn có thể hoàn toàn thích hợp trong các tình huống trong đó một lớp có thể thực hiện một phương thức giao diện theo cách tuân thủ hợp đồng giao diện nhưng không hữu ích . Ví dụ, trong khi tôi không thích ý tưởng về các lớp IDisposable.Disposelàm một cái gì đó nhưng được đặt một tên khác như thế Close, tôi sẽ xem xét nó hoàn toàn hợp lý cho một lớp có hợp đồng từ chối rõ ràng rằng mã có thể tạo và từ bỏ các thể hiện mà không gọi Disposeđể sử dụng triển khai giao diện rõ ràng để định nghĩa một IDisposable.Disposephương thức không làm gì cả
supercat

5

Tôi có thể là một dấu hiệu của mã lộn xộn, nhưng đôi khi thế giới thực là lộn xộn. Một ví dụ từ .NET framework là ReadOnlyColection ẩn bộ setter đột biến [], Add and Set.

IE ReadOnlyCollection thực hiện IList <T>. Cũng xem câu hỏi của tôi với SO vài năm trước khi tôi suy nghĩ về điều này.


Vâng, đó là giao diện của riêng tôi, tôi cũng sẽ sử dụng, vì vậy không có ý nghĩa gì khi làm sai ngay từ đầu.
Kyle Baran

2
Tôi sẽ không nói rằng nó che giấu setter đột biến mà đúng hơn là nó không cung cấp nó. Một ví dụ tốt hơn là ConcurrentDictionary, trong đó thực hiện các thành viên khác nhau IDictionary<T>một cách rõ ràng để người tiêu dùng của ConcurrentDictionaryA) sẽ không gọi họ trực tiếp và B) có thể sử dụng các phương thức yêu cầu triển khai IDictionary<T>nếu thực sự cần thiết. Ví dụ, người dùng ConcurrentDictionary<T> nên gọi TryAddhơn là Addđể tránh cần các ngoại lệ không cần thiết.
Brian

Tất nhiên, thực hiện Addrõ ràng cũng làm giảm sự lộn xộn. TryAddcó thể làm bất cứ điều gì Addcó thể làm ( Addđược thực hiện như một cuộc gọi đến TryAdd, vì vậy việc cung cấp cả hai sẽ là dư thừa). Tuy nhiên, TryAddnhất thiết phải có một tên / chữ ký khác nhau, vì vậy không thể thực hiện IDictionarymà không có sự dư thừa này. Thực hiện rõ ràng giải quyết vấn đề này một cách sạch sẽ.
Brian

Từ quan điểm của người tiêu dùng, các phương pháp được ẩn đi.
Esben Skov Pedersen

1
Bạn viết về IReadonlyCollection <T> nhưng liên kết đến ReadOnlyCollection <T>. Cái đầu tiên trong một giao diện, cái thứ hai là một lớp. IReadonlyCollection <T> không triển khai IList <T> mặc dù ReadonlyCollection <T> không.
Jørgen Fogh

2

Đó là hình thức tốt để làm như vậy khi thành viên là vô nghĩa trong bối cảnh. Ví dụ: nếu bạn tạo một bộ sưu tập chỉ đọc thực hiện IList<T>bằng cách ủy quyền cho một đối tượng nội bộ _wrappedthì bạn có thể có một cái gì đó như:

public T this[int index]
{
  get
  {
     return _wrapped[index];
  }
}
T IList<T>.this[int index]
{
  get
  {
    return this[index];
  }
  set
  {
    throw new NotSupportedException("Collection is read-only.");
  }
}
public int Count
{
  get { return _wrapped.Count; }
}
bool ICollection<T>.IsReadOnly
{
  get
  {
    return true;
  }
}

Ở đây chúng tôi có bốn trường hợp khác nhau.

public T this[int index]được định nghĩa bởi lớp của chúng tôi chứ không phải giao diện, và do đó tất nhiên không phải là một triển khai rõ ràng, mặc dù lưu ý rằng nó xảy ra tương tự như đọc-ghi T this[int index]được xác định trong giao diện nhưng chỉ đọc.

T IList<T>.this[int index]là rõ ràng bởi vì một phần của nó (getter) được kết hợp hoàn hảo bởi thuộc tính ở trên và phần khác sẽ luôn ném ngoại lệ. Mặc dù quan trọng đối với ai đó truy cập một thể hiện của lớp này thông qua giao diện, nhưng việc ai đó sử dụng nó thông qua một biến của loại lớp là vô nghĩa.

Tương tự vì bool ICollection<T>.IsReadOnlyluôn luôn trả về đúng, nó hoàn toàn vô nghĩa đối với mã được viết theo kiểu của lớp, nhưng có thể rất quan trọng đối với việc sử dụng nó thông qua loại giao diện, và do đó chúng tôi triển khai nó một cách rõ ràng.

Ngược lại, public int Countkhông được triển khai rõ ràng bởi vì nó có khả năng có thể được sử dụng cho ai đó sử dụng một thể hiện thông qua loại của chính nó.

Nhưng với trường hợp "rất hiếm khi được sử dụng" của bạn, tôi thực sự sẽ nghiêng về việc không sử dụng một triển khai rõ ràng.

Trong các trường hợp tôi khuyên bạn nên sử dụng một triển khai rõ ràng, gọi phương thức thông qua một biến kiểu của lớp sẽ là một lỗi (cố gắng sử dụng trình thiết lập được lập chỉ mục) hoặc vô nghĩa (kiểm tra một giá trị sẽ luôn giống nhau) để ẩn họ đang bảo vệ người dùng khỏi mã lỗi hoặc tối ưu phụ. Điều đó khác biệt đáng kể với mã mà bạn nghĩ là hiếm khi được sử dụng. Vì vậy, tôi có thể cân nhắc sử dụng EditorBrowsablethuộc tính để che giấu thành viên khỏi intellisense, mặc dù tôi sẽ cảm thấy mệt mỏi; bộ não của mọi người đã có phần mềm riêng để lọc ra những gì họ không quan tâm.


Nếu bạn đang thực hiện một giao diện, hãy thực hiện giao diện. Mặt khác, đây là một sự vi phạm rõ ràng về Nguyên tắc thay thế Liskov.
Telastyn

@Telastyn giao diện thực sự được thực hiện.
Jon Hanna

Nhưng hợp đồng của nó không được thỏa mãn - cụ thể, "Đây là thứ đại diện cho một bộ sưu tập truy cập ngẫu nhiên, có thể thay đổi".
Telastyn

1
@Telastyn nó hoàn thành hợp đồng được ghi lại, đặc biệt là "Bộ sưu tập chỉ đọc không cho phép bổ sung, xóa hoặc sửa đổi các yếu tố sau khi bộ sưu tập được tạo." Xem msdn.microsoft.com/en-us/l Library / 0cfatk9t% 28v = vs.110% 29.aspx
Jon Hanna

@Telastyn: Nếu hợp đồng bao gồm tính biến đổi, thì tại sao giao diện lại chứa một thuộc IsReadOnlytính?
nikie
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.