关于"继承"与"多态"的关系,我们到底理解了有多少.

微创社(MCC) 2009-08-05 01:22:13
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();
}
}
}
...全文
941 44 打赏 收藏 转发到动态 举报
写回复
用AI写文章
44 条回复
切换为时间正序
请发表友善的回复…
发表回复
duanzhi1984 2009-09-02
  • 打赏
  • 举报
回复
顶下
微创社(MCC) 2009-08-30
  • 打赏
  • 举报
回复
最后留个问题,思考一下.

using System;
using System.Collections.Generic;

interface IC
{
void M(int i);
//void M(double i);
}

class A : IC
{
void IC.M(int i)
{
Console.WriteLine("In class A");
}
}

class B1 : A
{
//void IC.M(double i)
//{
// Console.WriteLine("In class A");
//}
}

class B2:B1
{
//void IC.M()
//{
// Console.WriteLine("In class A");
//}
}

class Program
{
static void Main()
{
IC ic;
ic = new B2(); ic.M(100);
ic = new B1(); ic.M(100);

Console.ReadKey();
}
}


在这里,B1实现的显式接口M,
实际上来自于A1实现的显式接口M的override

如果不去查看IL,有什么办法可以证明的嘛?
暂明没有想到好的办法.
因为在显式接口上,是不能带任何修饰的,
这样就没什么好的手段了,

你有办法嘛?

满三了,请帮顶一下....
微创社(MCC) 2009-08-29
  • 打赏
  • 举报
回复
先更正一下:
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]接口-抽象类-具体类的实现方式,比如"集合"大量采用这种模式
微创社(MCC) 2009-08-29
  • 打赏
  • 举报
回复
[Quote=引用 39 楼 yzx_224 的回复:]
明天再仔细拜读,顶一个先……
[/Quote]
多谢楼上的。。。
继续我们的接口之旅。。。

如果你没有头晕的话,可以继续的理加的晕下去。。。
因为我现在要看的不但是接口的多态,还要叠加上类的多态!!
这里引用一个老贴子的经典问题
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();
}
}

yzx_224 2009-08-29
  • 打赏
  • 举报
回复
明天再仔细拜读,顶一个先……
微创社(MCC) 2009-08-29
  • 打赏
  • 举报
回复
对上面再作一个补充:
[X]可以只实现IA和IB,并且不实现默认的.
编译能通过的前提是:没有出现对象上的调用,而只有接口上的调用.

对上面的[1]关于修饰再加一个定语:
针对接口的实现部分.(针对接口的定义,是可以加上NEW修饰的)
-------------------------------------------------------

关于接口的继承:

using System;

interface IA
{
new void Test();
new void TestNew();
}

interface IB:IA
{
new void Test();
}

class B:IB
{
//public virtual void Test()
//{
// Console.WriteLine("B:default!");
//}

public virtual void TestNew()
{
Console.WriteLine("B:default-NEW!");
}

void IA.Test()
{
Console.WriteLine("B:IA!");
}

void IB.Test()
{
Console.WriteLine("B:IB!");
}

//void IA.TestNew()
//{
// Console.WriteLine("B:IA-NEW!");
//}

//显式接口声明中的“IB.TestNew”不是接口成员
//void IB.TestNew()
//{
// Console.WriteLine("B:IB-NEW!");
//}
}

class TestA
{
static void Main(string[] args)
{
B a = new B();
//测试默认接口
//a.Test();
//a.TestNew();

//测试IA接口
IA ia = a;
ia.Test();
ia.TestNew();

//测试IA接口
IB ib = a;
ib.Test();
ib.TestNew();

Console.ReadKey();
}
}


