13.C#基础之接口(完成)

     接口:定义一个约定,实现某接口的类或结构必须遵守该接口定义的约定;接口可以包含方法、属性、事件和索引器,接口本身不提供它所定义的成员的实现,接口只指定实现该接口的类或结构必须提供的成员。

     一个接口可以继承多个接口,一个类或结构也可以实现多个接口。

  13.1接口声明

   13.1.1 接口修饰符

     其中new修饰符只允许出现在类中定义的接口中使用,它用来指定接口隐藏同名的继承成员。其他的第三章都有写到过。

   13.1.2 基接口

     接口可以继承零个或多个接口,被继承的接口称为该接口的显式基接口。

     接口的显式接口必须至少具有与该接口相同的可访问性。

     接口的基接口包括显式基接口,以及显式基接口的基接口。换言之,基接口集是显式基接口及它们的显式基接口的完全可传递的闭包,接口继承其基接口的所有成员。比如:

     IComboBox的基接口是IControl,ITextBox和IListBox。实现了某接口的类或结构还隐式地实现了该接口的所有基接口。

   13.1.3 接口体

  13.2接口成员

     接口的成员也是两部分,一部分自己声明的,一部分继承来的。开头有说,接口自己声明的有方法、属性、事件和索引器;接口不能包含常数、字段、运算符、实例构造函数、析构函数或类型,也不能包含任何种类的静态成员。

     所有接口成员都隐式地具有public访问属性,且接口成员包含任何修饰符都会发生编译时错误。下面创造一个接口:

     上面接口涵盖了所有可能作为接口成员的种类。

     接口声明创建新的声明空间,接口声明直接包含的接口成员声明将新成员提供给该声明空间。有几点需要注意:

             1.方法的名称不能和同一接口声明的属性和事件的名称相同;方法的签名和其他方法的也不能相同;

             2.属性或事件的名称不能和同一接口的其他成员的相同;

             3.索引器的签名也不能和同一接口声明的其他索引器相同。

     接口所继承的成员不是该接口声明空间的一部分,所以可以隐藏基接口的成员,但派生接口的声明中必须包含new修饰符,不然会警告。

   13.2.1 接口方法

      接口方法声明中的属性、返回类型、标识符和形参表与类中的方法声明的对应项具有相同的意义。不允许接口方法声明指定方法体,因此,声明总是以分号结尾。

   13.2.2 接口属性

     接口属性声明的访问器与类属性声明的访问器相对应,不同之处在于接口属性声明的访问器体必须始终是一个分号。因此访问器在这里只用于表示该属性为读写、只读还是只写。

   13.2.3 接口事件

     接口事件中声明的属性、类型和标识符与类中事件声明的对应项具有相同的意义。

   13.2.4 接口索引器

     接口索引器中声明的属性、类型和形参表与类中索引器声明的对应项具有相同的意义;接口索引器声明的访问器与类索引器声明的访问器也相对应,不同之处在于接口索引器声明的访问体必须始终是一个分号。

   13.2.5 接口成员访问

     接口成员是通过I.M形式的成员访问表达式,也可以通过I[A]形式的索引器访问表达式来访问。I是接口类型,M是接口类型的方法、属性或事件,A是对应的索引器参数列表。

     对于严格单一继承(即继承链的每个接口恰好有零个或一个直接基接口)的接口,成员查找、方法调用和索引器访问规则的效果与类和结构的完全相同:派生程度较大的成员隐藏具有相同名称或签名的派生程度较小的成员。

     对于多重继承接口,当两个或更多各不相关(互不继承)的基接口声明了具有相同名称或签名的成员,就会发生多义性,在所有情况,都可以使用显式强制转换来解决这种多义性。比如:

     此类强制转换没有运行时开销,它们只是在编译时将该实例所属类型视为派生程度较小的类型而已。还有其他情况:

     由于方法调用要求以相同类型声明所有的重载候选方法,所以第一个调用是不明确的。然而第二个1.0就是double类型,所以是允许的。强制后就只有一个候选方法了。还有其他情况:

     多重继承接口中隐藏的直观规则简单地说明就是:如果成员在任何一个访问路劲中被隐藏,那么在所有访问路径中都被隐藏。就和上面的第一个调用一样,虽然从IDerived经IRight到IBse的访问路径没有隐藏IBse.F,但从IDerived经ILeft到IBse的访问路径隐藏了IBse.F,所以第一个调用仍是用ILeft.F。

  13.3完全限定接口成员名

     接口成员有时也用它的完全限定名来引用。完全限定名是由:声明该成员的接口的名称,后面跟一个点,再跟成员的名称。

     Paint的完全限定名是IControl.Paint,而SetText的完全限定名是ITextBox.SetText。不能用ITextBox.Paint来引用Paint方法。

     若接口在命名空间声明,则最前面还要加上命名空间名称。

  13.4接口实现

     接口可以由类和结构来实现。

     如果类或结构实现某个接口,还要隐式地实现该接口的所有基接口,即使在类或接口的基类列表没显式地列出来。

   13.4.1 显式接口成员实现

     为了实现接口,类或结构可以声明显式接口成员实现。显式接口成员是一种方法、属性、事件或索引器声明时,必须使用完全限定接口成员名作标识符的方法。比如:

     上面的ICloneable.Clone和IComparabel.CompareTo的声明就是显式接口成员实现。显式接口成员实现是为了在接口成员的名称在实现接口的类不方便声明时使用的。且在方法调用、属性访问和索引器访问中,不能直接访问显式接口成员实现的成员,使用完全限定名也不行,只能通过接口实例访问,并且在实例访问时,只能用接口成员的简单名称来引用。

     还有显式接口成员实现中不能包含访问修饰符,也不能包含abstract、virtual、override或static修饰符。

     显式接口成员实现由两个主要用途:1.由于显式接口成员实现不能用过类或结构实例访问,所以它们不属于类或结构自身的公共接口。即只能内部使用;

                                     2.显式接口成员实现可以消除因同时含多个相同签名的接口成员引起的多义性。

     为了使显式接口成员实现有效,声明它的类或结构必须在它的基类列表中指定一个接口,而该接口必须包含一个成员,该成员的完全限定名、类型和参数类型与该显式接口成员所具有的完全相同。比如:

     第二个无效是因为IComparable未在Shape的基类列表,并且不是ICloneable的基接口。

   13.4.2 接口映射

     刚才写到过类或结构必须为它的基类列表中所有接口的所有成员提供它自己的实现。在进行实现类或结构中定位接口成员的实现的过程就是接口映射。通俗点说,关于类或结构C的接口映射就是查找C的基类列表中指定的每个接口的每个成员的实现。

     对于某个特定成员I.M的实现的定位按下述规则执行:从C开始,按继承顺序,逐个查找它的每个后续基类,直到找到匹配项。

     如果找不到接口成员的实现,则会发生编译时错误。

     根据接口映射的含义,在下列情况下类成员A与接口成员B匹配:

     接口映射算法中需要注意的的几点:1.在类或结构成员确定哪个接口成员实现时,显式接口成员具有更高的优先级;

                                     2.接口映射不涉及非公共成员和静态成员。

     如果类或结构继承有2个或2个以上的接口,且这些接口含有相同的名称、类型和参数类型的成员,则可以全部映射到当个类或结构成员上。比如:

     接口的Paint方法都映射到page的Paint方法上了。如果类或结构实现一个包含被隐藏成员的接口,那么至少需要一个显式接口成员实现。比如:

     当已给类实现多个具有相同基接口的接口时,为该基接口提供的实现只能有一个。比如:

     发ITextBox和IListBox都继承了IControl,相当于三个接口都有F方法,但它们不可能有各自的实现,后两者的实现共享给IControl的实现。

   13.4.3 接口实现继承

     类继承是由其基类提供所有接口的实现。如果不显式地重新实现接口,派生类就无法以任何方式更改它从其基类继承的接口映射。比如:

     TextBox中的Paint方法隐藏Control中的Paint方法,但这种隐藏并不更改Control.Paint到IControl.Paint的映射,所以通过类实力和接口实例对Paint进行的调用有不同的结果:

     其实在前面写过,在非虚拟的方法中,该调用所涉及的那个实例的编译时类型是决定性因素。但当接口方法被映射到类的虚拟方法上,从该类派生的类重写了该虚拟方法,则将同时更改该接口的实现。比如:

     由于显式接口成员实现不能被声明为虚拟的,所以不能用上面这种方法;显式接口成员实现完全可以调用另一个方法,只要将该方法声明为虚拟方法,派生类就可以重写它了。比如:

     从Control派生的类可以通过重写PaintControl方法来专用化IControl.Paint的实现。

   13.4.4 接口重新实现

     一个类若继承了某个接口的实现,则只要将该接口列入它的基类列表中,就可以重新实现该接口。继承的接口映射不会对为重新实现该接口而建立的接口映射产生任何影响。

     上面Control将IControl.paint映射到Control.IControl.Paint上,而这并不会影响MyControl中IControl.paint的重新实现,在此重新实现中,会将IControl.paint映射到MyControl.paint。

     被继承的公共成员声明和被继承的显式接口成员声明可以参与重新实现接口的接口映射过程。比如:

     上面Derived中IMethods的实现将各个接口方法分别映射到Derived.F,Base.IMethods.G,Derived.IMethods.H和Base.I上。

     当类实现接口时,还隐式地实现该接口的所有基接口,同时,接口的重新实现也同时隐式地对该接口的所有基接口进行重新实现。比如:

   13.4.5 抽象类和接口

     与非抽象类类似,抽象类也必须为在该类的基类列表中所有列出的所有成员提供实现。比如:

     派生类必须实现这些抽象。但显式成员本身不能抽象,但可以抽象显式接口成员。比如:

原文地址:https://www.cnblogs.com/dreamoffire/p/10116648.html