接口多次实现的问题(.Net 1.1)

接口多次实现的问题(.Net 1.1

1.       接口的显式与隐式实现:

a)       接口的显式实现:

这个问题其实很简单,就是一般的接口实现。例子:

      public interface ITest

      {

           void ShowMsg();

      }

 

      public class TestA : ITest

      {

           #region ITest Members

           public void ShowMsg()

           {

                 MessageBox.Show("TestA:");

           }

           #endregion

      }

这里定义了一个Itest接口,并且在TestA类中显示实现了它。可以用实例直接访问接口里的方法,也可以把实例转化为接口后再来访问接口上的方法。

b)       接口的隐式实现:还是上面的接口,

      public class TestA : ITest

      {

           #region ITest Members

           void ITest.ShowMsg()

           {

                 MessageBox.Show("TestA:ITest");

           }

           #endregion

      }

      这里就是一个隐式实现,注意,在void ITest.ShowMsg()方法上不能添加任何访问修饰符。但实际上它是私有的。隐式实现的方法只有在把实例转化为接口后才能访问。

2.       接口的多次实现:

a)       接口的简单多次实现

最简单的多次实现就是一个类同时显式和隐式的实现同一个接口。那么结果会是什么样呢?假设上面的例子中,TestA同时隐式和显示的实现了接口Itest,那么看下面的例子:

           TestA m_test = new TestA();

           m_test.ShowMsg();

           (m_test as ITest).ShowMsg();

输出:TestA:TestA:Itest,也就是说,当实例以类的身份存在时,所调用的方法是显式实现的。其实是类的一个方法。而当把实例转化为接口时,就可以访问隐式实现的方法了。注意,如果没有隐式实现时,转化为接口后还是访问显式实现的方法。也就说,这样就可以在一个类里实现两个完全一样的函数了(比重载还强!!?),而隐式实现的那一个是只有在转化为接口后才能访问的。但我们明显的知道,这个类上有两个完全一样的方法,除了访问权限不一样以外,而重载是不能只修改访问权限来完成的。用类查看器看一下类的成员,会发现隐式接口实现的方法小有一点不同。它是以全名(名字空间和方法名)存在的。或者这样的接口多次实现意义并不大,但实现上.net库中的很多类都隐式的实现了很多接口。Int32就是一个例子。这样的实现的好处就是可以让一个类看上去很简单,不至于被大量的方法和属性搞糊了。这也是MS的一个初衷。然而它却有下面的一个问题。

b)       接口的继承上多次实现:

                        i.              父类的显示实现和子类的显示实现。下面的代码会是什么如果呢?
public class TestB : ITest

      {

           #region ITest Members

           public void ShowMsg()

           {

                 // TODO:  Add TestB.ShowMsg implementation

                 MessageBox.Show("TestB:");

           }

           #endregion

      }

//

              TestB m_test = new TestB();

              m_test.ShowMsg();

              (m_test as TestA).ShowMsg();

              (m_test as ITest).ShowMsg();

        如果是:TestB:TestA:TestB:。也就是说,(m_test as ITest).ShowMsg();调用的是TestB上的接口方法。注意,因为子类和父类都有同样的方法,因此编译器会给出提示,在子类的方法上应该添加关键字new

那么:((m_test as TestA) as ITest).ShowMsg();会是什么呢?

结果还是:TestB:为什么?也就是说第一步的把m_test转化为TestA是无效的?不是,这里的转化是有效,无效的是在转化为TestA的实例上再转化Itest并不是在TestA的实例上转化,而还是在原来的实例上进行转化,即是TestB的实例,所以结果还是TestB:。还好,因为父类使用的是显示继承,我们还可以用(m_test as TestA).ShowMsg();来访问父类的这一方法。然而,我们用类查看器来看类结构时,发现子类与父类的两个接口都存在,然而,在子类的实例上,已经无法访问父类的接口了。而用Reflector查看结构时,却发现TestB类上根本没有ITest的实现。只有父类的实现,而反汇编的代码却还是一样的,显示的实现了这一接口。

                       ii.              父类的显式实现和子类的隐式实现。这一结果比较简单,和上面的基本一样,只不过在子类的类实例上调用的是类方法,接口上调用的是隐式实现的方法。转化为父类后,就只调用父类的方法。一样无法转化为父类的接口。

                     iii.              父类的隐式实现和子类的显式实现。

TestB m_test = new TestB();

      m_test.ShowMsg();

      (m_test as ITest).ShowMsg();

      ((m_test as TestA) as ITest).ShowMsg();

如果还是一样,都是访问TestB类上的成员。根本就无法转化对象成为TestA上隐式实现的接口。说的再简单一点,在TestA上实现的隐式接口,在其派生类中,如果派生类也实现了该接口(不管是隐式还是显式),其派生类的实例都是永远无法访问TestA类上实现的接口了。

                    iv.              父类与子类的同时隐式实现,这就不用多说了,结果同上。

                      v.              父类与子类的都同时显式和隐式实现同一接口。这一情况看上去比较复杂,但仔细想一想,其实还是很简单的:

1.       子类实例以类形式存在时访问类方法(显式实现的方法)。

2.       子类实例以接口形式存在时访问接口方法(隐式实现的方法)。

3.       子类实例以父类形式存在时,访问父类方法(一般类的方法访问,当然也可以看做是访问父类显式实现的方法)。

4.       子类无法以父类的接口形式存在,所以也就无法访问父类的隐式接口方法。

3.       深入分析接口的实现:

a)       上面的讨论看的出来,一但子类实现了同样的接口,就意味着永远覆盖了父类实现的接口。而显式实现的接口只能以类形式访问,无法用接口访问。我的问题是,父类的接口隐式实现方法是否在也实际了接口子类的实例中存在?如果存在,又应该如何访问?先看存在问题。

                        i.              先用Reflector来看看:

image002.jpg
我们看到,类
TestB的基类上根本没有显示它实现了ITest接口,而在接口的派生层次上看到,TestB->TestA->ITest,也就是说,虽然我们多次实现了接口,其实却也只是单一的层次关系。而接口的隐式实现确实是有两个不同的版本存在于两个类中。用ILDASM工具查看的结果也与此一致。

b)       这是一个迷惑的问题,可能很多人都没有也不会去注意这一问题。或者根本就不知道接口的几种实现方法。这样的分析,虽然结果让人不是很满意,但至少也应该明白接口的这几种实现方法对类的设计,以及它的派生类的影响。这里推荐Effective C#里的接口实现方法,多数情况下使用虚函数方法来实现接口。不到万不得以,不使用隐式接口实现。同时要明白,一般使用隐式接口实现,就意味着它的派生类可能丢失这一接口实现,那就是在派生类也实现了这一接口时。

 

这里简单的讨论了一下接口的几种实现以及多次实现时的一些问题。结果不是很令人满意,而且我还是不太相信父类的接口实现就这样变得无影无踪了,但目前为止,我还没有办法来访问父类上的接口。我试过反射也不行,最后不得不修改一些代码,把隐式实现转化为虚函数来实现。希望本文起个抛砖引玉的作用,希望有知道细节问题的朋友们一起讨论一下。

 

原文地址:https://www.cnblogs.com/WuCountry/p/743697.html