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

Is it possible to define a list of any type that implements multiple interfaces?

考虑以下 class 层次结构:

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() {}
}

有什么方法可以定义一个列表(或任何通用类型),它可以同时包含 AB,同时允许我处理内容所述列表的 XY? IE。可以让我按照以下方式写一些东西的东西:

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

编辑

澄清一下,我目前处理的类型是 ObservableCollection<T> (A) and ReadOnlyObservableCollection<T> (B),我感兴趣的接口是 IList<T> (X) 和 INotifyCollectionChanged (Y)。显然,我无法更改他们的 class 层次结构来满足我的需求,因此我需要一个不同的解决方法。

不,除非你声明另一个接口:

IAB : IA, IB {}

并让两个 class 都实现它。

您也可以实现自己的集合 class,类似于 List<IFirst, ISecond>,这将允许这样做。

是的,如果你完全实施它们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);

然而,我质疑这是否明智。使用任何已知的序列化器序列化这样的列表肯定会遇到问题。

你试过这样的额外class吗...

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

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

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

如果你要在一个列表中有不同的具体类型,每个都实现一个不同的接口(可能包含或互斥),那么你需要测试每个接口,这通常使用 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 是您不能更改的任何接口变化:

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

您可能不想修改 class 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();

使用这样的包装器类型:

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

--等等

可以申报,

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 作为通用类型。