c语言接口与实现:C#实现接口



第 5节、实现接口

  1、显式实现接口成员

  为了实现接口类可以定义显式接口成员执行体(Expliciterfacememberimplementations)显式接口成员执行体可以是个思路方法、个属性、个事件或者是个索引指示器定义定义和该成员对应全权名应保持

using;
erfaceICloneable{
 objectClone;
}
erfaceIComparable{
 CompareTo(objectother);
}
ListEntry:ICloneable,IComparable{
 objectICloneable.Clone{…}
 IComparable.CompareTo(objectother){…}
}

  上面代码中ICloneable.Clone和IComparable.CompareTo就是显式接口成员执行体

  介绍说明:

  1、不能在思路方法、属性访问以及索引指示器访问中通过全权名访问显式接口成员执行体事实上显式接口成员执行体只能通过接口例子仅仅引用接口成员名称来访问

  2、显式接口成员执行体不能使用任何访问限制符也不能加上abstract,virtual,override或修饰符

  3、显式接口成员执行体和其他成员有着区别访问方式不能在思路方法、属性访问以及索引指示器访问中通过全权名访问显式接口成员执行体在某种意义上是私有但它们又可以通过接口例子访问也具有公有性质

  4、只有类在定义时把接口名写在了基类列表中而且类中定义全权名、类型和返回类型都和显式接口成员执行体完全致时显式接口成员执行体才是有效例如:

Shape:ICloneable{
objectICloneable.Clone{…}
IComparable.CompareTo(objectother){…}
}
使用显式接口成员执行体通常有两个目:

  1、显式接口成员执行体不能通过类例子进行访问这就可以从公有接口中把接口实现部分单独分离开如果个类只在内部使用该接口而类使用者不会直接使用到该接口这种显式接口成员执行体就可以起到作用

  2、显式接口成员执行体避免了接口成员的间同名而发生混淆如果个类希望对名称和返回类型相同接口成员采用区别实现方式这就必须要使用到显式接口成员执行体如果没有显式接口成员执行体那么对于名称和返回类型区别接口成员类也无法进行实现

  下面定义是无效Shape定义时基类列表中没有出现接口IComparable

Shape:ICloneable
{
objectICloneable.Clone{…}
}
Ellipse:Shape
{
objectICloneable.Clone{…}
}

  在Ellipse中定义ICloneable.Clone是Ellipse即使隐式地实现了接口ICloneableICloneable仍然没有显式地出现在Ellipse定义基类列表中

  接口成员全权名必须对应在接口中定义成员如下面例子中Pa显式接口成员执行体必须写成IControl.Pa

using;
erfaceIControl
{
 voidPa;
}
erfaceITextBox:IControl
{
 voidSetText(text);
}
TextBox:ITextBox
{
 voidIControl.Pa{…}
 voidITextBox.SetText(text){…}
}


  实现接口类可以显式实现该接口成员当显式实现某成员时不能通过类例子访问该成员而只能通过该接口例子访问该成员显式接口实现还允许员继承共享相同成员名两个接口并为每个接口成员提供个单独实现

  下面例子中同时以公制单位和英制单位显示框尺寸Box类继承IEnglishDimensions和IMetricDimensions两个接口它们表示区别度量衡系统两个接口有相同成员名Length和Width

  清单1DemonInterface.cs

erfaceIEnglishDimensions{
floatLength;
floatWidth;
}
erfaceIMetricDimensions{
floatLength;
floatWidth;
}
Box:IEnglishDimensions,IMetricDimensions{
floatlengthInches;
floatwidthInches;
publicBox(floatlength,floatwidth){
lengthInches=length;
widthInches=width;
}
floatIEnglishDimensions.Length{
lengthInches;
}
floatIEnglishDimensions.Width{
widthInches;
}
floatIMetricDimensions.Length{
lengthInches*2.54f;
}
floatIMetricDimensions.Width{
widthInches*2.54f;


}
publicvoidMain{
//定义个实类对象"myBox"::
BoxmyBox=Box(30.0f,20.0f);
//定义个接口"eDimensions"::
IEnglishDimensionseDimensions=(IEnglishDimensions)myBox;
IMetricDimensionsmDimensions=(IMetricDimensions)myBox;
//输出:
.Console.WriteLine("Length(in):{0}",eDimensions.Length);
.Console.WriteLine("Width(in):{0}",eDimensions.Width);
.Console.WriteLine("Length(cm):{0}",mDimensions.Length);
.Console.WriteLine("Width(cm):{0}",mDimensions.Width);
}
}

  输出:Length(in):30Width(in):20Length(cm):76.2Width(cm):50.8

  代码讨论:如果希望默认度量采用英制单位请正常实现Length和Width这两个思路方法并从IMetricDimensions接口显式实现Length和Width思路方法:

publicfloatLength{
lengthInches;
}
publicfloatWidth{
widthInches;
}
floatIMetricDimensions.Length{
lengthInches*2.54f;
}
floatIMetricDimensions.Width{
widthInches*2.54f;
}

  这种情况下可以从类例子访问英制单位而从接口例子访问公制单位:

.Console.WriteLine("Length(in):{0}",myBox.Length);
.Console.WriteLine("Width(in):{0}",myBox.Width);
.Console.WriteLine("Length(cm):{0}",mDimensions.Length);
.Console.WriteLine("Width(cm):{0}",mDimensions.Width);
2、继承接口实现

  接口具有不变性但这并不意味着接口不再发展类似于类继承性接口也可以继承和发展

  注意:接口继承和类继承区别首先类继承不仅是介绍说明继承而且也是实现继承;而接口继承只是介绍说明继承也就是说派生类可以继承基类思路方法实现而派生接口只继承了父接口成员思路方法介绍说明而没有继承父接口实现其次C#中类继承只允许单继承但是接口继承允许多继承个子接口可以有多个父接口

  接口可以从零或多个接口中继承从多个接口中继承时用":"后跟被继承接口名字多个接口名的间用","分割被继承接口应该是可以访问得到比如从private类型或ernal类型接口中继承就是不允许接口不允许直接或间接地从自身继承和类继承相似接口继承也形成接口的间层次结构

  请看下面例子:

using;
erfaceIControl{
voidPa;
}
erfaceITextBox:IControl{
voidSetText(text);
}
erfaceIListBox:IControl{
voidSetItems(items);
}
erfaceIComboBox:ITextBox,IListBox{}

  对个接口继承也就继承了接口所有成员上面例子中接口ITextBox和IListBox都从接口IControl中继承也就继承了接口IControlPa思路方法接口IComboBox从接口ITextBox和IListBox中继承因此它应该继承了接口ITextBoxSetText思路方法和IListBoxSetItems思路方法还有IControlPa思路方法
个类继承了所有被它基本类提供接口实现

  不通过显式实现个接口个派生类不能用任何思路方法改变它从它基本类继承接口映射例如在声明中

erfaceIControl{
voidPa;
}
Control:IControl{
publicvoidPa{...}
}
TextBox:Control{
publicvoidPa{...}
}

  TextBox中思路方法Pa隐藏了Control中思路方法Pa但是没有改变从Control.Pa到IControl.Pa映射而通过类例子和接口例子Pa将会有下面影响

Controlc=Control;
TextBoxt=TextBox;
IControlic=c;
IControlit=t;
c.Pa;//影响Control.Pa;
t.Pa;//影响TextBox.Pa;
ic.Pa;//影响Control.Pa;
it.Pa;//影响Control.Pa;

  但是个接口思路方法被映射到个类中虚拟思路方法派生类就不可能覆盖这个虚拟思路方法并且改变接口实现例如把上面声明重新写为



erfaceIControl{
voidPa;
}
Control:IControl{
publicvirtualvoidPa{...}
}
TextBox:Control{
publicoverridevoidPa{...}
}

  就会看到下面结果:

Controlc=Control;
TextBoxt=TextBox;
IControlic=c;
IControlit=t;
c.Pa;//影响Control.Pa;
t.Pa;//影响TextBox.Pa;
ic.Pa;//影响Control.Pa;
it.Pa;//影响TextBox.Pa;

  由于显式接口成员实现不能被声明为虚拟就不可能覆盖个显式接口成员实现个显式接口成员实现另外个思路方法是有效而另外那个思路方法可以被声明为虚拟以便让派生类可以覆盖它例如:

