为什么基类实现基本接口,而派生/具体类实现扩展接口?

6

我正在学习一本名为".NET Domain Driven Design with C#"的书。

问题基于以下类图场景:

图示: http://screencast.com/t/a9UULJVW0

在这个图示中,

A) IRepository接口由(抽象基类)RepositoryBase实现,而

B) IRepository接口也被ICompanyRepository接口扩展(ICompanyRepository : IRepository)。

C) ICompanyRepository由CompanyRepository实现,后者派生自SQLRepositoryBase,后者又派生自RepositoryBase (如A点所述),其实现了IRepository,是ICompanyRepository的父接口。

D) 我创建了一个接口变量ICompanyRepository,并引用了类CompanyRepository的对象,如下所示:

ICompanyRepository comRep = new Company Repository();

现在,如果我使用 ICompanyRepository 变量 comRep 调用 Add() 函数...
comRep.Add(); 

然后调用RepositoryBase类中的Add()函数(它是CompanyRepository的父类)。我的问题是:由于哪个确切的面向对象规则/机制导致在(抽象基类)“RepositoryBase”类中调用Add()函数?为方便起见,我以下列出了两种可能的机制:(请告诉我下面列出的两种机制中哪一种是正确的基础机制)
机制1:因为“RepositoryBase”实现了IRepoitory,所以调用基类“RepositoryBase”中的Add()函数?(因此要求RepositoryBase类实现IRepository才能调用Add())
或者
机制2:在基类“RepositoryBase”中调用Add()函数,因为CompanyRepository实现了ICompanyRepository,ICompanyRepository又实现了IRepository,而IRepository包含Add()函数的定义。因此,当在ICompanyRepository上调用Add()函数时,首先在ICompanyRepository中查找Add的定义,然后在父接口IRepository中查找,然后跳转到CompanyRepository类中查找Add()函数的实现,如果没有找到Add()函数的定义,则向上遍历到父类SQLRepositoryBase中查找Add()函数,最终在RepositoryBase类中找到Add()函数并调用它。这意味着,如果在RepositoryBase的任何派生类中找到Add()函数,则不会向上遍历(在父类中)。所有这些也意味着,在类链中从派生类向父类遍历以查找Add()函数时,RepositoryBase类实际上不需要直接继承IRepository?
在我的问题中有其他的事情,我无法理解哪个面向对象规则适用于我的情况,如下所述:
在我的问题中有两个接口,一个是父类即IRepository,另一个是扩展它即ICompanyRepository。父接口IRepository包含Add()函数的定义,但子接口ICopmanyRepository没有。
类层次结构链中的最后一个派生类“CompanyRepository”实现了ICompanyRepository(CompanyRepository没有实现IRepository接口的Add()函数),而根(最顶层)(抽象基类)类RepositoryBase实现了Add()函数。
因此,结构像http://screencast.com/t/a9UULJVW0中显示的图像一样。
现在如果我调用Add()函数:
ICompanyRepository lastDerived = new CompanyRepository(); ICompanyRepository->Add();
根据你在答案中提到的OO规则,查找将从CompanyRepository类开始,期望CompanyRepository已经实现了Add()函数,如下所示: code IRepository.Add() { } //根据[link] http://www.codeproject.com/Articles/18743/Interfaces-in-C-For-Beginners[link]第17页和第18页推导而来。code 然而,在我的情况下,CompanyRepository类没有实现IRepository.Add(){},尽管控制流(在跟踪时)可以成功跳转到基类中的Add()函数(并且代码工作正常)。我无法理解这里应用了哪个OO规则?
如果需要,让我知道是否需要展示上述带有代码的场景。

5
你能稍微把这个整理一下吗,让它更容易阅读吗? - Robert Harvey
罗伯特,现在好一些了吗? - Fakhar Anwar
1个回答

7
这是很多话。我会重述一下我认为您在问什么,并回答那个问题。如果我理解错了,请让我知道。
当通过接口调用方法时,是否需要在类型层次结构中明确声明该接口被更高级别的类型实现?
是的,这称为“接口重新实现”,它会改变方法的映射方式。 C#语言规范(第13.4.6节接口重新实现)对此进行了更详细的说明,但要点是指定该接口的最高级别类型是查找的起点。
interface ICreature
{
    void Speak();
}

class Animal : ICreature
{
    public void Speak() { Console.WriteLine("Rawr"); }
}

class Duck:Animal
{
    public void Speak() { Console.WriteLine("Quack"); }
}

class Human : Animal, ICreature
{
    public void Speak() { Console.WriteLine("Hello"); }
}

如果按照以下方式操作,将会打印出"Rawr"和"Hello"。
ICreature duck = new Duck();
ICreature human = new Human();
duck.Speak();
human.Speak();

这是因为在Duck层级中,指定ICreature接口的最派生类型是Animal,因此它将打印出"Rawr"。
在Human层次结构中,指定ICreature接口的最派生类型是Human(并且Human声明了实现),因此它将打印出“Hello”。如果Human类型没有声明实现,它也会打印“Rawr”。
更新
在您的特定情况下,完全相同的规则适用。让我们走一遍步骤。
ICompanyRepository继承自IRepository
CompanyRepository声明它实现了ICompanyRepository
CompanyRepository现在已经隐式地重新声明它实现了IRepository,因为ICompanyRepository继承自IRepository
然后调用链遵循以下步骤。
通过实例键入到ICompanyRepository接口来调用Add()方法。
现在显式声明它实现IRepository的最派生类型是CompanyRepository,因此查找从那里开始。
CompanyRepository不直接实现Add()方法,因此检查其父类。
检查SQLRepositoryBase,它不直接实现该方法,因此检查其父类。
检查RepositoryBase,并实现该方法,因此这就是将被调用的方法。

Chris,我的问题中还有一些需要解释的额外事项。我将针对您上面提到的答案和代码以及相关的OO规则(指定接口是查找的起点的最派生类型)再次提出问题。 - Fakhar Anwar
我将编辑您上面的代码,因此请根据更改后的代码回答。 - Fakhar Anwar
@FakharAnwar,你对问题所做的更改似乎与原始问题表达的意思相同。我已经为你的情况添加了具体的查找步骤,以澄清答案。 - Chris Hannon
太棒了,Chris!你的回答真正地回答了我的问题,非常合乎逻辑和精确。我不知道在这里如何给你的回答最高评价。 :) - Fakhar Anwar
答案是传递闭包。 - Nitin Dominic

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