【问题标题】:issue of virtual method in C#C#中的虚方法问题
【发布时间】:2010-10-11 00:24:05
【问题描述】:

在MSDN中有提到,

http://msdn.microsoft.com/en-us/library/9fkccyh4(VS.80).aspx

我很困惑这个项目是什么意思“通过包含使用覆盖修饰符的属性声明,可以在派生类中覆盖虚拟继承的属性。”?

(这是虚拟和抽象之间的第二个区别)

提前致谢, 乔治

【问题讨论】:

    标签: c# oop virtual abstract


    【解决方案1】:

    你能解释一下它有什么令人困惑的地方吗?属性可以像任何其他方法一样被覆盖。

    public class Base {
      public virtual int Prop1 { get { ... } set { ... } }
    }
    
    public class Derived : Base {
      public override int Prop1 { get { ... } set { ... } }
    

    【讨论】:

    • 我的困惑不是被覆盖的属性,而是为什么它是抽象和虚拟之间的区别——“”通过包含使用覆盖修饰符的属性声明,可以在派生类中覆盖虚拟继承的属性."?请参阅我引用的 MSDN 的上下文。
    • 抽象成员没有实现,因此必须被覆盖。虚拟成员有一个实现,并且可以被覆盖。这就是区别。
    • 明白了,MSDN的话太模糊了。 :-)
    • 我不认为这就是文档的意思,或者如果确实如此,那么这是一个非常糟糕的词选择。不过,这是最好的解释。作家可能刚刚过了鲍尔默的巅峰。耶 MSDN!
    【解决方案2】:

    如果您在基类中声明一个虚拟方法,您可以在派生类中覆盖它。

    例子

    class MyBaseClass
    {
       public virtual void MyOverridableMethod()
       {
              ...
       }
    
    }
    
    class MyDerivedClass : MyBaseClass
    {
       public override void MyOverridableMethod()
       {
             ...
       }
    }
    

    注意 MyDerivedClass 中的 override 修饰符。

    【讨论】:

    • 我的困惑是引用的词中抽象和虚拟之间的区别是什么——“虚拟继承属性可以通过包含使用覆盖修饰符的属性声明在派生类中被覆盖”?跨度>
    【解决方案3】:

    virtual 和 abstract 之间的唯一区别是抽象方法或属性在定义它的类(抽象类)中没有实现,并且它必须在子类;而虚拟方法或属性在其定义的类中具有实现,因此不必在子类中覆盖它。

    public abstract AbstractClass
    {
        // This class cannot be instantiated, since it is 
        // abstract, and the class is abstract because
        // it has an abstract member
    
        public abstract MyProperty {get; set; }
    }
    

    在从 AbstractClass 派生的类中(上面的 AbstractClass 仅用于说明目的;由于它没有具有实现的方法/属性,因此您可以创建接口而不是抽象类),您必须提供MyProperty 的实现。否则,它不会编译。 您通过“覆盖” MyProperty 来做到这一点,您不想引入新成员,而只是为之前定义的属性提供实现。

    public class ConcreteClass : AbstractClass
    {
        public override MyProperty {
           get
           {
                return _someValue;
           }
           set
           {
                if( _someValue != value ) _someValue = value;
           }
    }
    

    【讨论】:

    • 那么,你认为 MSDN 的意思是,对于抽象基类,它是“必须”,但对于非抽象基类,它是“可以”(不是必须)?
    • 这绝对是必须的;尝试从抽象类继承,不要重写抽象方法或属性;编译器会抱怨。
    【解决方案4】:

    好的,假设您有一个基类,而该基类本身是从另一个类派生的。

    public class Bar : Foo
    {
       virtual public int SomeProperty { get; set; }
    }
    

    virtual 关键字的意思是,在从 Bar 派生的类中,您可以重写 SomeProperty 以改变其行为:

    public class Baz : Bar
    {
       private int thisInt;
       override public int SomeProperty 
       {
          get { return thisInt; }
          set 
          {
             if(value < 0)
             {
                throw new ArgumentException("Value must be greater than or equal to zero.");
             }
             thisInt = 0;
          }
       }
    }
    

    说明:当使用 Baz 类型的对象时,会调用其 SomeProperty 版本,除非该类型被强制转换为 Bar。如果您将 Baz 的 SomeProperty 定义为虚拟,则从 Baz 派生的类也可以覆盖它(实际上,这可能是必需的——我想不起来了)。

    进一步说明:抽象方法没有实现;当你向你的类添加一个时,你还必须将该类标记为抽象类,并且你不能实例化它的新实例,如下所示:

    MyAbstractType m = new MyAbstractType();
    

    另一方面,虚拟成员可以有一个实现(如上面的 SomeProperty),因此您不必标记类抽象,并且可以实例化它们。

    【讨论】:

    • 谢谢迈克,你回答得很好,但不是我要问的。我的问题由 Tor Haugen 回答,你可以参考那里。我没有让自己被理解是我的错。
    • 不需要覆盖虚拟成员。
    • @George:我澄清了答案以适应您的问题。 @Frederik:请注意,我说过“您可以覆盖”虚拟成员。
    • @Mike:我指的是你的这句话:事实上,这可能是必需的——我一时想不起来
    • @Frederik:当然,你是对的。我想我想说点别的。那个时候早上太早了,我不知道为什么我要在喝够咖啡之前写这篇文章。 :) 感谢您的澄清。
    【解决方案5】:

    我能理解这种困惑。我以前去过那里,所以我将分享我如何保持基本差异...

    virtualabstract

    • 如果类方法(或属性)是 标记为virtual,那么它可能是 使用override 覆盖 关键字,如果您选择继承 (又名派生)来自该类。

      virtual 关键字旨在唤起这样一种想法,即该方法可能是也可能不是实际调用的方法。因此,我一直将virtual 成员视为默认实现,这意味着它表示可以泛化的功能,例如Human 类上的Eat() 方法,这可能涉及与一个人的手。但是,ChineseHuman 类可能会覆盖Eat()默认实现,以便允许使用筷子代替的实现。最后,因为虚拟方法和属性是默认实现,定义该成员的类必须提供方法或属性的完整实现。所有Human 对象都必须知道如何 Eat()

      一种面向对象的思维方式可能会声明virtual 成员代表本能Eat()Human 类对象的本能ChineseHuman 可以学习Eat() 用筷子。

    • 如果类方法(或属性)是 标记为abstract,那么它必须 使用override 覆盖 关键字,如果您选择继承

      abstract 关键字旨在唤起这样一种想法,即该类仅支持该成员所代表的能力,并且没有任何通用逻辑可以针对该特征进行概括。换句话说,abstract 成员只是概念,因此它们缺乏实现。当我们实现继承关系时,C# 要求我们使用override 抽象成员有点令人困惑,但在这种情况下,这实际上意味着我们用具体实现覆盖了空概念。 Human 类的 abstract 成员的示例可能是 Speak()。对于所有Human 对象,不会有一种通用的说话方式,也不是本能的,因为它需要语言来表达。注意:有些人可能会争辩说,Speak() 属于 interface

      面向对象的思维方式可能会声明 abstract 成员代表行为(方法)要学习 em> 和知识或信念(属性)要获得Speak()Human 类对象的学习 行为。 ChineseHuman 可能会以不同于 EnglishHuman 的方式学习 Speak(),并且他们都不知道如何 Speak() 只是因为它们都是 Human

    细微差别:

    • virtual 方法不需要被覆盖。
    • 没有 virtual 类这样的东西。
    • abstract 成员只能出现在 abstract 类中。在上面的例子中,在Human 类上有一个abstract 方法意味着Human 是一个abstract 类,因此不能使用短语var baby = new Human(); 实例化Human。相反,BabyHuman 类应该继承自 Human,并且它应该被实例化为 var baby = new BabyHuman();。因为BabyHuman()HumanEnglishHumanChineseHuman 都继承自Human,所以EnglishHuman 可以继承自BabyHuman 而不是Human。成为Human 就是abstract,因为我们都不仅仅是Human
    • abstract 成员不能被隐藏,只有他们的 override 实现可能是(进一步的继承链)。例如,BabyHuman 必须将abstract Speak() 方法实现为override。如果EnglishHuman 继承自BabyHuman,则它可能会使用new 关键字隐藏Speak()BabyHuman 实现及其自己的实现(请参阅下面的“C# 中的方法隐藏”参考)。
    • abstract 类可以有 virtual 成员。这是interfaceabstract 类之间的主要区别。从这个意义上说,abstract 类可以定义类行为的协定和模板,而 interface 仅定义协定。

    代码参考:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-13
      • 1970-01-01
      • 1970-01-01
      • 2012-11-30
      • 2011-02-16
      • 2011-03-25
      • 1970-01-01
      相关资源
      最近更新 更多