隐藏方法

时间:2017-04-30 17:50:39

标签: 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()
    {
        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 关键字而不是虚拟/覆盖,因此Banana中的 PeelMe 方法隐藏了Fruit中的那个。

我在解释为​​什么打电话时遇到了问题:

IPeelable banana = new Banana();    
banana.PeelMe();// returns I'm a fruit and I've been peeled

在运行时有一个隐式的upcast。

基于继承范例调用的方法

  • PeelMe上的虚拟/覆盖:Banana.PeelMe被称为
  • 冗余IPeelable继承:Banana.PeelMe被称为
  • 隐藏新的PeelMe方法:称为Fruit.PeelMe

Banana不是一个IPeelable并且每次都在Banana中调用PeelMe方法吗? 有人能解释一下幕后发生的事情吗?

2 个答案:

答案 0 :(得分:1)

您看到的行为是因为您隐藏方法而不是覆盖它。覆盖方法会保留相同的签名,但会更改实现。隐藏方法会创建全新的方法,而不会出现任何多态行为。

请考虑以下代码:

Banana banana = new Banana();
banana.EatMe();

Fruit bananaAsFruit = banana;
bananaAsFruit.EatMe();

正如您所料,这两次都会打印I'm a banana and I've been eaten。由于在EatMe中使用virtual关键字声明了Fruit并在Banana中覆盖了Banana,因此.NET使用对象类型来确定要调用的方法,无论保持对它的引用的变量。无论Banana banana = new Banana(); banana.PeelMe(); Fruit bananaAsFruit = banana; bananaAsFruit.PeelMe(); 对象如何存储,都会调用相同的方法。

现在考虑调用非虚拟方法的类似代码段。这输出是什么?

banana

尽管bananaAsFruitBanana都包含virtual相同实例,但这两个调用会产生不同的结果!那是因为没有Fruit.PeelMe关键字,Banana.PeelMeBanana方法之间没有联系 - 从编译器的角度来看,它们可能也有完全不同的名称。

IPeelable引用变量中存储Banana可能会产生两种效果之一,具体取决于两种类型之间的关系。

  • 如果Fruit继承自IPeelableIPeelable.PeelMe实施Banana.PeelMeFruitPeelMe之间就没有联系。 IPeelableBanana.PeelMe接口实现Fruit.PeelMe方法,并且(从编译器的角度来看)Banana方法与IPeelable完全不同。

  • 如果声明Banana.PeelMe实现IPeelable,那么Banana将成为IPeelable接口的实现方法。存储在I'm a banana参考中的EatMe将打印virtual

对于override方法,运行时只查看实际对象的类型以确定要调用的内容 - 指向它的引用类型没有区别!这就是Data := Clipboard Xl := ComObjCreate("Excel.Application") Xl := ComObjActive("Excel.Application") filepath = %A_WorkingDir%\new.xlsx Xl.Workbooks.Open(filepath) Xl.Visible := True Clipboard := Data ; tried these get COM error mentioned above ; Xl.Selection.PasteSpecial(-4163) Xl.Range("A:D").PasteSpecial(-4163) Xl.Selection.Range("A:D").PasteSpecial(-4163) Xl.Range("A:D").Selection.PasteSpecial(-4163) Xl.Selection.PasteSpecial(-4163).Range("A:D") Xl.Selection.Range("A:D") := XL.Selection.PasteSpecial(-4163) Xl.ActiveSheet.Selection.PasteSpecial(-4163) ; tried these no COM error mentioned above but does not paste ; ActiveSheet.PasteSpecial(-4163) Selection.PasteSpecial(-4163) ActiveSheet.Selection.PasteSpecial(-4163) Selection.ActiveSheet.PasteSpecial(-4163) ActiveSheet.Range("A:D").PasteSpecial(-4163) ActiveSheet.PasteSpecial(-4163).Range("A:D") ActiveSheet.Selection.PasteSpecial(-4163).Range("A:D") ActiveSheet.Selection.Range("A:D").PasteSpecial(-4163) ActiveSheet.Range("A:D").Selection.PasteSpecial(-4163) var number = 121; var numbArr = []; for(var x = 1; x <= 500; x++){ numbArr.push(x); } function shuffle(array) { var m = array.length, t, i; // While there remain elements to shuffle… while (m) { // Pick a remaining element… i = Math.floor(Math.random() * m--); // And swap it with the current element. t = array[m]; array[m] = array[i]; array[i] = t; } return array; } shuffle(numbArr); for(var x = 0; x < numbArr.length; x++){ if(number == numbArr[x]){ console.log("Found number at entry " + x); } } 与方法隐藏的不同之处。

答案 1 :(得分:0)

这是一个有趣的问题,我认为答案也很有趣。

解释您的问题

你问:

为什么输出

  

我是一个水果,我被剥了皮

而不是

  

我是香蕉,我已经去皮了

如果您调用以下代码

IPeelable banana = new Banana();    
banana.PeelMe();// returns I'm a fruit and I've been peeled

你有一个类Fruit和一个Banana类,但只有Fruit实现了IPeelable接口。因此,IPeelable将实例化Fruit类的对象。

基于此,您的问题与以下内容相同:

为什么输出

  

我是一个水果,我被剥了皮

如果您调用以下代码

Fruit banana = new Banana();    
banana.PeelMe();// returns I'm a fruit and I've been peeled

有了这个问题,还有另外一个问题:

  

背后会发生什么?

我可以给出以下答案。

<强>答案

  

背后会发生什么?

如果你致电Fruit banana = new Banana();,它将被分配给banana的内存,其中包含两种方法。方法PeelMe()来自Fruit和Banana。如果香蕉的类型是Fruit,PeelMe()使用来自Fruit的PeelMe()的引用。如果香蕉的类型是Banana,PeelMe()使用来自Banana的PeelMe()的引用。

测试,验证我的答案

  • 如果向Banana类添加属性(整数)并查看分配的内存,结果如下: Integer property added to the Banana class

  • 如果向两个类添加相同的属性(整数)(使用虚拟和覆盖)并查看分配的内存,结果如下: Integer property added to both classes

将两个图像放在顶部,你可以看到,你从Fruit的属性和Banana的属性中为整数变量分配内存。

如果您想测试它,您还可以执行以下操作:

 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
}