Override引起的疑问!

Hide1984 2009-08-12 10:29:09
 class OverloadAndOverride
{
public static void Main()
{
int a = 5;
Child child = new Child();
child.Call(a);
Console.ReadKey();
}
}

public class Base
{
public virtual void Call(int input)
{
Console.WriteLine("This is Base[int]::Call(input={0})",input);
}
}

public class Child : Base
{
public override void Call(int input)
{
Console.WriteLine("This is Child[int]::Call(input={0})", input);
}

public void Call(double input)
{
Console.WriteLine("This is Child[double]::Call(input={0})", input);
}
}


这段代码的输出是:
This is Child[double]::Call(input=5)

Main中的Call方法IL如下

IL_000c:  callvirt   instance void Demo1.Child::Call(float64)


如果和您的答案一样的话,那么请告诉我,为什么?

Child 中的两个Call是否是函数重载关系,如果是,为什么没有调用相匹配的方法?

当然,如果把这里的Override该成new,答案显而易见。




我个人觉得对静态多态(Overload)及动态多态(Override)理解的还是比较清楚的,早绑定晚绑定也很清晰,但是遇到这个问题我看不懂了。。。。

如果高手们有兴趣,也可以就此说说多态及其应用,请不吝赐教!!!
...全文
231 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
微创社(MCC) 2009-08-14
  • 打赏
  • 举报
回复
先给你看一个简单点的情况,
构造上的案例还要再研究一下,
案例比较难构造,也不太好描述.

最好的方法,就是上代码:

#define INT
#define DOUBLE

using System;
using System.Reflection;

public class B
{
public B()
{
Console.WriteLine("调用基类开始:ClassB!");
int val = 100;
this.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;
this.Func(val);
}

#if INTs
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 virtual 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;
this.Func(val);
}

#if INTs
public override 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 DOUBLE
public override void Func(double val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBBB-Func!-double");
}
#endif

public void Func(string val)
{
Console.WriteLine("Type is {0},{1}", this.GetType(), "ClassBBB-Func!-string");
}
}

class Test
{
static void Main()
{
BB bb = new BBB();
int val = 100;

Console.WriteLine("变量BB类型开始:ClassBB!");
bb.Func(val);

//Reflection
Type[] types=new Type[3];
types[0] = Type.GetType("B");
types[1] = Type.GetType("BB");
types[2] = Type.GetType("BBB");
for( int i = 0 ; i<3;i++)
{
Console.WriteLine("======={0}=====",types[i].ToString());
MemberInfo[] memberInfos = types[i].GetMembers();
foreach (MemberInfo memberInfo in memberInfos)
{
Console.WriteLine("{0} is a {1},Name is {2}", memberInfo, memberInfo.MemberType, memberInfo.MetadataToken);
}
}


Console.ReadKey();
}
}

//[0]重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。

//[1]非构造调用:派生类的原始方法相匹配的方法
//[2]非构造调用:派生类的原始方法相兼容的方法
//[3]非构造调用:基类的方法相匹配的方法
//[4]非构造调用:基类的方法相兼容的方法

//[5]构造调用:派生类的原始方法相匹配的方法
//[6]构造调用:派生类的原始方法相兼容的方法
//[7]构造调用:基类的方法相匹配的方法
//[8]构造调用:基类的方法相兼容的方法

//=======B=====
//Void Func(Int32) is a Method,Name is 12426544*A(B)
//Void Func(Int64) is a Method,Name is 12426552*(B)
//=======BB=====
//Void Func(Int64) is a Method,Name is 12426664+(B)
//Void Func(Double) is a Method,Name is 12426672*(BB)
//Void Func(Int32) is a Method,Name is 12426544A(B)
//=======BBB=====
//Void Func(Int64) is a Method,Name is 12426784+(B)
//Void Func(Double) is a Method,Name is 12426792+(BB)
//Void Func(Int32) is a Method,Name is 12426544A(B)


//=======B=====
//Void Func(Int32) is a Method,Name is 100663298A*
//Void Func(Int64) is a Method,Name is 100663299B*
//=======BB=====
//Void Func(Int64) is a Method,Name is 100663301B+
//Void Func(Double) is a Method,Name is 100663302C*
//Void Func(Int32) is a Method,Name is 100663298A
//=======BBB=====
//Void Func(Int64) is a Method,Name is 100663304B+
//Void Func(Double) is a Method,Name is 100663305C+
//Void Func(System.String) is a Method,Name is 100663306D*
//Void Func(Int32) is a Method,Name is 100663298A


案例说明:
主要是观察BBB的构造函数中的调用~~
fun(int32)在B中定义,BBB通过"自然继承"得到
fun(int64)在B中定义,BBB通过"重写"得到
fun(double)在BB中定义,BBB通过"重写"得到
最后通过比较,double在BB中原生,最接近BBB,
最后的结果是选择fun(double)

得出的结论:
[1]如果是多层次的继承,不单是比较本类和基类,还要看哪个更接近.
[2]同样需要考虑自然继承的情况

其它:
通过修改DEBUG,来调整其它的组合,
可以进一步验证我的最后结果,
在下面的贴子中发表.


新规则总结在这个贴子的:
http://topic.csdn.net/u/20090805/13/254df2c5-2356-438a-a7f5-7f9ce5a9b3b0.html?seed=683743527&r=59086770#r_59086770
34楼:对"方法选择"的一个归纳
Hide1984 2009-08-14
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 pcnetman888 的回复:]
引用 9 楼 hide1984 的回复:
重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调用与具有相同名称和兼容参数的重写方法匹配.

