在C#中使用多个接口键入多态值

时间:2009-12-10 16:30:32

标签: c# interface

是否有任何类型安全的,编译时检查可能引用实现多个接口的值?

鉴于

interface A {
    void DoA();
}

interface B {
    void DoB();
}

我能够为实现A B的对象编写代码,但不能同时编写。{p>所以我想出了丑陋的包装纸:

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> ...

3 个答案:

答案 0 :(得分:7)

您可以像在C#中那样执行参数多态,但不能执行子类型多态。也就是说,您可以创建一个多态方法,如:

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

然后你必须传递一个在编译时类型已知的对象来实现IFoo和IBar。

但是没有办法说出来

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

然后传入一个既是IFoo又是IBar的值。整洁的功能,但不是我们支持的功能。

答案 1 :(得分:1)

好吧,正如Eric Lippert所说,没有IFoo-and-IBar类型可以用作方法参数类型。

然而,我正在尝试一些想法,并想出了另一种使用你的包装类的方法,可能更好。我会把这个留给你(或者其他人可能会搜索这个问题)来决定:

<强>类

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来替换强制转换,如果更可取的话。

如果编译器可以推断通过AB的隐式转换存在从ABWrapperABWrapper<T>的隐式转换,那将会很酷。然而,可能有一个非常好的理由不会尝试这样做。

但是,您获得的是能够在整个方法参数中放置ABWrapper而无需对方法进行通用化。

答案 2 :(得分:0)

我不清楚你为什么要这样做。如果你这样做,你可以声明一个基本接口:

interface AorB {}

interface A : AorB {
    void DoA();
}

interface B : AorB {
    void DoB();
}

并将其存储在集合中。当然,在检索时你必须使用is或as-cast(标准扩展方法可以在这里提供帮助)。

在我看来,这可能违反了SRP,而且这个集合做得太多了。或者,界面太精细了。