【问题标题】:virtual keyword in c#C#中的虚拟关键字
【发布时间】:2012-12-01 04:01:31
【问题描述】:

我了解 Java 并且最近几天一直在学习 C#。现在我遇到了“virtual”关键字,正如this link 所建议的那样,它用于允许在子类中覆盖相应的方法、属性等。现在我认为即使不使用“virtual”关键字,我们也可以覆盖方法。那为什么需要呢?

【问题讨论】:

  • "现在我认为即使不使用 'virtual' 关键字,我们也可以覆盖方法。"你觉得呢?
  • @BoltClock 当然可以。基类中的abstract 和派生类中的override 就足够了。不需要virtual:P
  • 是的,virtual 的意思是“我正在提供一个实现,但你可以多态地覆盖它”,abstract 的意思是“我正在定义一些你必须为其提供实现的东西,它将以多态方式运行”。与阴影(通常被认为是次优的)不同,函数签名必须匹配。
  • @CodesInChaos:嗯,是的;)

标签: c# oop overriding virtual


【解决方案1】:

如果你真的想在子类中使用override 方法,你需要virtual 关键字。否则基础实现将被新实现隐藏,就像您使用 new 关键字声明它一样。

通过“覆盖”方法来隐藏方法而不声明基方法virtual 会使您没有多态性,这意味着:如果您将专用版本“转换”为“基”版本并调用方法,则始终是基方法将使用 classes 实现而不是覆盖的版本 - 这不是您所期望的。

例子:

class A
{
    public void Show() { Console.WriteLine("A"); }
}

class B : A
{
    public void Show() { Console.WriteLine("B"); }
}

A a = new A();
B b = new B();

a.Show(); // "A"
b.Show(); // "B"

A a1 = b;
a1.Show(); // "A"!!!

【讨论】:

    【解决方案2】:

    virtual 是一种定义方法具有默认实现的方式,但该实现可以在子类中被覆盖。除了使用 virtual 之外,您不能在不使用 new 关键字的情况下直接覆盖方法(这通常是不好的做法)。

    virtual 实现的一个很好的例子是ToString() 方法。 C# 中的每个对象都保证能够调用ToString(),因为每个对象都继承自基类System.Object,其中包含一个虚方法ToString()。然而,派生类可以覆盖它,并提供它们自己的实现,这可能对对象的用户更有用。

    更新:我最近写了一篇博文,深入探讨了这个主题。 Check it out here.

    【讨论】:

      【解决方案3】:

      现在我认为即使不使用“virtual”关键字,我们也可以覆盖方法。

      不,你不能。与 Java 不同,C# 中的成员默认是密封的,除非您使用 virtual 关键字标记它们,否则您无法覆盖它们。

      【讨论】:

        【解决方案4】:

        看看这个例子:

            void Main()
            {
                var o1 = new S();
                Console.WriteLine(((B)o1).m1());    
            }
        
            public class B
            {
                public virtual string m1() {
                    return "m1";
                }
            }
        
            public class S : B
            {
                override public string m1() {
                    return "overridden m1";
                }
            }
        

        在此示例中,子类S 被实例化并分配给对象变量o1。 在Console.WriteLine 语句的参数中,它被转换为基类B,然后调用方法m1。 因为我们在基类B 中使用了virtual,在子类S 中使用了override,我们得到了

        覆盖的 m1

        作为输出。如果您在子类Boverride 中的m1 的方法声明中删除virtual virtual,那么您将得到

        m1

        作为输出,这意味着强制转换还具有使用基类B中方法m1的原始声明的效果。

        注意如果您在子类S 中使用new 关键字,例如

                 new public string m1() {
                    return "overridden m1";
                }
        

        假设基类 B 中的 virtual 关键字不存在,您将获得输出

        m1

        也是。如果您不将其转换为B,则将使用新方法。这称为 shadowing(或 hiding)方法(基类的原始方法)。

        总结:

        • 要覆盖一个方法,如果你强制转换为基类也应该有效,在基类中使用virtual关键字,在子类中使用override

        • 如果您打算覆盖仅应在子类中处于活动状态的方法,请在子类方法声明中使用 new 关键字。正如您所看到的,没有它它也可以工作,但如果它存在则更好,这样每个人都知道这是该方法的新版本。

        【讨论】:

          【解决方案5】:

          如果派生类被实例化为基类,虚拟/覆盖将允许派生类覆盖基函数。 示例:

                class A
                {
                   public virtual void Show() { Console.WriteLine("A"); }
                }
          
                class B : A
                {
                   public override void Show() { Console.WriteLine("B"); }
                }
          
          
                   A a = new A();
                   B b = new B();
          
                   a.Show(); 
                   b.Show(); 
          
                   A a1 = b;
                   a1.Show();  // here is the test!!!
          
                   B b1 = new B();
                   b.Show();  
          
          With virtual / override :
          A
          B
          B
          B
          
          After removing virtual / override keywords (or simply removing override):
          A
          B
          A
          B
          

          【讨论】:

            猜你喜欢
            • 2013-08-08
            • 2018-04-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-21
            相关资源
            最近更新 更多