为什么C#中的集合类(如ArrayList)要从多个接口继承,如果其中一个接口继承了其余的接口?

32

当我按下f12键进入由vs2008生成的元数据时,我发现生成的类声明如下所示:

public class ArrayList : IList, ICollection, IEnumerable, ICloneable

我知道IList已经继承了ICollection和IEnumerable,那么为什么ArrayList还要冗余地继承这些接口呢?


2
+1 那是一个非常好的问题。 - AnthonyWJones
我同意。在反射器中,ArrayList被定义为public class ArrayList:IList、ICollection、IEnumerable、ICloneable。 - RichardOD
我已经做了一些研究,并添加了一个带有代码示例的答案。 - RichardOD
为什么这很重要?包括上述每个接口不会使事情更清晰吗? - mP.
@mP- 这并不是很重要,但这仍然是一个有趣的问题! - RichardOD
目前,如果IFoo有成员函数Woozle,并且IDeluxeFoo继承了IFoo,实现IDeluxeFoo的类可以显式地实现IDeluxeFoo.WoozleIFoo.Woozle中的任何一个;这两个定义是同义的。如果实现IDeluxeFoo的类的CLR没有明确指定它实现了IFoo,我不知道CLR是否会识别IFoo.Woozle的实现。 - supercat
7个回答

14

好的,我已经做了一些研究。如果你创建以下层次结构:

  public interface One
    {
        void DoIt();
    }

    public interface Two : One
    {
        void DoItMore();
    }

    public class Magic : Two
    { 
        public void DoItMore()
        {
            throw new NotImplementedException();
        }

        public void DoIt()
        {
            throw new NotImplementedException();
        }
    }

编译它,然后在不同的解决方案中引用DLL,输入Magic并按F12键,您将得到以下结果:

 public class Magic : Two, One
    {
        public Magic();

        public void DoIt();
        public void DoItMore();
    }

你会发现接口层次是扁平化的,还是编译器在添加接口?如果你使用反射器,你也会得到相同的结果。

更新:如果你在ILDASM中打开DLL文件,你会看到它说:

implements ...Two

implements ...One。


我猜这是因为知道这点非常方便。并不是每个人都知道IList继承自ICollection和IEnumerable(或用另一个层次结构代替此句)。 - RichardOD

2

额外的接口显示出来是因为它们被IList暗示了。如果你实现了IList,那么你也必须实现ICollection和IEnumerable。


好的,这意味着它是从VS生成的,而不是ArrayList的真正声明? - Ahmed
是的。如果您使用Reflector来查看基类型,您会发现它只显式地实现了IList和ICloneable接口。这也是微软公开的Rotor源代码中的情况:http://www.123aspx.com/Rotor/RotorSrc.aspx?rot=39810 - Zr40
1
我猜测VS可能会扁平化方法的实现。也就是说,由于IList实现了ICollection,ArrayList提供了ICollection方法的实现。 - shahkalpesh
但它已经在文档中了 http://msdn.microsoft.com/zh-cn/library/system.collections.arraylist.aspx - Ahmed

2
我只是猜测,但我认为实际上它只在代码中实现了IList接口,但文档也显示了其他接口,以使使用该类的程序员更加明确。

3
即使它确实实现了它们所有,接口的明确性也很好,因为你可以通过查看这些接口来确定需要实现的所有内容,而不是深入挖掘接口及其基础等。 - workmad3
我可以理解你的观点,但是我对这是否是件好事持怀疑态度。我想重复一下可能不会有坏处,但我无法想象自己会以这种方式编写代码,这仍然引出一个问题:微软在何处记录了他们这样做的意图? - mkelley33

1

来自MSDN....

如果一个类实现了两个包含相同签名成员的接口,那么在该类上实现该成员将导致两个接口使用该成员作为它们的实现。

显式实现也用于解决两个接口声明同名成员(如属性和方法)的情况:


我的问题是为什么ArrayList继承自ICollection和IEnumerable,而IList也继承自这两个接口? - Ahmed

0

ArrayList 应该明确包含 ICollection 和 IEnumerable 实现,就像 IList 接口一样,它包含 ICollection 和 IEnumerable 实现。但 ArrayList 实现不应依赖于 IList。这样,如果列表结构发生变化,ArrayList 的行为不会受到影响。因此,它们必须被视为完全独立的。
继承 ICollection 和 IEnumerable 对于确保 ArrayList 的独立性非常重要。因此,如果 IList 结构发生更改并且未运行 ICollection 或 IEnumerable,则 ArrayList 结构将不会更改。

例如:
如果您创建以下层次结构:

public interface IOne
{
   void DoIt();
}
public interface ITwo : IOne
{
   void DoItMore();
}
public class Magic : ITwo
{
    public void DoItMore()
    {
        throw new NotImplementedException();
    }
    public void DoIt()
    {
        throw new NotImplementedException();
    }
}

现在,接口 ITwo 决定不使用 IOne 实现。由于我们在 Magic 的实现中明确继承了 IOne,程序可以独立运行,不依赖于对 ITwo 接口的更改。

public interface IOne
{
   void DoIt();
}
public interface ITwo
{
   void DoItMore();
}
public class Magic : ITwo,IOne 
{
    public void DoItMore()
    {
        throw new NotImplementedException();
    }
    public void DoIt()
    {
        throw new NotImplementedException();
    }
}

更新:如果您查看.NET源代码Microsoft文档,您会发现生成的元数据与您在问题中提到的不同。
这可能与IL发射有关。

0

我的猜测是CLR不支持从另一个接口继承的接口。

C#支持这种结构,但必须“展平”继承树以符合CLR规范。

[编辑]

在下面的建议后,我快速设置了一个VB.Net项目:

Public Interface IOne
    Sub DoIt()
End Interface

Public Interface ITwo
    Inherits IOne
    Sub DoIt2()
End Interface

Public Class Class1
    Implements ITwo

    Public Sub DoIt() Implements IOne.DoIt
        Throw New NotImplementedException()
    End Sub

    Public Sub DoIt2() Implements ITwo.DoIt2
        Throw New NotImplementedException()
    End Sub
End Class

编译结果如下(C#):

public class Class1 : ITwo
{
    public Class1();
    public void DoIt();
    public void DoIt2();
}

这表明与C#相反,VB.Net并不会扁平化接口层次结构。我不知道为什么会这样。


-1:与其三周后猜测,为什么不在VB.NET中尝试并找出答案呢?毕竟没有什么急事。 - John Saunders

0

不要接受这个作为答案。

我在重复workmad3上面说的话。

通过在ArrayList中实现它,一个人可以很容易地知道 - ArrayList实现了哪些接口,而不是去IList找到它实现了ICollection和IEnumerable。

这避免了来回继承链的需要。

编辑:在基本层面上,一个接口实现另一个接口不能提供实现。从IList派生的类(因此间接实现ICollection和IEnumerable)也是如此。因此,即使您编写自己的实现IList的类(并且在声明中没有添加ICollection、IEnumerable),您将看到它将不得不为ICollection和IEnumerable提供实现。

而workmad3的推理是有道理的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接