总结如下:
[0]接口的声明,可以用NEW修饰(如果没有修饰等同于NEW,区别在于没有警告)
[1]对继承来的接口必须加以实现,可以是显式的,否则用隐式的来替代.
[2]继承来的接口必须在基接口上实现(IA.TestNew,如未实现//“B”不实现接口成员“IA.TestNew()”
[3]继承来的接口不能在派生接口上实现(如实现IA.TestNew//显式接口声明中的“IB.TestNew”不是接口成员)
[4]如果派生接口覆盖了基接口(IB.Test覆盖了IA.Test),则必须同时实现这两个接口.IB.Test的实现不能替代IA.Test的实现().虽然方法名相同,应该看作两个不同的方法,但是可以同时被同名的隐式实现方法给替代.
[5]B:IA,IB区别于B:IB,后者不能转为IA接口.

---------------------------------------------------
最后留一下问题:
以下这两组定义,是否完全相同?
interface IA
{
new void Test();
}
interface IB:IA
{
new void Test();
}


interface IA
{
new void Test();
}
interface IB
{
new void Test();
}



[size=48px]满三帮顶[/size]
微创社(MCC) 2009-08-29
  • 打赏
  • 举报
回复
接口的显式实现VS隐式实现

using System;
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]优先级
显式实现,优先于隐式实现,
当显示实现不存在时,隐式可替代显式,
但显式不能替代隐式.
微创社(MCC) 2009-08-29
  • 打赏
  • 举报
回复
类/接口(组合)方法/数据

关于接口:(这个比类更麻烦,先来点小的...)

using System;
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者参照类的规则,所以说:接口比类更复杂)
lizheng19860824 2009-08-15
  • 打赏
  • 举报
回复
NB
微创社(MCC) 2009-08-14
  • 打赏
  • 举报
回复
看来还没完,如果把重载(方法选择)混入继函,变得更加的复杂

开胃菜,说明一下重载是有优先级的
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 DOUBLE

using 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 DOUBLE

using 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();
}
}
codelabs 2009-08-14
  • 打赏
  • 举报
回复
如果你是主席的话你要了解多态,如果你是地方官员的话你要了解继承
微创社(MCC) 2009-08-14
  • 打赏
  • 举报
回复
重新归纳一下"方法选择"
首先分为两个层次,纵向的横向的
纵向优先,仍有争议再进行横向的"决断"
[1]纵向
首先看本类有没有匹配的方法(包括兼容的方法)
其它看基类有没有实现
再看基类的基类,如此往复
[2]横向(在纵向完成的基础上)
在某个层次有匹配的方法就是本方法
如果有兼容方法,选择最"精确"的方法,比如int>long>double...

方法所在位置的判定:
//[1]重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的"新实现"。(动态的)
//[2]自然继承的方法被视为是在基类上声明的方法的"原实现"。(静态的)

"方法选择"的所在类型的起点
//[1]方法起点是由变量的类型来决定,而不是由对象类型来决定的
微创社(MCC) 2009-08-11
  • 打赏
  • 举报
回复
重新总结一下:
--------
上下作用
--------
[1]向上作用:abstract void fun():抽象方法
[2]向下作用:空:自然继承
--------
本类作用
--------
[1]能够被重写|能够重写基类:override void fun():重写
[2]能够被重写|不能重写基类:[new] virtual void fun():虚方法
[3]不能被重写|能够重写基类:sealed override:封闭重写
[4]不能被重写|不能重写基类:[new] void fun():隐藏

基于类型的多态,基本上清楚了,
下一步开始研究基于接口的多态,这个复杂多鸟~~
微创社(MCC) 2009-08-11
  • 打赏
  • 举报
回复
多谢两位的,否则我还跟不了贴。
继续拌大脑壳,
今天又有新的发现:
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]任何接口类型(前提是:实现接口时)
卧_槽 2009-08-11
  • 打赏
  • 举报
回复
楼主拌大脑壳。
dming4 2009-08-11
  • 打赏
  • 举报
回复
方法的重载,重写 属于典型的多态多态
soaringbird 2009-08-06
  • 打赏
  • 举报
回复
15楼的问题,首先得说清“等价”的含义是什么。因为既然有不同的标识符,他们就不可能是等价的。
lovvver 2009-08-06
  • 打赏
  • 举报
回复
继承偏少,聚合较多。但如果面向对象开发的话,多态肯定还是时时刻刻少不了的,否则你怎么降低耦合度。
zzxap 2009-08-06
  • 打赏
  • 举报
回复
项目中真的很少用到继承与多态
理查德 2009-08-06
  • 打赏
  • 举报
回复
关注
加载更多回复(23)

110,545

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