繁体   English   中英

c#中继承和接口的意外行为

[英]Unexpected behaviour of inheritance and interfaces in c#

今天,在c#中实现一些测试类时,我在c#中讨论了有关继承(和接口)的一些问题。 下面我有一些示例代码来说明我的问题。

interface ILuftfahrzeug
{
    void Starten();
}

class Flugzeug : ILuftfahrzeug
{
    public void Starten()
    {
        Console.WriteLine("Das Flugzeug startet, "+Dings());
    }

    protected string Dings()
    {
        return "Flugzeug Dings";
    }
}


class Motorflugzeug : Flugzeug, ILuftfahrzeug
{
    public new void Starten()
    {
        Console.WriteLine("Das Motorflugzeug startet, "+Dings());
    }

    protected new string Dings()
    {
        return "Motorflugzeug Dings";
    }
}

class InterfaceUndVererbung
{
    static void Main(string[] args)
    {
        //Motorflugzeug flg = new Motorflugzeug(); // case1: returns "Das Motorflugzeug startet, Motorflugzeug Dings"
        //Flugzeug flg = new Motorflugzeug(); // case2: returns "Das Flugzeug startet, Flugzeug Dings"
        ILuftfahrzeug flg = new Motorflugzeug(); // case3: returns "Das Motorflugzeug startet, Motorflugzeug Dings"
                    // if Motorflugzeug implements ILuftfahrzeug explicitly, 
                    // otherwise "Das Motorflugzeug startet, Motorflugzeug Dings"

        flg.Starten();
        Console.ReadLine();
    }
}

这些是我的问题:

  1. 使用Flugzeug flg = new Motorflugzeug()进行声明和初始化; (case2)我期望调用Motorflugzeug.Starten而不是Flugzeug.Starten(我很确定这是Java显示的行为)。 openbook.galileo说在那种情况下使用c#运行时类型是Flugzeug。 有什么理由吗? 对我来说,这种继承行为毫无意义。
  2. 与ILuftfahrzeug相同flg = new Motorflugzeug(); (case3) - 在这里我可以让Motorflugzeug明确地实现ILuftfahrzeug(就像它在示例代码中一样)。 但对我而言,这是多余的,因为Flugzeug已经实施了ILuftfahrzeug。
  3. 现在我要覆盖由Starten()调用的受保护方法Dings()。 如果我运行代码,因为它在示例中实现everthing工作正常。 但是如果在Motorflugzeug中没有实现Starten(),则将调用基类的Dings()而不是Motorflugzeug.Dings()。 我被告知Java也会显示这种行为。
    有什么模式可以解决这个问题吗? 否则我将不得不覆盖调用我实际打算覆盖的方法的每个方法(这里:Starten())(这里:Dings()),即使它与基类完全相同。

1:你重新宣布方法( new ); 如果你override它应该工作。 new破坏了任何多态行为。

2:你正在重新实现界面; 这确实称为最高的实施。 再次, override将解决这个问题。

class Flugzeug : ILuftfahrzeug {
    public virtual void Starten() {
        Console.WriteLine("Das Flugzeug startet, " + Dings());
    }    
    protected virtual string Dings() {
        return "Flugzeug Dings";
    }
}
class Motorflugzeug : Flugzeug {
    public override void Starten() {
        Console.WriteLine("Das Motorflugzeug startet, " + Dings());
    }    
    protected override string Dings() {
        return "Motorflugzeug Dings";
    }
}

只需确保声明您希望能够覆盖为虚拟的方法。 然后在继承的类上使用override关键字。

更新1:新关键字明确表示这样一个事实,即直接使用该类时,在继承的类上声明非虚方法只会隐藏基本方法。 无论何时使用基类,都无需隐藏。

新的keywork和override关键字执行两个非常不同的事情,并且您正在体验new的行为,从您的描述中我认为您希望使用override,因为这遵循通常预期的继承行为。 您需要在基类中声明方法/属性virtual并使用override而不是new。

啊,太慢了!

这是因为您使用“新”修饰符,请参阅MSDN

你应该使用“覆盖”

已实现的接口成员不是自动虚拟的。 由你决定虚拟,抽象等等。

您的程序没有按预期执行的原因是因为方法中的new关键字。

默认情况下,c#方法不是动态绑定的。 这意味着调用的方法在编译时确定。 您可以查看变量类型确定调用哪个方法而不是内存中的实际对象。

要获得所需的效果,您需要确保c3动态绑定方法。 你可以通过声明方法virtual和覆盖方法override来做事。

class Flugzeug : ILuftfahrzeug
{
    public virtual void Starten()
    {
        Console.WriteLine("Das Flugzeug startet, "+Dings());
    }

    protected virtual string Dings()
    {
        return "Flugzeug Dings";
    }
}


class Motorflugzeug : Flugzeug, ILuftfahrzeug
{
    public override void Starten()
    {
        Console.WriteLine("Das Motorflugzeug startet, "+Dings());
    }

    protected override string Dings()
    {
        return "Motorflugzeug Dings";
    }
}

通常,永远不要在方法上使用new 它几乎永远不会做你想要的。

我第二个关于虚拟关键字的弗雷迪里奥斯的观点。 除非你的基类使用virtual关键字声明一个方法,否则根本就没有多态行为。 无论您使用覆盖还是新建都无关紧要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM