RT:有例证更好~~为便于统一开展讨论,先给一个空壳
using System;namespace PCTestC
{
public class Class0
{ } public class Class1 : Class0
{ } public class Class2 : Class1
{ } public class Class3 : Class2
{ } public class Class4 : Class3
{ } public class Class5 : Class4
{ } public class Class6 : Class5
{ } public class Test
{
static void Main()
{
//Begin
Console.WriteLine("Begin!"); //End
Console.ReadKey();
}
}
}
using System;namespace PCTestC
{
public class Class0
{ } public class Class1 : Class0
{ } public class Class2 : Class1
{ } public class Class3 : Class2
{ } public class Class4 : Class3
{ } public class Class5 : Class4
{ } public class Class6 : Class5
{ } public class Test
{
static void Main()
{
//Begin
Console.WriteLine("Begin!"); //End
Console.ReadKey();
}
}
}
没有继承,跟本谈不上多态.先讲一些初级的大道理:(虽然代码胜于道理...看来不讲不行)
------------------------
多态定义:同一操作,作用于不同的对象,产生不同的结果。
多态分类:编译时的多态性,运行时的多态性。
多态实现:
编译时的多态性=>编译时己经确定,通过"重载"实现。(比较特殊的有静态调用时,以精确为标准的"判定"规则)
运行时的多态性=>系统运行时,根据实际情况决定实现何种操作,通过覆写虚成员实现。多态体现在继承上的,方法的修饰:
一般书本就提四大概念(略):重载,覆写,虚方法和抽象方法。(重载先不提)
我这里会分拆到"七种"情况:
--------------
[1]空:自然继承
[2]abstract void fun():抽象方法
[3]void fun():隐式重写
[4]new void fun():显式重写
[5]virtual void fun():虚方法
[6]new virtual void fun():重写的虚方法
[7]override覆写另外,可以研究一下C#与其它语方(C++/java)在多态实现上的差异
可以讨论一下多态性不仅对派生类很重要,对基类也很重要
也可以加以讨论多态性、继承直接影响了类型转换(显式和隐式的)
类型转换是一个更大的课题。我的观点是:(Yes/No)
多态、继承、转换互相之间是紧密联系着,想在深入理解C#这个语言,
这三者是重点也是难点(抛开语言层面之外的因素来说)我的问题本来就是开放性的,
有意参加讨论的,欢迎。
自认为水平很高的,就免了.大家都是出来混的,不容易,希望少一些废话,多一些实质的内容。
最好是通过code来表达...
[1]空:自然继承
[2]abstract void fun():抽象方法
[3]void fun():隐式重写
[4]new void fun():显式重写
[5]virtual void fun():虚方法
[6]new virtual void fun():重写的虚方法
[7]override覆写 这里你说的重写应叫做隐藏,你说的覆写才叫做重写。
override译作:重写|覆写|覆盖(以下统一为重写吧)使用中文就是麻烦,就像属性和特性,常量和不变量的区别重新组织一下:
[1]空 :自然继承
[2]abstract void fun() :抽象方法
[3][new] void fun() :显式和隐式的隐藏
[4][new] virtual void fun():虚方法//在作用于“根基”还是有一点差别的
[5]override void fun() :重写
封装(wrap)、继承(inheritance)、重载(override)都是为=》多态(Polymorphism)作准备
[2]多态实际上就是泛型
OO通过接口(Interface)和抽象类(Abstract Class)进行了真正意义上的泛型,C#实现了语法级的包装。
[3]多态是建立继承、重载这两者的基础之上的
多态与继承、重载并不是孤立的,他们之间存在着紧密的联系,
就继承而言是早期的,晚期绑定一定是多态。
[4]反射的本质也是多态
反射就是一种晚期绑定的技术,这个技术实质上表现出来的就是多态这一特征。
[5]接口的本质也是多态
因为需要抽象,接口采用所谓面向接口的方法,再利用反射实现。最后被充两个NEW的使用场景
[1]用于阶段性的版本控制,或者是用自己的版本替换(原因是不能改变原设计)外来的基类中的版本。
[2]用new+接口=>重新实现(Interface reimplementation)来纠正非预期的多态行为。
还是先出个题吧
--------------
interface IC1 { }
interface IC2 : IC1 { }
class A1 : IC1 { }
class A2 : IC1, IC2 { }
class B1 : A1 { }
class B2 : A1, IC1 { }
---------------
请问:
[1]A1与A2“等价”嘛
[2]B1与B2“等价”嘛有点写“茴”香豆的味道~~
[1]接口多态性。
[2]继承多态性。
[3]通过抽象类实现的多态性。 不过再我看来,接口与继承有重大区别,但在这里也可以变的不再重要,
用统一的观点来看待多态,比如15楼的问题。
{
public B()
{
Console.WriteLine("ClassB!");
this.Func();
} public B(string str)
{
Console.WriteLine("ClassB!-str:{0}", str);
} public virtual void Func()
{
Console.WriteLine("ClassB-Func!");
}
}public class BB : B
{
public BB()
{
Console.WriteLine("ClassBB!");
} public BB(string str)
{
Console.WriteLine("ClassBB!-str:{0}", str);
} public override void Func()
{
Console.WriteLine("ClassBB-Func!");
}
}public class BBB : BB
{
public BBB()
{
Console.WriteLine("ClassBBB!");
} public BBB(string str)
: base(str)
{
Console.WriteLine("ClassBBB!-str:{0}", str);
} public override void Func()
{
Console.WriteLine("ClassBBB-Func!");
}
}class Test
{
static void Main()
{
BBB aaa = new BBB("NEW");
Console.ReadKey();
}
}
[1]在没有用户定构造函数的前提下,类提供默认的构造函数(默认赋值的先不算)
这样理解:想在有无参构造函数的话,要么自己搞一个,要么一个都没有(带参和不带参).
否则会:提示“BBB”不包含采用“0”参数的构造函数[2]构造函数是逐级回溯的,最后从基类开始逐个实现.
我们看一下,显式的定义是这样的:
public BBB(string str): base(str)
我们看一下,隐式的定义是这样的:
public BBB(string str)
注意要点!!!:
他实现的是public BBB(string str):base()
而不是public BBB(string str):base(str)
还可以这样用法:
public BBB():this(str)[3]当构造函数访问内部函数时,他调用的不是该类所在的方法,而是对象运时所在的方法.
这个很重要,我以前也没注意到有这点~~
具体看这里:
http://topic.csdn.net/u/20090805/22/5e0c911d-eb09-41d9-8878-3d4762e102cf.html最后再归纳一下:
[1]"无参数构造函数"与"默认构造函数"不同
[2]"默认构造函数"仅当对象未提供任何一个"构造函数"(有参或无参)时,才被系统默认调用
[3]"默认构造函数"同样具有向基类进行回溯的特点
[4]"构造函数"一定会调用基类的构造
[5]"构造函数"调用基类构造时,可以"越过"中间类不存在构造,直达下个(至少有一个,或者默认的).
[6]"构造函数"可显式调用base(...)
[7]"构造函数"可显式调用与本构造参数不同的构造base(...)
[8]"构造函数"可显式调用this(...)
[9]"构造函数"在没有显式调用的情况下,隐式调用base()
[10]"构造函数"可引用内部方法(这时见上述案例),也可明确引用基类的方法.
代码://[1]abstract void fun():抽象方法
//[2]空:自然继承
//[3][new] void fun():显式和隐式的隐藏
//[4][new] virtual void fun():虚方法//在作用于“根基”还是有一点差别的
//[5]override void fun():重写
using System;namespace PCTestC
{
/// <summary>
/// 测试修饰符对多态的影响
/// </summary>
public class Class0
{
//virtual+自由
public virtual void Fun_0()
{
Console.WriteLine(this.GetType().Name + ":" + "Class0!");
} //virtual+override
public virtual void Fun_1()
{
Console.WriteLine(this.GetType().Name + ":" + "Class0!");
} //virtual+new
public virtual void Fun_2()
{
Console.WriteLine(this.GetType().Name + ":" + "Class0!");
} //virtual+继承
public virtual void Fun_3()
{
Console.WriteLine(this.GetType().Name + ":" + "Class0!");
} //virtual+virtual
public new virtual void Fun_4()
{
Console.WriteLine(this.GetType().Name + ":" + "Class0!");
} } public class Class1 : Class0
{
public new void Fun_0()
{
Console.WriteLine(this.GetType().Name + ":" + "Class1!");
} public override void Fun_1()
{
Console.WriteLine(this.GetType().Name + ":" + "Class1!");
} public new void Fun_2()
{
Console.WriteLine(this.GetType().Name + ":" + "Class1!");
} public new virtual void Fun_4()
{
Console.WriteLine(this.GetType().Name + ":" + "Class1!");
} } public class Class2 : Class1
{
public new void Fun_0()
{
Console.WriteLine(this.GetType().Name + ":" + "Class2!");
} public override void Fun_1()
{
Console.WriteLine(this.GetType().Name + ":" + "Class2!");
} public new void Fun_2()
{
Console.WriteLine(this.GetType().Name + ":" + "Class2!");
} public new virtual void Fun_4()
{
Console.WriteLine(this.GetType().Name + ":" + "Class2!");
} }
public class Class3 : Class2
{
//public override void Fun_0()
//{
// Console.WriteLine(this.GetType().Name + ":" + "Class3!");
//} public override void Fun_1()
{
Console.WriteLine(this.GetType().Name + ":" + "Class3!");
} public new void Fun_2()
{
Console.WriteLine(this.GetType().Name + ":" + "Class3!");
} public new virtual void Fun_4()
{
Console.WriteLine(this.GetType().Name + ":" + "Class3!");
}
} public class Class4 : Class3
{
public new void Fun_0()
{
Console.WriteLine(this.GetType().Name + ":" + "Class4!");
} public override void Fun_1()
{
Console.WriteLine(this.GetType().Name + ":" + "Class4!");
} public new void Fun_2()
{
Console.WriteLine(this.GetType().Name + ":" + "Class4!");
} public new virtual void Fun_4()
{
Console.WriteLine(this.GetType().Name + ":" + "Class4!");
} } public class Class5 : Class4
{
public new void Fun_0()
{
Console.WriteLine(this.GetType().Name + ":" + "Class5!");
} public override void Fun_1()
{
Console.WriteLine(this.GetType().Name + ":" + "Class5!");
} public new void Fun_2()
{
Console.WriteLine(this.GetType().Name + ":" + "Class5!");
} public new virtual void Fun_4()
{
Console.WriteLine(this.GetType().Name + ":" + "Class5!");
} } public class Class6 : Class5
{
public new void Fun_0()
{
Console.WriteLine(this.GetType().Name + ":" + "Class6!");
} public override void Fun_1()
{
Console.WriteLine(this.GetType().Name + ":" + "Class6!");
} public new void Fun_2()
{
Console.WriteLine(this.GetType().Name + ":" + "Class6!");
} public new virtual void Fun_4()
{
Console.WriteLine(this.GetType().Name + ":" + "Class6!");
} } public class Test
{
static void Main()
{
//Begin
Console.WriteLine("Begin!"); //实例化
Console.WriteLine("实例化");
Class0 a0 = new Class0();
Class1 a1 = new Class1();
Class2 a2 = new Class2();
Class3 a3 = new Class3();
Class4 a4 = new Class4();
Class5 a5 = new Class5();
Class6 a6 = new Class6(); //初始化
Console.WriteLine("初始化");
Class0 b0 ;
Class1 b1 ;
Class2 b2 ;
Class3 b3 ;
Class4 b4 ;
Class5 b5 ;
Class6 b6 ; //自由组合
Console.WriteLine("");
Console.WriteLine("自由组合");
Console.WriteLine("转换到Class0");
b0 = a1; b0.Fun_0();
b0 = a2; b0.Fun_0();
b0 = a3; b0.Fun_0();
b0 = a4; b0.Fun_0();
b0 = a5; b0.Fun_0();
b0 = a6; b0.Fun_0();
Console.WriteLine("转换到Class1");
b1 = a2; b1.Fun_0();
b1 = a3; b1.Fun_0();
b1 = a4; b1.Fun_0();
b1 = a5; b1.Fun_0();
b1 = a6; b1.Fun_0();
Console.WriteLine("转换到Class2");
b2 = a3; b2.Fun_0();
b2 = a4; b2.Fun_0();
b2 = a5; b2.Fun_0();
b2 = a6; b2.Fun_0();
Console.WriteLine("转换到Class3");
b3 = a4; b3.Fun_0();
b3 = a5; b3.Fun_0();
b3 = a6; b3.Fun_0();
Console.WriteLine("转换到Class4");
b4 = a5; b4.Fun_0();
b4 = a6; b4.Fun_0();
Console.WriteLine("转换到Class5");
b5 = a6; b5.Fun_0(); //重写
Console.WriteLine("");
Console.WriteLine("重写");
Console.WriteLine("转换到Class0");
b0 = a1; b0.Fun_1();
b0 = a2; b0.Fun_1();
b0 = a3; b0.Fun_1();
b0 = a4; b0.Fun_1();
b0 = a5; b0.Fun_1();
b0 = a6; b0.Fun_1();
Console.WriteLine("转换到Class1");
b1 = a2; b1.Fun_1();
b1 = a3; b1.Fun_1();
b1 = a4; b1.Fun_1();
b1 = a5; b1.Fun_1();
b1 = a6; b1.Fun_1();
Console.WriteLine("转换到Class2");
b2 = a3; b2.Fun_1();
b2 = a4; b2.Fun_1();
b2 = a5; b2.Fun_1();
b2 = a6; b2.Fun_1();
Console.WriteLine("转换到Class3");
b3 = a4; b3.Fun_1();
b3 = a5; b3.Fun_1();
b3 = a6; b3.Fun_1();
Console.WriteLine("转换到Class4");
b4 = a5; b4.Fun_1();
b4 = a6; b4.Fun_1();
Console.WriteLine("转换到Class5");
b5 = a6; b5.Fun_1(); //隐藏
Console.WriteLine("");
Console.WriteLine("隐藏");
Console.WriteLine("转换到Class0");
b0 = a1; b0.Fun_2();
b0 = a2; b0.Fun_2();
b0 = a3; b0.Fun_2();
b0 = a4; b0.Fun_2();
b0 = a5; b0.Fun_2();
b0 = a6; b0.Fun_2();
Console.WriteLine("转换到Class1");
b1 = a2; b1.Fun_2();
b1 = a3; b1.Fun_2();
b1 = a4; b1.Fun_2();
b1 = a5; b1.Fun_2();
b1 = a6; b1.Fun_2();
Console.WriteLine("转换到Class2");
b2 = a3; b2.Fun_2();
b2 = a4; b2.Fun_2();
b2 = a5; b2.Fun_2();
b2 = a6; b2.Fun_2();
Console.WriteLine("转换到Class3");
b3 = a4; b3.Fun_2();
b3 = a5; b3.Fun_2();
b3 = a6; b3.Fun_2();
Console.WriteLine("转换到Class4");
b4 = a5; b4.Fun_2();
b4 = a6; b4.Fun_2();
Console.WriteLine("转换到Class5");
b5 = a6; b5.Fun_2(); //自然继承
Console.WriteLine("");
Console.WriteLine("自然继承");
Console.WriteLine("转换到Class0");
b0 = a1; b0.Fun_3();
b0 = a2; b0.Fun_3();
b0 = a3; b0.Fun_3();
b0 = a4; b0.Fun_3();
b0 = a5; b0.Fun_3();
b0 = a6; b0.Fun_3();
Console.WriteLine("转换到Class1");
b1 = a2; b1.Fun_3();
b1 = a3; b1.Fun_3();
b1 = a4; b1.Fun_3();
b1 = a5; b1.Fun_3();
b1 = a6; b1.Fun_3();
Console.WriteLine("转换到Class2");
b2 = a3; b2.Fun_3();
b2 = a4; b2.Fun_3();
b2 = a5; b2.Fun_3();
b2 = a6; b2.Fun_3();
Console.WriteLine("转换到Class3");
b3 = a4; b3.Fun_3();
b3 = a5; b3.Fun_3();
b3 = a6; b3.Fun_3();
Console.WriteLine("转换到Class4");
b4 = a5; b4.Fun_3();
b4 = a6; b4.Fun_3();
Console.WriteLine("转换到Class5");
b5 = a6; b5.Fun_3(); //End
Console.ReadKey();
}
}
}
由于字节数不够,删除了虚继承的部分
更正一下b:倒数第二个红色标注,应上移一行.
说明一下:
代码区中的Fun_0()用来测试自由组合的部分
讲解一下规律,先从一个小小的案例开始://方法部分
Class1:virtual void Fun_0()
Class1:override void Fun_0()
Class2:new virtual void Fun_0()
Class3:
Class4:override void Fun_0()
Class5:override void Fun_0()
Class6:override void Fun_0()//定义部分
b3=a5
先想一想,会输出什么结果?
答案是:Class5
//步聚
[1]Class3调用Fun_0,没有该方法,能过"自然继承",转向第一个拥有该方法的类Class2
[2]Class2可被重载,向上查询Class3,没有,继续向上,转向类Class4
[3]Class4仍可被重载,续继向上查询Class5,Class5己经到顶了
[4]Class5的Fun_0方法被调用,输出Class5//再来一个
b2=a3
先想一想,会输出什么结果?
答案是:Class2
//步聚
[2]Class2可被重载,向上查询Class3,没有,并且己到顶
[3]回到Class2
[4]Class2的Fun_0方法被调用,输出Class2
b3=a5 改为b3=a5;b3.Fun_0();
b2=a3 改为b2=a3;b3.Fun_0();现在来总结一下,C#是怎么工作的,主要分两大步.
注:从变量的类型开始出发,暂称为"操作类型",对象本身的类型称为"源类型"
[1]向下(基类)方向,找到第一个实现
"操作类型"本身己实现该方法,可重写转向上
"操作类型"向下找到第一个实现该方法的基类,如果该基类可被重写,转向上,否则就是该类型
"操作类型"向下找到不到实现该方法的基类,转异常
[2]向上(扩展)方向,直到发现隐藏
判断当前类型是否被重写,不可被重写,则为该类型
如果当前类型可被重写,向上查询,直到发现new|virtaul|new virtaul|"源类型",为该类型
继续拌大脑壳,
今天又有新的发现:
sealed override
其实含义很好理解:本类还是override的,但是对子类鞋葑锁。
[1]sealed override跟 override区别:显然本类被葑锁了
[2]sealed override跟 new区别:显然本类还可以向下重写分别在原测试代码上演练一下:
在class5 上 的 Fun_0 加上不同的修饰
[1]override
自由组合
转换到Class0
Class1:Class1!
Class2:Class2!
Class3:Class3!
Class4:Class4!
Class5:Class5!
Class6:Class6!
[2]new
自由组合
转换到Class0
Class1:Class1!
Class2:Class2!
Class3:Class3!
Class4:Class4!
Class5:Class4!
Class6:Class4!
[3]sealed override
自由组合
转换到Class0
Class1:Class1!
Class2:Class2!
Class3:Class3!
Class4:Class4!
Class5:Class5!
Class6:Class5!其它的顺便提一下:
[1]字段不能是虚拟的,只有方法、属性、事件和索引器才可以是虚拟的。
[2]new 关键字用于在派生类中创建该方法、字段或属性的新定义。(注意:这里是含字段的)感觉还意尤未尽,欢迎补充,等类型的差不多了,下次开始研究接口。
通过继承,一个类可以用作多种类型,可以用作它:
[1]自己的类型
[2]任何基类型
[3]任何接口类型(前提是:实现接口时)
--------
上下作用
--------
[1]向上作用:abstract void fun():抽象方法
[2]向下作用:空:自然继承
--------
本类作用
--------
[1]能够被重写|能够重写基类:override void fun():重写
[2]能够被重写|不能重写基类:[new] virtual void fun():虚方法
[3]不能被重写|能够重写基类:sealed override:封闭重写
[4]不能被重写|不能重写基类:[new] void fun():隐藏基于类型的多态,基本上清楚了,
下一步开始研究基于接口的多态,这个复杂多鸟~~
long优先于double,一会我们还会用的到using System;class B
{
public void Func(long val)
{
Console.WriteLine("LONG");
} public void Func(double val)
{
Console.WriteLine("DOUBLE");
}
}
class Test
{
static void Main()
{
B a = new B();
int val = 100;
a.Func(val); Console.ReadKey();
}
}
下面是用来证明方法选择时,所判定的规则#define INTA
#define DOUBLEusing System;public class B
{
public B()
{
Console.WriteLine("调用基类开始:ClassB!");
int val = 100;
Func(val);
}#if INT
public virtual void Func(int val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassB-Func!-int");
}
#endif public virtual void Func(long val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassB-Func!-long");
}}public class BB : B
{
public BB()
{
Console.WriteLine("调用扩展类开始:ClassBB!");
int val = 100;
Func(val);
}#if INT
public override void Func(int val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBB-Func!-int");
}
#endif public override void Func(long val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBB-Func!-long");
}#if DOUBLE
public void Func(double val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBB-Func!-double");
}
#endif
}class Test
{
static void Main()
{
Console.WriteLine("======构造调用========");
BB aa = new BB();
B a;
int val = 100;
Console.WriteLine("======非构造调用========");
Console.WriteLine("变量B类型开始:ClassB!");
a = aa;
a.Func(val);
Console.WriteLine("变量BB类型开始:ClassBB!");
aa.Func(val); Console.ReadKey();
}
}//[0]重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。//[1]非构造调用:派生类的原始方法相匹配的方法
//[2]非构造调用:派生类的原始方法相兼容的方法
//[3]非构造调用:基类的方法相匹配的方法
//[4]非构造调用:基类的方法相兼容的方法//[5]构造调用:派生类的原始方法相匹配的方法
//[6]构造调用:派生类的原始方法相兼容的方法
//[7]构造调用:基类的方法相匹配的方法
//[8]构造调用:基类的方法相兼容的方法
最后用来证明:
//[0]重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。
//[1]方法是由变量的类型来决定,而不是由对象类型来决定的#define INT
#define DOUBLEusing System;public class B
{
public B()
{
Console.WriteLine("调用基类开始:ClassB!");
int val = 100;
Func(val);
}#if INT
public virtual void Func(int val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassB-Func!-int");
}
#endif public virtual void Func(long val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassB-Func!-long");
}}public class BB : B
{
public BB()
{
Console.WriteLine("调用扩展类开始:ClassBB!");
int val = 100;
Func(val);
}#if INT
public new void Func(int val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBB-Func!-int");
}
#endif public override void Func(long val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBB-Func!-long");
}#if DOUBLE
public void Func(double val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBB-Func!-double");
}
#endif
}public class BBB : BB
{
public BBB()
{
Console.WriteLine("调用扩展类开始:ClassBBB!");
int val = 100;
Func(val);
}#if INT
public new void Func(int val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBBB-Func!-int");
}
#endif public override void Func(long val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBBB-Func!-long");
}#if DOUBLEs
public void Func(double val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBBB-Func!-double");
}
#endif
}class Test
{
static void Main()
{
BB bb = new BBB();
int val = 100;
bb.Func(val); Console.ReadKey();
}
}
首先分为两个层次,纵向的横向的
纵向优先,仍有争议再进行横向的"决断"
[1]纵向
首先看本类有没有匹配的方法(包括兼容的方法)
其它看基类有没有实现
再看基类的基类,如此往复
[2]横向(在纵向完成的基础上)
在某个层次有匹配的方法就是本方法
如果有兼容方法,选择最"精确"的方法,比如int>long>double...方法所在位置的判定:
//[1]重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的"新实现"。(动态的)
//[2]自然继承的方法被视为是在基类上声明的方法的"原实现"。(静态的)"方法选择"的所在类型的起点
//[1]方法起点是由变量的类型来决定,而不是由对象类型来决定的
using System.Collections.Generic;interface ITest
{
void Test();
}class B:ITest
{
public virtual void Test()
{
Console.WriteLine("B:ITest!");
}
}class BB:B,ITest
{
private new void Test()
{
//base.Test();
Console.WriteLine("BB:override!");
}
}class BBB : BB
{
public new void Test()
{
//base.Test();
Console.WriteLine("BBB:override!");
}
}class TestA
{
static void Main(string[] args)
{
BBB aaa = new BBB();
ITest i = aaa;
i.Test(); Console.ReadKey();
}
}总结:
接口上的调用,取决于"离对象最近"的"己实现的接口"的并且是"公开"的方法.
上述案例中,
[1]i首先查询BB的关于ITest的实现
[2]BB的有ITest的实现,但是private的,
[3]继续向下查询B的ITest实现,
最后在ITest上实现(如果被override者参照类的规则,所以说:接口比类更复杂)
using System.Collections.Generic;interface IA
{
void Test();
}interface IB
{
void Test();
}class B:IA,IB
{
public virtual void Test()
{
Console.WriteLine("B:default!");
} void IA.Test()
{
Console.WriteLine("B:IA!");
} void IB.Test()
{
Console.WriteLine("B:IB!"); }
}class BB:B
{
private new void Test()
{
//base.Test();
Console.WriteLine("BB!");
}
}class BBB : BB
{
public new void Test()
{
//base.Test();
Console.WriteLine("BBB!");
}
}class TestA
{
static void Main(string[] args)
{
BBB aaa = new BBB();
//测试默认接口
B a = aaa;
a.Test(); //测试IA接口
IA ia = aaa;
//IA ia = aaa as IA
ia.Test(); Console.ReadKey();
}
}总结如下:
[1]关于修饰
显式接口,必须是公共的(public),但属性是隐式的,不用写,写了反而错.
显式接口,必须是没有修饰的,比如virtual...
隐式接口,必须不是私有的(private),但可以是其它.[2]关于接口的被实现
显式的接口和隐式的接口(而且只能是public)至少实现一个
相当于没有显式实现实,用隐式实现来代替,见[4]
比如在上面的案例中:
public virtual void Test()//可以
void IA.Test();void IB.Test()//可以
private virtual void Test()//不可以[3]关于实现
对象上的调用,一定是隐式接口实现.
接口上的调用,优先调用该接口的实现,如果不存在则调用隐式实现,见[4][4]优先级
显式实现,优先于隐式实现,
当显示实现不存在时,隐式可替代显式,
但显式不能替代隐式.
继续我们的接口之旅如果你没有头晕的话,可以继续的理加的晕下去
因为我现在要看的不但是接口的多态,还要叠加上类的多态!!
这里引用一个老贴子的经典问题
http://www.cnblogs.com/allenlooplee/archive/2004/11/19/64553.html先看代码:
using System;
using System.Collections.Generic;interface IC
{
void M();
}class A : IC
{
void IC.M()
{
Console.WriteLine("In class A");
}
}class B1 : A
{
//public void M()
//{
// Console.WriteLine("In class B1");
//} //void IC.M()
//{
// Console.WriteLine("In class A");
//}
}class B2 : A, IC
{
void IC.M()
{
Console.WriteLine("In class B2");
}
}class Program
{
static void Main()
{
List<IC> cs = new List<IC>();
cs.Add(new A());
cs.Add(new B1());
cs.Add(new B2()); foreach (IC c in cs)
c.M(); IC ic;
ic = new B2(); ic.M();
ic = new B1(); ic.M();
Console.ReadKey();
}
}编译通不过:“B1.IC.M()”: 包含类型不实现接口“IC”
我猜测这个错误提示好象写出错了,换成“B1.IC.M()”: 包含类型己实现接口“IC”
下面解说一下:
B1继承了A,A己经实现了显式M接口,
B1通过自然继承的法则,获得了A的显式接口M接口,所以不能重复定义了。
而:
ic = new B2(); ic.M();
ic = new B1(); ic.M();
用就近法则能解释的通,ic上的调用是接口上的调用,
在显式实现的情况下,在就近的情况下,先显式,后隐式(在这里的隐式是替代的显式,所以还得理解为显式)下面的例子,进一步证时,先接口,
即便是隐式的己经实现了,还是会实现通过继承得到的显式接口.
using System;
using System.Collections.Generic;interface IC
{
void M();
}class A : IC
{
void IC.M()
{
Console.WriteLine("In class A");
}
}class B1 : A
{
public void M()
{
Console.WriteLine("In class B1");
} //void IC.M()
//{
// Console.WriteLine("In class B1");
//}
}class B2 : A, IC
{
void IC.M()
{
Console.WriteLine("In class B2");
}
}class Program
{
static void Main()
{
IC ic = new B1(); ic.M(); Console.ReadKey();
}
}
38楼中关于[5]是错的,
//说明IB带入了IA,相当于IB=IB,IA
下面再给案例证实一下:
using System;interface IA
{
new void Test();
}interface IB : IA
{
new void Test();
}class B : IB,IA
{
void IA.Test()
{
Console.WriteLine("B:IA!");
} void IB.Test()
{
Console.WriteLine("B:IB!");
}
}class BB : B,IB
{
void IA.Test()
{
Console.WriteLine("BB:IA!");
} void IB.Test()
{
Console.WriteLine("BB:IB!");
}
}class TestA
{
static void Main(string[] args)
{
B b = new B();
//测试IA/IB接口
IA ia = b; ia.Test();
IB ib = b; ib.Test();//说明IB带入了IA,相当于IB=IB,IA BB bb = new BB();
//测试IA接口
IA iaa = bb;
iaa.Test(); Console.ReadKey();
}
}
结果是:iaa.Test()=BB:IA!
说明就近选择的是class BB : B,IB,这个隐式的实现了IA(由IB带来IA的实现)
更进一步可以看这个贴子:
http://www.cnblogs.com/allenlooplee/archive/2004/11/16/64394.html
有Eric Gunnerson(Eric是C# Compiler Team的成员)的证言IB:IA//IA与IB是有继承关系的接口
Class:IB与Class:IB,IA的效果是一样的
多看看"集合"的实现,里面全是这样写的....总结如下:
[1]类 的继承关系的多态,反映的是纵向关系,是"覆盖"的效果.
[2]接口的继承关系的多态,反映的是模向关系,是"叠加"的效果
[3]有接口的继承实际上对“方法”进行override.
[4]接口能看作是一个十分特殊的抽象类,区别在于接口强制了成员的实现
[5]接口-抽象类-具体类的实现方式,比如"集合"大量采用这种模式