虛擬覆蓋新
虛擬和覆蓋
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
{
}
}