虚拟覆盖新
虚拟和覆盖
virtual
关键字允许方法,属性,索引器或事件被派生类覆盖并呈现多态行为。 (默认情况下,成员在 C#中是非虚拟的)
public class BaseClass
{
public virtual void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
为了覆盖成员,override
关键字用于派生类。 (注意成员的签名必须相同)
public class DerivedClass: BaseClass
{
public override void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
虚拟成员的多态行为意味着在调用时,正在执行的实际成员是在运行时而不是在编译时确定的。在最派生类中的重写成员,特定对象是将被执行的实例。
简而言之,对象可以在编译时声明类型为 BaseClass
,但如果在运行时它是 DerivedClass
的实例,则将执行被覆盖的成员:
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"
覆盖方法是可选的:
public class SecondDerivedClass: DerivedClass {}
var obj1 = new SecondDerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"
新
由于只有定义为 virtual
的成员是可覆盖和多态的,因此重新定义非虚拟成员的派生类可能会导致意外结果。
public class BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
public class DerivedClass: BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!
发生这种情况时,始终根据对象的类型在编译时确定执行的成员。
- 如果对象声明为
BaseClass
类型(即使在运行时是派生类),则执行BaseClass
的方法 - 如果对象声明为
DerivedClass
类型,则执行DerivedClass
的方法。
这通常是一个意外(在将相同的成员添加到派生类型后将成员添加到基本类型中)并且在这些方案中生成编译器警告 CS0108 。
如果是故意的,那么 new
关键字用于抑制编译器警告(并告知其他开发人员你的意图!)。行为保持不变,new
关键字只是抑制编译器警告。
public class BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
public class DerivedClass: BaseClass
{
public new void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!
覆盖的使用不是可选的
与 C++不同,override
关键字的使用不是可选的:
public class A
{
public virtual void Foo()
{
}
}
public class B : A
{
public void Foo() // Generates CS0108
{
}
}
以上示例也会导致警告 CS0108 ,因为 B.Foo()
不会自动覆盖 A.Foo()
。当意图覆盖基类并导致多态行为时添加 override
,当你需要非多态行为时添加 new
并使用静态类型解析调用。后者应谨慎使用,因为它可能会导致严重的混淆。
以下代码甚至导致错误:
public class A
{
public void Foo()
{
}
}
public class B : A
{
public override void Foo() // Error: Nothing to override
{
}
}
派生类可以引入多态性
以下代码完全有效(尽管很少见):
public class A
{
public void Foo()
{
Console.WriteLine("A");
}
}
public class B : A
{
public new virtual void Foo()
{
Console.WriteLine("B");
}
}
现在所有具有静态引用 B(及其衍生物)的对象都使用多态来解析 Foo()
,而 A 的引用使用 A.Foo()
。
A a = new A();
a.Foo(); // Prints "A";
a = new B();
a.Foo(); // Prints "A";
B b = new B();
b.Foo(); // Prints "B";
虚拟方法不能是私有的
C#编译器严格防止无意义的构造。标记为 virtual
的方法不能是私有的。因为无法从派生类型中看到私有方法,所以也无法覆盖它。这无法编译:
public class A
{
private virtual void Foo() // Error: virtual methods cannot be private
{
}
}