erfaceIControl{
 voidPa;
}
Control:IControl{
 voidIControl.Pa{PaControl;}
 protectedvirtualvoidPaControl{...}
}
TextBox:Control{
 protectedoverridevoidPaControl{...}
}

  这里从Control继承类可以通过覆盖思路方法PaControl来对IControl.Pa实现进行特殊化3、重新实现接口

  我们已经介绍过派生类可以对基类中已经定义成员思路方法进行重载类似概念引入到类对接口实现中来叫做接口重实现(re-implementation)继承了接口实现类可以对接口进行重实现这个接口要求是在类定义基类列表中出现过对接口重实现也必须严格地遵守首次实现接口规则派生接口映射不会对为接口重实现所建立接口映射产生任何影响

  下面代码给出了接口重实现例子:

erfaceIControl{
 voidPa;
 Control:IControl
 voidIControl.Pa{…}
 MyControl:Control,IControl
 publicvoidPa{}
}

  实际上就是:Control把IControl.Pa映射到了Control.IControl.Pa但这并不影响在MyControl中重实现在MyControl中重实现中IControl.Pa被映射到MyControl.Pa的上

  在接口重实现时继承而来公有成员定义和继承而来显式接口成员定义参和到接口映射过程

using;
erfaceIMethods{
 voidF;
 voidG;
 voidH;
 voidI;
}
Base:IMethods{
 voidIMethods.F{}
 voidIMethods.G{}
 publicvoidH{}
 publicvoidI{}
}
Derived:Base,IMethods{
 publicvoidF{}
 voidIMethods.H{}
}

  这里接口IMethods在Derived中实现把接口思路方法映射到了Derived.F,Base.IMethods.G,Derived.IMethods.H,还有Base.I前面我们说过类在实现个接口时同时隐式地实现了该接口所有父接口同样类在重实现个接口时同时隐式地重实现了该接口所有父接口

using;
erfaceIBase{
 voidF;
}
erfaceIDerived:IBase{
 voidG;
}
C:IDerived{
 voidIBase.F{
 //对F进行实现代码…
}
voidIDerived.G{
 //对G进行实现代码…
}
}
D:C,IDerived{
 publicvoidF{
 //对F进行实现代码…
}
publicvoidG{
 //对G进行实现代码…
}
}

  这里对IDerived重实现也同样实现了对IBase重实现把IBase.F映射到了D.F
4、映射接口

  类必须为在基类表中列出所有接口成员提供具体实现在类中定位接口成员实现称的为接口映射(erfacemapping)

  映射数学上表示对应关系接口映射含义也是接口通过类来实现那么对于在接口中定义个成员都应该对应着类个成员来为它提供具体实现



  类成员及其所映射接口成员的间必须满足下列条件:

  1、如果A和B都是成员思路方法那么A和B名称、类型、形参表(包括参数个数和每个参数类型)都应该是

  2、如果A和B都是属性那么A和B名称、类型应当而且A和B访问器也是类似但如果A不是显式接口成员执行体A允许增加自己访问器

  3、如果A和B都是时间那么A和B名称、类型应当

  4、如果A和B都是索引指示器那么A和B类型、形参表(包括参数个数和每个参数类型)应当而且A和B访问器也是类似但如果A不是显式接口成员执行体A允许增加自己访问器

  那么对于个接口成员怎样确定由哪个类成员来实现呢?即个接口成员映射是哪个类成员?在这里我们叙述下接口映射过程假设类C实现了个接口IInterfaceMember是接口IInterface中个成员在定位由谁来实现接口成员Member即Member映射过程是这样:

  1、如果C中存在着个显式接口成员执行体该执行体和接口IInterface及其成员Member相对应则由它来实现Member成员

  2、如果条件(1)不满足且C中存在着个非静态公有成员该成员和接口成员Member相对应则由它来实现Member成员

  3、如果上述条件仍不满足则在类C定义基类列表中寻找个C基类D用D来代替C

  4、重复步骤1--3遍历C所有直接基类和非直接基类直到找到个满足条件成员

  5、如果仍然没有找到则报告

  下面是基类思路方法来实现接口成员例子类Class2实现了接口Interface1类Class2基类Class1成员也参和了接口映射也就是说类Class2在对接口Interface1进行实现时使用了类Class1提供成员思路方法F来实现接口Interface1成员思路方法F:

