为什么我们使用虚拟和覆盖?

时间:2011-10-24 12:26:51

标签: c#

为什么我们使用override和virtual如果它们在我们不使用override和virtual时会产生相同的效果?

示例1:

class BaseClass
{
    public virtual string call()
    {
        return "A";
    }
}

class DerivedClass : BaseClass
{
    public override string call()
    {
        return "B";
    }
}

输出:B

示例2:

class BaseClass
{
    public string call()
    {
        return "A";
    }
}

class DerivedClass : BaseClass
{
    public string call()
    {
        return "B";
    }
}

并且输出仍然相同:

输出:B

运行测试:

class Program
{
    static void Main(string[] args)
    {
        DerivedClass dc = new DerivedClass();
        Console.WriteLine(dc.call());
        Console.ReadKey();
    }
}

编译器是否在编译时自动添加虚拟和覆盖?

如果有人向我解释使用虚拟和覆盖的原因,我会很高兴。

4 个答案:

答案 0 :(得分:21)

(注意,我正在悄悄忽略编译错误)

现在做:

BaseClass obj = new DerivedClass();
Console.WriteLine(obj.call());

如果没有virtual,那么当A实际应该写DerivedClass时,会打印B。这是因为它只是调用BaseClass实现(因为obj被输入为BaseClass,并且没有定义多态)。

答案 1 :(得分:5)

Virtual和override是面向对象编程中继承的基本机制。 当您在C#或Java等语言中使用类时,这可能是最重要的事情。

http://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming

继承允许您重用代码,添加新字段,属性和方法或替换先前定义的类的方法和属性。

虚拟和覆盖允许您替换方法的内容,当我说替换时,我说替换。

我会给你一个很好的例子。

public class MyClassEnglish
{
    public virtual string SomethingToSay()
    {
        return "Hello!";
    }

    public void WriteToConsole()
    {
        Console.WriteLine(this.SomethingToSay());
    }
}

public class MyClassItalian :
    MyClassEnglish
{
    public override string SomethingToSay()
    {
        return "Ciao!";
    }
}

int main()
{
    MyClassItalian it = new MyClassItalian();

    it.WriteToConsole();
}

如果省略虚拟和覆盖,MyClassItalian将打印出“Hello!”而不是“Ciao!”。

在您的示例中,您显示了一种阴影技术,但编译器应该给您一个警告。 如果要隐藏基类中的方法,则应添加“new”关键字。 隐藏方法并不是最重要的!只是藏身。

我想到的一个可能用途是,例如,当您需要某种优化时,可以使用它。

public abstract class MySpecialListBase
{
    public int Count()
    {
        return this.GetCount();
    }

    protected abstract int GetCount();
}

public sealed class MySpecialArrayList : MySpecialListBase
{
    int count;

    public new int Count()
    {
        return this.count;
    }

    protected override int GetCount()
    {
        return this.count;
    }
}

现在... 您可以在所有代码中使用MySpecialListBase,当您调用Count()时,它将调用虚方法GetCount()。 但是如果你只使用MySpecialArrayList,它将调用非虚拟的优化Count()并且只返回一个字段,从而提高性能。

// This works with all kind of lists, but since it is a more general purpose method it will call the virtual method.    
public void MyMethod(MySpecialListBase list)
{
    Console.WriteLine(list.Count());
}

// This works only with MySpecialArrayList, and will use the optimized method.
public void MyMethod(MySpecialArrayList list)
{
    Console.WriteLine(list.Count());
}

答案 2 :(得分:0)

最好的例子我可以想到它在哪里有用的是当你创建自己的对象(类)时,你必须将该对象的列表添加到组合框中。

将对象添加到组合框时,您希望能够控制每个项目显示的文本。 Object.toString是一个虚方法。 http://msdn.microsoft.com/en-us/library/system.object.tostring.aspx因此您可以覆盖该方法并设置.toString以通过覆盖它来显示有关对象的正确信息。

public MyClass()
{
     private int ID;
     public override string ToString()
     {
         return "My Item:" + ID;
     }
}

答案 3 :(得分:0)

方法覆盖:

在父类中定义或实现虚方法,然后在后代类中替换它。

当您决定将方法声明为虚拟时,您将授予派生类以使用自己的实现扩展和覆盖该方法。您也可以让扩展方法调用父方法的代码。

在大多数OO语言中,您还可以选择隐藏父方法。当您使用相同的签名引入相同命名方法的新实现而不覆盖时,您将隐藏父方法。

C#覆盖 在C#中,您在父类中使用virtual关键字指定虚方法,并使用override关键字在后代类中扩展(或替换)它。

在descendant方法中使用base关键字来执行父方法中的代码,即base.SomeMethod()。

语法示例:

class Robot
{
  public virtual void Speak()
  {
  }
}

class Cyborg:Robot
{
   public override void Speak()
   {
   }
}

覆盖详细信息 您不能覆盖常规的非虚方法,也不能覆盖静态方法。 父方法的第一个版本必须是虚拟或抽象的。 您可以覆盖标记为virtual,abstract或override(已经被覆盖)的任何父方法。 方法必须具有相同的签名。 这些方法必须具有相同的可见性(相同的访问级别)。 使用base关键字引用父类,如base.SomeMethod()。 C#覆盖示例 以下代码段演示了如何使用virtual和override覆盖后代类中的父方法。

using System;

class Dog
{
   public virtual void Bark()
   {
       Console.WriteLine("RUFF!");
   }
}

class GermanShepard:Dog
{
    public override void Bark()
    {
       Console.WriteLine("Rrrrooouuff!!");
    }
}

class Chiuaua:Dog
{
    public override void Bark()
    {
         Console.WriteLine("ruff");
    }
}
class InclusionExample
{
     public static void Main()
     {
        Dog MyDog=new Dog();    
        MyDog=new GermanShepard();
        MyDog.Bark(); // prints Rrrrooouuff!!

        MyDog=new Chiuaua();
        MyDog.Bark(); // prints ruff;
    }
}

用新方法隐藏方法 使用new关键字引入父方法的新实现(这隐藏了父方法)。您可以在不使用new的情况下隐藏方法但是会收到编译器警告。使用new会抑制警告。

new和override修饰符具有不同的含义。 new修饰符创建具有相同名称,签名和可见性的新成员,并隐藏原始成员。 override修饰符扩展了继承成员的实现,并允许您实现基于继承的多态。

避免引入新成员:有时有明确的理由引入具有相同名称,签名和父方法可见性的新方法。在那些明显的案例中,引入新成员是一个强大的功能。但是,如果您没有明确的理由,那么通过将新方法命名为唯一且恰当的方法,避免引入新版本的方法。

class Robot : System.Object
{
  public void Speak()
  {
    MessageBox.Show("Robot says hi");
  }
}

class Cyborg : Robot
{
  new public void Speak()
  {
    MessageBox.Show("hi");
  }
}

调用基类版本 常见任务在OO中,首先执行父方法代码然后添加代码来扩展方法。使用base关键字引用父类,如base.SomeMethod()。

class Robot : System.Object
{
    public virtual void Speak()
    {
       MessageBox.Show("Robot says hi");
    }
}
class Cyborg : Robot
{
  public override void Speak()
  {
      base.Speak(); 
      MessageBox.Show("hi");
  }
}