C# 隐藏方法时的接口继承传播
如果我有一个接口C# 隐藏方法时的接口继承传播,c#,inheritance,interface,C#,Inheritance,Interface,如果我有一个接口 public interface IPeelable { void PeelMe(); } 以及以下继承树: public class Fruit : IPeelable { public virtual void EatMe() { Console.WriteLine("I'm a fruit and I've been eaten"); } public void PeelMe() { Co
public interface IPeelable
{
void PeelMe();
}
以及以下继承树:
public class Fruit : IPeelable
{
public virtual void EatMe()
{
Console.WriteLine("I'm a fruit and I've been eaten");
}
public void PeelMe()
{
Console.WriteLine("I'm a fruit and I've been peeled");
}
}
public class Banana : Fruit
{
public override void EatMe()
{
Console.WriteLine("I'm a banana and I've been eaten");
}
public new void PeelMe()
{
Console.WriteLine("I'm a banana and I've been peeled");
}
}
我故意使用了new关键字而不是virtual/override,因此香蕉中的PeelMe方法将其隐藏在水果中
我无法理解为什么我打电话时:
IPeelable banana = new Banana();
banana.PeelMe();// returns I'm a fruit and I've been peeled
在运行时存在一个隐式向上投射
基于继承范例调用的方法:
- PeelMe上的虚拟/覆盖:调用Banana.PeelMe
- 冗余的iPelable继承:称为Banana.PeelMe
- 使用new:Fruit.PeelMe隐藏的PeelMe方法称为
有人能解释一下幕后发生了什么吗?您看到的行为是因为您隐藏了该方法,而不是覆盖它。重写方法会保留相同的签名,但会更改实现。隐藏方法会创建一个没有任何多态行为的全新方法 考虑以下代码:
Banana banana = new Banana();
banana.EatMe();
Fruit bananaAsFruit = banana;
bananaAsFruit.EatMe();
正如你所料,这张照片显示了我是一个香蕉,两次都被吃掉了。由于EatMe
在Fruit
中使用virtual
关键字声明,并在Banana
中重写,因此.NET使用对象类型来确定要调用的方法,而不考虑维护对该方法引用的变量。无论如何存储Banana
对象,都会调用相同的方法
现在考虑一个类似的片段,调用非虚方法。这个输出是什么
Banana banana = new Banana();
banana.PeelMe();
Fruit bananaAsFruit = banana;
bananaAsFruit.PeelMe();
尽管banana
和bananaAsFruit
都包含一个banana
的相同实例,但这两个调用产生的结果不同!这是因为如果没有virtual
关键字,Fruit.PeelMe
和Banana.PeelMe
方法之间就没有联系——从编译器的角度看,它们的名称可能完全不同
根据两种类型之间的关系,将香蕉
存储在iElable
参考变量中可能会产生两种效果之一
- 如果
继承自Banana
,而Fruit又实现了Fruit
,则ipelable
和ipelable.PeelMe
之间没有联系Banana.PeelMe
为Fruit
接口实现iPelable
方法,并且(从编译器的角度来看),PeelMe
方法与Banana.PeelMe
完全不同Fruit.PeelMe
- 如果
声明实现Banana
,则ipelable
将成为Banana.PeelMe
接口的实现方法。存储在ipelable
引用中的ipelable
将打印Banana
I'm abana
对于
EatMe
方法,运行时只查看实际对象的类型,以确定调用什么-指向它的引用类型没有区别!这就是virtual
和override
与方法隐藏的不同之处。这是一个有趣的问题,我认为答案也很有趣
对您问题的解释 你问: 为什么是输出 我是一个水果,我被剥皮了 而不是 我是一个香蕉,我被剥皮了 如果调用以下代码
IPeelable banana = new Banana();
banana.PeelMe();// returns I'm a fruit and I've been peeled
Fruit banana = new Banana();
banana.PeelMe();// returns I'm a fruit and I've been peeled
您有一个类Fruit和一个类Banana,但只有Fruit实现接口ipelable。因此,iPelable将实例化类Fruit的对象
基于此,您的问题与以下内容相同:
为什么是输出
我是一个水果,我被剥皮了
如果调用以下代码
IPeelable banana = new Banana();
banana.PeelMe();// returns I'm a fruit and I've been peeled
Fruit banana = new Banana();
banana.PeelMe();// returns I'm a fruit and I've been peeled
带着这个问题和另外一个问题:
后面发生了什么
我可以告诉你以下答案
回答 后面发生了什么 如果您调用
水果香蕉=新香蕉()代码>将为包含这两种方法的香蕉分配内存。从水果和香蕉中提取PeelMe()的方法。如果香蕉的类型是水果,那么PeelMe()使用对来自水果的PeelMe()的引用。如果香蕉的类型是香蕉,则PeelMe()使用对香蕉中PeelMe()的引用
测试,验证我的答案
- 如果将属性(整数)添加到类中并查看分配的内存,则结果如下:
- 如果将相同的属性(integer)添加到两个类(使用virtual和override)并查看分配的内存,则结果如下:
在顶部的两个图像中,您可以看到,您为来自水果属性和香蕉属性的整数变量分配了内存
如果要测试它,还可以执行以下操作:
public class Fruit : IPeelable
{
public int NumberOfBananas = 10;
public virtual void EatMe()
{
Console.WriteLine("I'm a fruit and I've been eaten");
}
public void PeelMe()
{
Console.WriteLine("I'm a fruit and I've been peeled");
}
}
public class Banana : Fruit
{
public new int NumberOfBananas = 5;
public override void EatMe()
{
Console.WriteLine("I'm a banana and I've been eaten");
}
public new void PeelMe()
{
Console.WriteLine("I'm a banana and I've been peeled");
}
}
private static void Main()
{
Fruit banana = new Banana();
Console.WriteLine(banana.NumberOfBananas); // Result: 10
Banana realBanana = (Banana)banana;
Console.WriteLine(realBanana.NumberOfBananas); // Result: 5
Fruit bananaFruit = (Fruit)realBanana;
Console.WriteLine(bananaFruit.NumberOfBananas); // Result: 10
}
请显示变量的定义和赋值banana
它可以工作!我添加了声明,它不见了。这是重点。谢谢你,斯科特。@SeanStayn,你到donetfiddle的链接非常有用,我不知道这个在线工具。我已经深入研究了IL代码,香蕉类中的PeelMe方法是非虚拟的,但名称相同。方法public hidebysig instance void PeelMe()cil managed{在IL中对PeelMe的调用是:callvirt实例void Program/ipelable::PeelMe(),因为banana中的方法PeelMe是非虚拟的,它正在解析为Fruit.PeelMe()