2010-06-21 23 views
16

Giả sử tôi có hệ thống phân cấp lớp sau đây:C# generics - Tôi có thể biến T thành một trong hai lựa chọn không?

Class A {...} 

Class B : A {...} 

Class C : A {...} 

Những gì tôi đang có là

Class D<T> where T : A {...} 

nhưng tôi muốn một cái gì đó có dạng

Class D<T> where T in {B,C} 

Điều này là do một số hành vi kỳ lạ Tôi không chịu trách nhiệm về nơi B và C có phương pháp phổ biến mà không phải là trong A, nhưng nó sẽ là tốt đẹp để có thể gọi chúng trong D trên T.

Lưu ý: Tôi không có quyền truy cập vào A, B hoặc C để chỉnh sửa chúng

Trả lời

23

Bạn cần phải xác định một giao diện cho các phương pháp phổ biến có trong B và C (cho phép gọi nó là IBC), làm cho B và C thực hiện giao diện này, và sau đó bạn có thể viết:

Class D<T> where T : A, Ibc {...} 
+6

Đó là một số rootbeer tốt ... – Nate

+7

Nhưng, ông không kiểm soát các nguồn A, B và C. Sẽ làm cho giải pháp này một chút khó khăn để thực hiện. – GvS

+0

Ah Tôi chưa thấy bản chỉnh sửa. Nếu một giao diện như vậy không tồn tại, thì không có cách nào trực tiếp để triển khai lớp D theo như tôi biết. – Grzenio

3

Do B và C có triển khai cùng một giao diện không? Đó có thể là một tuyến đường tốt hơn.

2

Một số tùy chọn :

  1. Thực hiện một giao diện IderivedFromA có chứa các phương pháp phổ biến từ BC.
    Trông như thế này là không thể từ câu hỏi của bạn
  2. Trong D đúc T-dynamic và gọi các phương pháp tự động
    Các giải pháp dễ dàng nhất, nếu bạn có thể sử dụng Net 4
  3. Trong D kiểm tra nếu bạn xử lý một số B hoặc C, truyền và gọi
    Sẽ được trình biên dịch kiểm tra và có thể từ .Net 2
  4. The Dan Tao answer: Tạo triển khai cụ thể D<T> cho BC, chúng có thể gọi trực tiếp các phương thức từ số BC. (Không nghĩ về điều này một mình).
    Sẽ chỉ hoạt động nếu "nguồn người dùng" biết rằng nó đang xử lý B hoặc C và không sử dụng số trừu tượng A để sử dụng D<A>. Thay vào đó, bạn nên sử dụng DB hoặc DC. Nhưng tôi nghĩ rằng đây là trường hợp, nếu không bạn không cần generics.
0

Trường hợp hạn chế trong C# không cho phép bạn chỉ định nhiều lớp như một lựa chọn. Ngoài ra nếu bạn sẽ chỉ định nhiều nơi chứa, thì cả hai đều phải được thỏa mãn. Không có logic OR cho ràng buộc. Đây là đặc điểm kỹ thuật: http://msdn.microsoft.com/en-us/library/bb384067.aspx

Câu trả lời từ Grzenio có vẻ phù hợp với bạn. Trích xuất hành vi phổ biến vào giao diện chung cho B và C. Sau đó, bạn có thể sử dụng giao diện đó làm giới hạn.

8

Điều này không thể trực tiếp.

Như những người khác đề xuất, bạn có thể xác định giao diện và triển khai giao diện trong cả hai BC.

Nếu đây không phải là tùy chọn (ví dụ: nếu các lớp này nằm ngoài tầm kiểm soát của bạn), điều tôi có thể đề xuất là: đầu tiên, bắt đầu với lớp trừu tượng bao gồm tất cả chức năng bạn có thể đạt được với bất kỳ T nào xuất phát từ A. Sau đó, nói rằng bạn có một số phương pháp tồn tại cho cả hai số BC không phải là một phần của A. Trong D bạn có thể làm cho các phương pháp trừu tượng được thực hiện bởi lớp con:

public abstract class D<T> where T : A 
{ 
    protected T _member; 

    public void DoSomethingAllTsCanDo() 
    { 
     _member.DoSomething(); 
    } 

    public abstract void DoSomethingOnlyBAndCCanDo(); 
} 

Sau đó, bạn có thể kế thừa từ lớp cơ sở đối với từng loại BC và ghi đè lên các phương pháp trừu tượng (s) để cung cấp các chức năng thích hợp:

public class DB : D<B> 
{ 
    public override void DoSomethingOnlyBAndCCanDo() 
    { 
     _member.DoSomethingOnlyBCanDo(); 
    } 
} 

public class DC : D<C> 
{ 
    public override void DoSomethingOnlyBAndCCanDo() 
    { 
     _member.DoSomethingOnlyCCanDo(); 
    } 
} 
0

vì bạn không có quyền truy cập vào các nguồn, câu trả lời thực chỉ (trừ khi bạn sẵn sàng để mất an toàn bằng cách sử dụng dynamic) là rõ ràng kiểm tra B/C và truyền.

5

Đầu tiên, nếu B và C có phương pháp chung, đó là lỗi thiết kế mà chúng không chia sẻ giao diện. Điều đó nói rằng, bạn có thể khắc phục điều đó ngay cả khi không có quyền truy cập vào B và C.
Có thể tạo giao diện chung. Giả sử bạn có:

public class A 
{ 
} 
public class B : A 
{ 
    public void Start() { } 
} 
public class C : A 
{ 
    public void Start() { } 
} 

Bạn có thể tạo một giao diện chung:

public interface IStartable 
{ 
    void Start(); 
} 

Và sử dụng nó trên các lớp thừa kế từ B và C:

public class BetterB : B, IStartable 
{ 
} 
public class BetterC : C, IStartable 
{ 
} 

Bạn không có khả năng để đạt được rằng nếu bạn nhận được trường hợp B và C như hiện tại, nhưng nó có thể được xem xét nếu bạn tạo chúng. Trong thực tế, với các lớp chuyên biệt của B và C, bạn có thể sử dụng giao diện thay vì D<T>.

Các vấn đề liên quan