2009-12-10 147 views
5

是否存在任何類型安全的編譯時檢查可能性是否引用實現多個接口的值?在C#中使用多個接口輸入多態值

鑑於

interface A { 
    void DoA(); 
} 

interface B { 
    void DoB(); 
} 

我能夠編寫代碼實現AB,而不是兩個對象。所以我拿出醜陋包裝:

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

有沒有竅門,以更直觀地編寫代碼,而不會失去類型安全(運行時類型轉換等)?

E.g.

private List<A and B> ... 

編輯:這是不是有特別的列表 - 我只是想給與存儲這樣的價值觀的問題「完整」的例子。我的問題只是如何鍵入兩個接口的組合(如A & BA and B)。

另一個更加有用例如:List<IDrawable & IMovable> ...

+0

分數爲Java泛型(最終)的點。 – 2009-12-10 16:32:26

+0

@mmyers:Java泛型如何在這裏幫助?通配符讓你寫'List <?擴展了A&B>,這很好(而C#不能匹配),但是這是'AND',他想要'O​​R'。 – 2009-12-10 16:49:16

+0

@PavelMinaev:我不想要或者 - 你給的Java例子正是我正在尋找的東西。 – Dario 2009-12-10 16:50:44

回答

6

你可以做參數多態性像C#那樣,但不是亞型多態性。也就是說,你可以創建一個像多態方法:

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

,然後你必須通過一個對象,其類型在編譯時是已知的同時實現的IFoo和伊巴爾。

但是沒有辦法說

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

,然後通過在既是IFoo的和IBAR的值。整潔的功能,但不是我們所支持的。

+0

我不認爲這實際上是他的問題。我想他對「List」感到疑惑。 – 2009-12-10 16:56:38

+0

@Eric Lippert:謝謝。如果該功能不被直接支持,是否有更好的編碼解決方法? – Dario 2009-12-10 17:01:22

+1

@Dario:你寫的包裝器大概和它一樣好。你可以創建一個泛型類,但是你將無法公開其上的成員。 – 2009-12-10 17:15:18

0

我不是爲什麼你要做到這一點。如果你這樣做,你可以聲明一個基本接口:

interface AorB {} 

interface A : AorB { 
    void DoA(); 
} 

interface B : AorB { 
    void DoB(); 
} 

並將它們存儲在集合中。當然,當檢索時(標準擴展方法可以在這裏幫助),你必須是或現在。

在我看來,這可能違反了SRP,並且收集過多。或者接口太細。

+2

這不會解決它,如果沒有'A'和'B'是不是由他定義的接口,並有(因而不知道AorB'的'存在其中實現它們的)類也不能由他定義。例如,假設'A = IComparable','B = IFormattable',他希望以匹配任何既是'IComparable'和'IFormattable' - 比如'System.Int32'。 – 2009-12-10 17:13:55

+0

@Pavel:好點。 – TrueWill 2009-12-10 18:48:17

1

那麼,正如Eric Lippert所說,沒有IFoo-and-IBar類型可以用作方法參數類型。

但是,我在玩弄一些想法,並想出了一種替代方式,使用你的包裝類可能更好。我會離開,你(或其他任何人可能會搜索這個問題)決定:

CLASSES

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

public class AB : IA, IB { } 

void Method(ABWrapper x) 
{ 
} 

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

關於這個噁心的事是你需要在每個呼叫站點播放ABWrapper<T>。你也可以創建一個擴展方法ABWrapper ToABWrapper<T>(this T t) where T : IA, IB來替換這個cast,如果這更好。

如果編譯器可以推斷從ABABWrapper的隱式轉換通過與ABWrapper<T>之間的隱式轉換存在,那將是很酷的。但是,可能有一個很好的理由,它不會嘗試這麼做。

不過,你得到就是把所有ABWrapper整個方法參數,而不需要genercize方法的能力。