erfaceInterface1{
 voidF;
}
Class1{
 publicvoidF{}
 publicvoidG{}
}
Class2:Class1,Interface1{
 publicvoidG{}
}

  注意:接口成员包括它自己定义成员而且包括该接口所有父接口定义成员在接口映射时不仅要对接口定义体中显式定义所有成员进行映射而且要对隐式地从父接口那里继承来所有接口成员进行映射
在进行接口映射时还要注意下面两点:

  1、在决定由类中哪个成员来实现接口成员时类中显式介绍说明接口成员比其它成员优先实现

  2、使用Private、protected和修饰符成员不能参和实现接口映射例如:

erfaceICloneable{
 objectClone;
}
C:ICloneable{
 objectICloneable.Clone{…}
 publicobjectClone{…}
}

  例子中成员ICloneable.Clone称为接口ICloneable成员Clone实现者它是显式介绍说明接口成员比其它成员有着更高优先权

  如果个类实现了两个或两个以上名字、类型和参数类型都相同接口那么类中个成员就可能实现所有这些接口成员:

erfaceIControl{
 voidPa;
}
erfaceIForm{
 voidPa;
}
Page:IControl,IForm{
 publicvoidPa{…}
}

  这里接口IControl和IForm思路方法Pa都映射到了类Page中Pa思路方法当然也可以分别用显式接口成员分别实现这两个思路方法:

erfaceIControl{
 voidPa;
}
erfaceIForm{
 voidPa;
}
Page:IControl,IForm{
 publicvoidIControl.Pa{
 //具体接口实现代码
}
publicvoidIForm.Pa{
 //具体接口实现代码
}
}

  上面两种写法都是正确但是如果接口成员在继承中覆盖了父接口成员那么对该接口成员实现就可能必须映射到显式接口成员执行体看下面例子:

erfaceIBase{
 P{get;}
}
erfaceIDerived:IBase{
 P;
}

  接口IDerived从接口IBase中继承这时接口IDerived成员思路方法覆盖了父接口成员思路方法这时存在着同名两个接口成员那么对这两个接口成员实现如果不采用显式接口成员执行体编译器将无法分辨接口映射所以如果某个类要实现接口IDerived在类中必须至少定义个显式接口成员执行体采用下面这些写法都是合理:

//:对两个接口成员都采用显式接口成员执行体来实现
lassC:IDerived{
 IBase.P
 get
 {//具体接口实现代码}


  IDerived.P{
  //具体接口实现代码}
 }
// 2:对Ibase接口成员采用显式接口成员执行体来实现
C:IDerived{
 IBase.P
 get{//具体接口实现代码}
  publicP{
  //具体接口实现代码}
 }
// 3:对IDerived接口成员采用显式接口成员执行体来实现
C:IDerived{
 publicP
 get{//具体接口实现代码}
 IDerived.P{
 //具体接口实现代码}
}

  另种情况是如果个类实现了多个接口这些接口又拥有同个父接口这个父接口只允许被实现

using;
erfaceIControl{
 voidPa;
 erfaceITextBox:IControl{
 voidSetText(text);
}
erfaceIListBox:IControl{
 voidSetItems(items);
}
ComboBox:IControl,ITextBox,IListBox{
 voidIControl.Pa{…}
 voidITextBox.SetText(text){…}
 voidIListBox.SetItems(items){…}
}

  上面例子中类ComboBox实现了 3个接口:IControlITextBox和IListBox如果认为ComboBox不仅实现了IControl接口而且在实现ITextBox和IListBox同时又分别实现了它们父接口IControl实际上对接口ITextBox和IListBox实现分享了对接口IControl实现

  我们对C#接口有了较全面认识基本掌握了怎样应用C#接口编程但事实上C#不仅仅应用于.NET平台它同样支持以前COM可以实现COM类到.NET类转换如C#API欲了解这方面知识请看下节-接口转换
Tags:  显式实现接口 不会实现接口成员 接口的实现 c语言接口与实现

延伸阅读

最新评论

发表评论