2009-12-10 24 views
5

Có bất kỳ khả năng kiểm tra loại thời gian an toàn, biên dịch nào đề cập đến các giá trị triển khai nhiều giao diện không?Nhập các giá trị đa hình với nhiều giao diện trong C#

Với

interface A { 
    void DoA(); 
} 

interface B { 
    void DoB(); 
} 

Tôi có thể viết mã cho các đối tượng thực hiện AhayB, nhưng không phải cả hai. Vì vậy, tôi đã đưa ra các trình bao bọc xấu xí:

class ABCollection { 
    private class ABWrapper : A, B { 
     private readonly A a; 
     private readonly B b; 

     public static ABWrapper Create<T>(T x) where T : A, B { 
      return new ABWrapper { a = x, b = x }; 
     } 

     public void DoA() { 
      a.DoA(); 
     } 

     public void DoB() { 
      b.DoB(); 
     } 
    } 

    private List<ABWrapper> data = new List<ABWrapper>(); 

    public void Add<T>(T val) where T : A, B { 
     data.Add(ABWrapper.Create(val)); 
    } 
} 

Có cách nào để viết mã này trực quan hơn mà không làm mất an toàn loại (thời gian chạy-v.v ...)?

Ví dụ:

private List<A and B> ... 

Sửa: Đây không phải là về việc có một danh sách đặc biệt - Tôi chỉ muốn đưa ra một "hoàn thành" ví dụ với vấn đề lưu trữ giá trị như vậy. Vấn đề của tôi chỉ là cách nhập kết hợp cả hai giao diện (như A & B hoặc A and B).

Một hữu ích hơn dụ: List<IDrawable & IMovable> ...

+0

Ghi điểm cho các Generics Java (cuối cùng). –

+0

@mmyers: Java generics sẽ giúp ích như thế nào ở đây? Các ký tự đại diện cho phép bạn viết 'Danh sách ', là tốt đẹp (và C# không thể phù hợp với điều đó), nhưng đó là' AND', và anh ta muốn 'OR'. –

+0

@PavelMinaev: Tôi không muốn HOẶC - Ví dụ Java bạn đưa ra chính xác là những gì tôi đang tìm kiếm. – Dario

Trả lời

6

Bạn có thể làm Đa hình tham số tương tự như vậy trong C#, nhưng không phải là đa hình loại phụ. Tức là, bạn có thể tạo một phương pháp đa hình như:

void Foo<T>(T t) where T : IFoo, IBar 
{ 
    t.Foo(); 
    t.Bar(); 
} 

và sau đó bạn phải vượt qua một đối tượng có thời gian biên dịch để thực hiện cả IFoo và IBar.

Nhưng không có cách nào để nói

void Foo(IFoo-and-IBar t) 
{ 
    t.Foo(); 
    t.Bar(); 
} 

và sau đó vượt qua trong một giá trị đó là cả một IFoo và một Ibar. Tính năng gọn gàng, nhưng không phải là tính năng chúng tôi hỗ trợ.

+0

Tôi không nghĩ đó thực sự là câu hỏi của anh ấy. Tôi nghĩ anh ta đang băn khoăn về 'Danh sách'. –

+0

@Eric Lippert: Cảm ơn. Nếu tính năng không được hỗ trợ trực tiếp, có cách nào thích hợp hơn để mã hóa giải pháp thay thế không? – Dario

+1

@Dario: trình bao bọc mà bạn đã viết là tốt như nó được. Bạn có thể tạo một lớp chung, nhưng sau đó bạn sẽ không thể để lộ các thành viên trên đó. –

0

Tôi không rõ ràng về lý do tại sao bạn muốn làm điều này. Nếu bạn đã làm, bạn có thể khai báo một giao diện cơ sở:

interface AorB {} 

interface A : AorB { 
    void DoA(); 
} 

interface B : AorB { 
    void DoB(); 
} 

và lưu trữ chúng trong bộ sưu tập. Tất nhiên bạn phải có hoặc đang cast khi lấy (các phương thức mở rộng tiêu chuẩn có thể giúp ở đây).

Dường như với tôi rằng đây là sự vi phạm có thể xảy ra của SRP và bộ sưu tập đang hoạt động quá nhiều. Mặt khác, các giao diện quá mịn.

+2

Điều này sẽ không giải quyết được nếu cả hai 'A' và' B' không phải là giao diện được định nghĩa bởi anh ta, và có các lớp cũng không được định nghĩa bởi anh ta (và do đó không biết về sự tồn tại của 'AorB' thực hiện chúng). Ví dụ, hãy tưởng tượng rằng 'A = IComparable', 'B = IFormattable', và anh ta muốn khớp với bất cứ thứ gì có thể là' IComparable' và 'IFormattable' - chẳng hạn như' System.Int32'. –

+0

@Pavel: Điểm tốt. – TrueWill

1

Vâng, như Eric Lippert đã nói, không có loại IFoo-and-IBar nào bạn có thể sử dụng làm loại tham số phương thức.

Tuy nhiên, tôi đã chơi xung quanh với một số ý tưởng và đã đưa ra một cách thay thế sử dụng lớp bao bọc của bạn mà có thể trở nên tốt hơn.Tôi sẽ để lại mà tùy thuộc vào bạn (hoặc bất cứ ai khác có thể tìm kiếm câu hỏi này) để quyết định:

LỚP

public abstract class ABWrapper : IA, IB 
{ 
    private readonly IA a; 
    private readonly IB b; 

    protected ABWrapper(IA a, IB b) { this.a = a; this.b = b; } 

    // Implement methods on IA and IB 
} 

public sealed class ABWrapper<T> : ABWrapper 
    where T : IA, IB 
{ 
    private ABWrapper(T a, T b) : base(a, b) { } 

    public static implicit operator ABWrapper<T>(T t) 
    { 
     if (t == null) return null; 
     return new ABWrapper<T>(t, t); 
    } 
} 

VÍ DỤ

public class AB : IA, IB { } 

void Method(ABWrapper x) 
{ 
} 

void Main() 
{ 
    AB x = null; 
    Method((ABWrapper<AB>) x); 
} 

Điều icky về vấn đề này là bạn cần phải thực hiện một dàn diễn viên đến ABWrapper<T> tại mọi trang web cuộc gọi. Bạn cũng có thể tạo phương thức tiện ích mở rộng ABWrapper ToABWrapper<T>(this T t) where T : IA, IB để thay thế dàn diễn viên nếu thích hợp hơn. Sẽ là tuyệt vời nếu trình biên dịch có thể giải thích rằng chuyển đổi ẩn từ AB thành ABWrapper tồn tại thông qua chuyển đổi ngầm đến và từ ABWrapper<T>. Có lẽ là một lý do rất tốt nhưng nó không cố gắng làm điều đó, tuy nhiên.

Tuy nhiên, những gì bạn đạt được là khả năng đặt ABWrapper tất cả trong suốt các tham số phương pháp của bạn mà không cần phải tăng cường các phương pháp.

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