是否可以定义实现多个接口的任何类型的列表?

时间:2015-01-13 01:15:13

标签: c# generics

考虑以下类层次结构:

public interface X { void Foo(); }

public interface Y { void Bar(); }

public class A : X, Y
{
    public void Foo() {}
    public void Bar() {}
}

public class B : X, Y
{
    public void Foo() {}
    public void Bar() {}
}

有什么方法可以定义一个列表(或任何通用类型),它可以包含A&{39}和B,同时允许我将所述列表的内容视为XY?即能够让我按照以下方式写一些东西的东西:

var list = ???
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();

修改

为了澄清,我目前正在处理的类型是ObservableCollection<T>A)和ReadOnlyObservableCollection<T>B),其中接口I&#39 ;我感兴趣的是IList<T>X)和INotifyCollectionChangedY)。很明显,我无法改变他们的班级层次以满足我的需求,所以我需要一个不同的解决方法。

8 个答案:

答案 0 :(得分:15)

不,除非您声明另一个界面:

IAB : IA, IB {}

并使两个类都实现它。

您还可以实现自己的集合类,例如List<IFirst, ISecond>,这样就可以了。

答案 1 :(得分:6)

您可能不想修改A类和B类 所以包装器将完成这项工作

public class XYWrapper : X, Y
{

  private dynamic InternalObject { get; set; }

  public void Foo() { InternalObject.Foo(); }
  public void Bar() { InternalObject.Bar(); }

  public static implicit operator XYWrapper(A value)
  {
    var instance = new XYWrapper();
    instance.InternalObject = value;
    return instance;
  }

  public static implicit operator XYWrapper(B value)
  {
    var instance = new XYWrapper();
    instance.InternalObject = value;
    return instance;
  }
}

所以你以这种方式使用它:

var list = new List<XYWrapper>();
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();

答案 2 :(得分:2)

是的,如果您完全实施explicitly

public interface IFoo
{
}

public interface IBar
{
}

public class DualList : IList<IFoo>, IList<IBar>
{
    List<IFoo> list1 = new List<IFoo>();
    List<IBar> list2 = new List<IBar>();

    #region IList<IFoo> Members

    int IList<IFoo>.IndexOf(IFoo item)
    {
        return list1.IndexOf(item);
    }

    // Etc etc

    #endregion

    #region IList<IBar> Members

    int IList<IBar>.IndexOf(IBar item)
    {
        return list2.IndexOf(item);
    }

    // Etc etc

    #endregion
}

此容器行为充当两个不同类型的独立列表。要使用,您可以这样投射:

((IList<IFoo>)dualList).Add(item);
然而,我质疑这一点的智慧。你肯定无法使用任何已知的序列化程序序列化这样的列表。

答案 3 :(得分:2)

你可以声明,

public class SomeType<T> where T: X, Y
{
    public void Do(T t)
    {
        t.Foo();
        t.Bar();
    }
}

here类似,您知道由于通用类型限制,T必须同时实现XY

但是,当您实例化SomeType时,您需要AB继承或实施的单一类型。

如果你只是添加,

interface Z : X, Y { }

然后更改AB以实施Z

public class A : Z
{
    public void Foo() {}
    public void Bar() {}
}

public class B : Z
{
    public void Foo() {}
    public void Bar() {}
}

您只需使用Z作为通用类型。

答案 4 :(得分:1)

如果您创建两个接口的复合接口:

interface IA
{
    void Foo();
}
interface IB
{
    void Bar();
}
interface IAB:IA,IB{}

现在开始你的两个课程:

class A:IA,IB
{
    public void Foo(){}
    public void Bar(){}
}

class B:IA,IB
{
    public void Foo(){}
    public void Bar(){}
}

并创建实现复合接口的子类(不需要实际实现,因为它已经存在于复合接口中的两个接口中):

class AX:A,IAB
{
}
class BX:B,IAB
{
}

现在你可以......

void Main()
{
    List<IAB> list=new List<IAB>();
    list.Add(new AX());
    list.Add(new BX());
}

答案 5 :(得分:1)

如果要在列表中使用不同的具体类型,每个都实现不同的接口(可能是包含的或互斥的),那么您需要测试每个接口,这通常是使用instance as ISomeInterface完成并检查是否结果是非空的。

请注意,以下内容适用于实现IFooable和IBarrable的类型,或仅实现两者之一。因此它在保持类型安全的同时为您提供了很多灵活性。唯一的缺点是具体类型可能直接实现标记接口而根本不实现IFooable或IBarrable。

// marker interface
public interface IFooBar
{
}

public interface IFooable:IFooBar
{ void Foo(); }

public interface IBarrable:IFooBar
{ void Bar(); }


public class FooBarProcessor
{
    public void ProcessFooBars(IList<IFooBar> foobars)
    {
        foreach(var foobar in foobars)
        {
           // feature detection to see which interfaces the instance implements
           IBarrable bar = foobar as IBarrable;
           if(bar != null)
               bar.Bar();

           IFooable foo = foobar as IFooable;
           if(foo != null)
               foo.Foo();
        }
    }
}

如果您无法控制您的界面,那么您可以使用List<object>,它的类型安全性稍差,但仍然使用foobar as IBarrable技术。

如果你仍然想利用标记接口来更多地限制IList,你可以用你喜欢的界面包装你可以改变的界面,其中IOutOfMyControlFooable是你能做的任何界面&#39 ; t改变:

public interface IFooable : IFooBar, IOutOfMyControlFooable
{ void Foo(); }

public interface IBarrable : IFooBar, IOutOfMyControlBarrable
{ void Bar(); }

public class FooBarProcessor
{
    public void ProcessFooBars(IList<IFooBar> foobars)
    {
        foreach(var foobar in foobars)
        {
           // feature detection to see which interfaces the instance implements
           IBarrable bar = foobar as IBarrable;
           if(bar != null)
               bar.Bar();

           IFooable foo = foobar as IFooable;
           if(foo != null)
               foo.Foo();
        }
    }
}

答案 6 :(得分:1)

使用这样的包装器类型:

public class Wrapper<T>() {
    public WrapperObsCollection(ObservableCollection<T> collection) {
      IsReadOnly      = false;
      ObsCollection   = collection;
      ROObsCollection = null;
    }
    public WrapperObsCollection(ReadOnlyObservableCollection<T> collection : base {
      IsReadOnly      = true;
      ObsCollection   = null;
      ROObsCollection = collection;
    }

    public bool                            IsReadOnly        { get; private set; }
    public ObservableCollection<T>         ObsCollection     { get; private set; }
    public ReadOnlyObsrevableCollection<T> ROObsCollection   { get; private set; }
}

- 等。

答案 7 :(得分:0)

你有没有尝试过这样的额外课程......

public class ABList<T> : List<T> where T : X, Y
{
    // implement me
}

...并像这样使用它?

var list = new ABList<dynamic>(); // issue here; see comment below
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();

更新:由于编译器问题,解决方案无效(感谢@mikez)。如果类型检查无关紧要,可以优化解决方案,只使用这样的动态列表:

var list = new List<dynamic>();
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();