【发布时间】:2010-10-11 00:24:05
【问题描述】:
在MSDN中有提到,
http://msdn.microsoft.com/en-us/library/9fkccyh4(VS.80).aspx
我很困惑这个项目是什么意思“通过包含使用覆盖修饰符的属性声明,可以在派生类中覆盖虚拟继承的属性。”?
(这是虚拟和抽象之间的第二个区别)
提前致谢, 乔治
【问题讨论】:
在MSDN中有提到,
http://msdn.microsoft.com/en-us/library/9fkccyh4(VS.80).aspx
我很困惑这个项目是什么意思“通过包含使用覆盖修饰符的属性声明,可以在派生类中覆盖虚拟继承的属性。”?
(这是虚拟和抽象之间的第二个区别)
提前致谢, 乔治
【问题讨论】:
你能解释一下它有什么令人困惑的地方吗?属性可以像任何其他方法一样被覆盖。
public class Base {
public virtual int Prop1 { get { ... } set { ... } }
}
public class Derived : Base {
public override int Prop1 { get { ... } set { ... } }
【讨论】:
如果您在基类中声明一个虚拟方法,您可以在派生类中覆盖它。
例子
class MyBaseClass
{
public virtual void MyOverridableMethod()
{
...
}
}
class MyDerivedClass : MyBaseClass
{
public override void MyOverridableMethod()
{
...
}
}
注意 MyDerivedClass 中的 override 修饰符。
【讨论】:
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;
}
}
【讨论】:
好的,假设您有一个基类,而该基类本身是从另一个类派生的。
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),因此您不必标记类抽象,并且可以实例化它们。
【讨论】:
我能理解这种困惑。我以前去过那里,所以我将分享我如何保持基本差异...
virtual 与abstract:
如果类方法(或属性)是
标记为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() 是Human 和EnglishHuman 和ChineseHuman 都继承自Human,所以EnglishHuman 可以继承自BabyHuman 而不是Human。成为Human 就是abstract,因为我们都不仅仅是Human。 abstract 成员不能被隐藏,只有他们的 override 实现可能是(进一步的继承链)。例如,BabyHuman 必须将abstract Speak() 方法实现为override。如果EnglishHuman 继承自BabyHuman,则它可能会使用new 关键字隐藏Speak() 的BabyHuman 实现及其自己的实现(请参阅下面的“C# 中的方法隐藏”参考)。abstract 类可以有 virtual 成员。这是interface 和abstract 类之间的主要区别。从这个意义上说,abstract 类可以定义类行为的协定和模板,而 interface 仅定义协定。代码参考:
【讨论】: