【问题标题】:Why do we use virtual and override?为什么我们使用 virtual 和 override?
【发布时间】:2011-10-24 12:26:50
【问题描述】:

如果不使用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();
    }
}

编译器会在编译时自动添加 virtual 和 override 吗?

如果有人向我解释使用 virtual 和 override 的原因,我会很高兴。

【问题讨论】:

  • 如果你可以在调用 add 之前将derivedClass 转为基类,你会得到不同的结果:1 => 2 and 2 => 1
  • @CD.. 没有重复,关于“覆盖和虚拟”
  • 发布示例...编译后真的会好很多。
  • @VuralAcar:虽然问题略有不同,但原理是一样的。您只是误解了多态性和方法隐藏之间的区别。
  • 您的测试(显然)没有证明差异。将行更改为“BaseClass dc = new DerivedClass();”然后再试一次。现在有区别了吗?

标签: c#


【解决方案1】:

(注意,我悄悄地忽略了编译错误)

现在做:

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

没有virtual,这将打印A,而实际上DerivedClass应该写B。这是因为它只是简单地调用了BaseClass 实现(因为obj 被键入为BaseClass,并且没有定义多态性)。

【讨论】:

  • 这是一个很好的链接(可能更好,但这是我在谷歌搜索隐式隐藏时发现的第一个)geekswithblogs.net/BlackRabbitCoder/archive/2010/06/24/…
  • 打印一样,用override和virtual也没关系。我认为编译器会在编译时添加它。
  • @Vural 非常仔细地查看我的场景和你的场景之间的差异。现在将您的代码更改为BaseClass bc = new DerivedClass(); Console.WriteLine(bc.call());。强调:没有它不会在编译时自动添加;只是,您正在测试不同的东西。
  • @Vural 因为这是重要的时候。如果您不使用多态性,那么是的:它们将显示相同的内容。区别在于virtual 支持抽象和substitution。你问“我们为什么要使用 {these}”——我的意思是:支持这种情况。很多时候,我们不知道具体/最终类型——我们只知道基本类型。例如,winforms 中的 Control 或 ASP.NET 中的 Page
  • @TAHASULTANTEMURI 这将是多态性的整个基石。基本示例:您有一个类并且想要更改文本表示(覆盖ToString())或相等的工作方式(覆盖Equals()GetHashCode())。更复杂的示例:您正在实现一个自定义 ADO.NET 提供程序,因此您将 DbConnectionDbParameterDbDataReader 等子类化并覆盖各种方法以添加您的实现。
【解决方案2】:

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

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

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

Virtual 和 Override 允许您替换方法的内容,当我说替换时,我说替换。

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

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();
}

如果省略 virtual 和 override,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());
}

【讨论】:

    【解决方案3】:

    我能想到的最佳示例是当您创建自己的对象(类)并且您必须将该对象的列表添加到组合框时。

    当您将对象添加到组合框时,您希望能够控制为每个项目显示的文本。 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;
         }
    }
    

    【讨论】:

      【解决方案4】:

      方法覆盖:

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

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

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

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

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

      语法示例:

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

      覆盖细节 您不能覆盖常规的非虚拟方法,也不能覆盖静态方法。 父方法的第一个版本必须是虚拟的或抽象的。 您可以覆盖任何标记为虚拟、抽象或覆盖(已覆盖)的父方法。 这些方法必须具有相同的签名。 这些方法必须具有相同的可见性(相同的访问级别)。 使用 base 关键字来引用父类,如在 base.SomeMethod() 中。 C# 覆盖示例 以下代码 sn-p 演示了使用 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");
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2010-11-21
        • 2011-09-03
        • 2015-06-14
        • 1970-01-01
        • 1970-01-01
        • 2019-07-26
        相关资源
        最近更新 更多