hdt一句话点醒梦中人那~~~


今天重新研究了一下,发现问题还没有结束,
某些特定情况下,上述情况并不符合.
[/Quote]

To: pcnetman888 能告诉我是什么特定的情况吗?
微创社(MCC) 2009-08-13
  • 打赏
  • 举报
回复
同意:
重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。
chenjunsheep 2009-08-13
  • 打赏
  • 举报
回复
学习了,帮顶
Hide1984 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 hdt 的回复:]
重写和方法选择
当在类中指定方法时,如果有多个方法与调用兼容(例如,存在两种同名的方法,并且其参数与传递的参数兼容),则 C# 编译器将选择最佳方法进行调用。下面的方法将是兼容的:

C#  复制代码
public class Derived : Base
{
    public override void DoWork(int param) { }
    public void DoWork(double param) { }
}



在 Derived 的一个实例中调用 DoWork 时,C# 编译器将首先尝试使该调用与最初在 Derived 上声明的 DoWork 版本兼容。重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调用与具有相同名称和兼容参数的重写方法匹配。例如:

C#  复制代码
int val = 5;
Derived d = new Derived();
d.DoWork(val);  // Calls DoWork(double).



由于变量 val 可以隐式转换为 double 类型,因此 C# 编译器将调用 DoWork(double),而不是 DoWork(int)。有两种方法可以避免此情况。首先,避免将新方法声明为与虚方法同名。其次,可以通过将 Derived 的实例强制转换为 Base 来使 C# 编译器搜索基类方法列表,从而使其调用虚方法。由于是虚方法,因此将调用 Derived 上的 DoWork(int) 的实现。例如:

C#  复制代码
((Base)d).DoWork(val);  // Calls DoWork(int) on Derived.



[/Quote]



重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调用与具有相同名称和兼容参数的重写方法匹配.


hdt一句话点醒梦中人那~~~
hecker728 2009-08-13
  • 打赏
  • 举报
回复
学习
微创社(MCC) 2009-08-13
  • 打赏
  • 举报
回复
重写和方法选择
ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_csref/html/88247d07-bd0d-49e9-a619-45ccbbfdf0c5.htm

这种机制,在C#中又称为“决断”
简单说是以“精确优先”的原则
[1]“本类”优先于“基类”;
[2]“精确”优先于“模糊”。

从别一个侧面反映出,
重载的情况下,
对象调用函数是从基类发起的,
而不是从本类发起的.

现在来总结一下,C#的重载是怎么工作的,主要分两大步.
注:从变量的类型开始出发,暂称为"操作类型",对象本身的类型称为"源类型"
[1]向下(基类)方向,找到第一个实现
"操作类型"本身己实现该方法,可重写转向上
"操作类型"向下找到第一个实现该方法的基类,如果该基类可被重写,转向上,否则就是该类型
"操作类型"向下找到不到实现该方法的基类,转异常
[2]向上(扩展)方向,直到发现隐藏
判断当前类型是否被重写,不可被重写,则为该类型
如果当前类型可被重写,向上查询,直到发现new|virtaul|new virtaul|"源类型",为该类型
微创社(MCC) 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 hide1984 的回复:]
重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调用与具有相同名称和兼容参数的重写方法匹配.

hdt一句话点醒梦中人那~~~
[/Quote]

今天重新研究了一下,发现问题还没有结束,
某些特定情况下,上述情况并不符合.
真相重于对错 2009-08-13
  • 打赏
  • 举报
回复
重写和方法选择
当在类中指定方法时,如果有多个方法与调用兼容(例如,存在两种同名的方法,并且其参数与传递的参数兼容),则 C# 编译器将选择最佳方法进行调用。下面的方法将是兼容的:

C# 复制代码
public class Derived : Base
{
public override void DoWork(int param) { }
public void DoWork(double param) { }
}



在 Derived 的一个实例中调用 DoWork 时,C# 编译器将首先尝试使该调用与最初在 Derived 上声明的 DoWork 版本兼容。重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调用与具有相同名称和兼容参数的重写方法匹配。例如:

C# 复制代码
int val = 5;
Derived d = new Derived();
d.DoWork(val); // Calls DoWork(double).



由于变量 val 可以隐式转换为 double 类型,因此 C# 编译器将调用 DoWork(double),而不是 DoWork(int)。有两种方法可以避免此情况。首先,避免将新方法声明为与虚方法同名。其次,可以通过将 Derived 的实例强制转换为 Base 来使 C# 编译器搜索基类方法列表,从而使其调用虚方法。由于是虚方法,因此将调用 Derived 上的 DoWork(int) 的实现。例如:

C# 复制代码
((Base)d).DoWork(val); // Calls DoWork(int) on Derived.


济南大飞哥 2009-08-13
  • 打赏
  • 举报
回复
我也是,占个位置
Hide1984 2009-08-12
  • 打赏
  • 举报
回复
看来晚上大家都睡觉了,明天再来看答案,呵呵~~~
希望得到个准确而清晰的解答~

z~z~~Z~~~Z~~~~
ztbasdf 2009-08-12
  • 打赏
  • 举报
回复
ddddddddddddddddddd
ws_hgo 2009-08-12
  • 打赏
  • 举报
回复
先看下
Hide1984 2009-08-12
  • 打赏
  • 举报
回复
沙发自己坐

110,533

社区成员

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

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

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