2009-12-10 37 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方法的能力。