簡介
在 C#
中,virtual
和 abstract
關鍵字都用于面向對象編程中的繼承和多態,它們主要用于方法、屬性和事件的定義,但在用法上存在一些重要的區別。
virtual 關鍵字
virtual
表示可重寫的方法,但可以提供默認實現,派生類可以選擇是否重寫。
使用規則
- 只能在類成員(方法、屬性、事件)上使用,不能用于字段。
- 必須有默認實現,子類可以選擇是否
override
進行重寫。 - 在基類中調用時,調用的是基類的方法,但如果子類重寫了,則會調用子類的實現(運行時多態)。
- 不能用于
static
方法,但可以用于屬性和事件。
示例:virtual
方法
using System;class BaseClass
{public virtual void ShowMessage(){Console.WriteLine("基類的 ShowMessage() 方法");}
}class DerivedClass : BaseClass
{public override void ShowMessage(){Console.WriteLine("子類的 ShowMessage() 方法");}
}class Program
{static void Main(){BaseClass obj1 = new BaseClass();obj1.ShowMessage(); // 輸出: 基類的 ShowMessage() 方法DerivedClass obj2 = new DerivedClass();obj2.ShowMessage(); // 輸出: 子類的 ShowMessage() 方法BaseClass obj3 = new DerivedClass();obj3.ShowMessage(); // 輸出: 子類的 ShowMessage() 方法 (運行時多態)}
}
virtual
屬性
class Animal
{public virtual string Name { get; set; } = "Unknown Animal";
}class Dog : Animal
{public override string Name { get; set; } = "Dog";
}class Program
{static void Main(){Animal a = new Dog();Console.WriteLine(a.Name); // 輸出: Dog}
}
abstract 關鍵字
abstract
表示抽象成員,沒有實現,必須在派生類中重寫。它只能出現在 abstract
類中。
使用規則
- 只能在
abstract
類中使用,不能用于sealed
類(密封類)。 - 沒有方法體,子類必須
override
提供具體實現。 - 抽象方法不能有
private
修飾符,但可以是protected
或public
。 - 抽象方法在基類中不能有默認實現,必須在子類實現。
- 抽象類本身不能被實例化,但可以有構造函數。
示例:abstract
方法
using System;abstract class Animal
{public abstract void MakeSound(); // 沒有方法體,子類必須實現
}class Dog : Animal
{public override void MakeSound(){Console.WriteLine("汪汪汪!");}
}class Cat : Animal
{public override void MakeSound(){Console.WriteLine("喵喵喵!");}
}class Program
{static void Main(){Animal dog = new Dog();dog.MakeSound(); // 輸出: 汪汪汪!Animal cat = new Cat();cat.MakeSound(); // 輸出: 喵喵喵!}
}
abstract
屬性
abstract class Animal
{public abstract string Name { get; set; }
}class Dog : Animal
{public override string Name { get; set; } = "Dog";
}class Program
{static void Main(){Animal a = new Dog();Console.WriteLine(a.Name); // 輸出: Dog}
}
virtual vs abstract 的區別
關鍵字 | virtual | abstract |
---|---|---|
是否有實現 | 有默認實現 | 沒有默認實現,必須在子類實現 |
是否必須重寫 | 可以重寫,也可以不重寫 | 必須被子類重寫 |
是否能在非 abstract 類中使用 | 可以 | 只能用于 abstract 類 |
能否實例化 | 可以實例化基類 | 抽象類不能實例化 |
適用范圍 | 方法、屬性、事件 | 方法、屬性、事件 |
什么時候用 virtual,什么時候用 abstract?
用 virtual
- 適用于提供默認行為,但允許子類覆蓋的場景。
- 例如:基類提供
virtual
方法SaveToDatabase()
,子類可以重寫也可以直接使用。
用 abstract
- 適用于基類無法提供合理默認實現,必須由子類提供具體實現的情況。
- 例如:
Animal
類的MakeSound()
方法,每種動物的叫聲都不同,所以必須由子類實現。
綜合示例
using System;abstract class Animal
{public abstract void MakeSound(); // 必須由子類實現public virtual void Sleep(){Console.WriteLine("動物正在睡覺...");}
}class Dog : Animal
{public override void MakeSound(){Console.WriteLine("汪汪汪!");}public override void Sleep(){Console.WriteLine("狗狗正在睡覺...");}
}class Program
{static void Main(){Animal myDog = new Dog();myDog.MakeSound(); // 汪汪汪!myDog.Sleep(); // 狗狗正在睡覺...}
}
virtual 方法如何調用基類方法?
當子類重寫了 virtual
方法后,仍然可以在 override
方法中調用 基類的實現,這在 擴展父類功能 時特別有用。
在子類 override
方法中調用 base
方法
using System;class BaseClass
{public virtual void ShowMessage(){Console.WriteLine("基類的 ShowMessage() 方法");}
}class DerivedClass : BaseClass
{public override void ShowMessage(){base.ShowMessage(); // 先調用基類方法Console.WriteLine("子類的 ShowMessage() 方法");}
}class Program
{static void Main(){DerivedClass obj = new DerivedClass();obj.ShowMessage();}
}
輸出:
基類的 ShowMessage() 方法
子類的 ShowMessage() 方法
- 這里
base.ShowMessage()
; 先執行基類的方法,再執行子類的邏輯。 - 這種方式避免了完全覆蓋,而是基于已有邏輯做擴展。
abstract 和 interface 的區別
在 C#
中,abstract
和 interface
都可以定義必須由子類實現的方法,但它們有一些關鍵區別。
abstract | interface | |
---|---|---|
是否有方法實現 | 不一定,可以有 abstract 也可以 virtual | C# 8.0+ 支持默認實現,但大多數情況下沒有 |
是否可以包含字段 | 可以(非 static) | 不可以 |
是否可以有構造函數 | 可以 | 不可以 |
繼承方式 | 只能繼承一個 | 可以實現多個 |
abstract class Animal
{public abstract void MakeSound();public virtual void Sleep(){Console.WriteLine("睡覺中...");}
}interface IAnimal
{void MakeSound();void Sleep();
}class Dog : Animal, IAnimal // 可以同時繼承抽象類和接口
{public override void MakeSound(){Console.WriteLine("汪汪汪!");}public void Sleep(){Console.WriteLine("狗狗正在睡覺...");}
}
abstract class
適用于有共享邏輯的場景,而 interface
適用于不同類之間的通用行為。
virtual 和 abstract 結合使用
在某些情況下,抽象類可以定義 virtual
方法,讓子類選擇是否覆蓋。
abstract class Shape
{public abstract void Draw(); // 必須重寫public virtual void Move(){Console.WriteLine("Shape is moving...");}
}class Circle : Shape
{public override void Draw(){Console.WriteLine("畫一個圓形");}public override void Move(){Console.WriteLine("圓形在移動...");}
}class Program
{static void Main(){Shape shape = new Circle();shape.Draw(); // 畫一個圓形shape.Move(); // 圓形在移動...}
}
Draw()
是抽象方法,必須重寫。Move()
是虛方法,子類可以選擇是否重寫。
virtual 和 abstract 在性能上的考慮
在 C#
中,virtual
方法會在運行時通過虛方法表(VTable
) 進行調用,而 abstract
只是提供一個強制實現的約定,子類實現后仍然是 virtual
方法。
普通方法調用(非 virtual
)
public class MyClass
{public void NormalMethod() { }
}// 直接調用,無額外開銷。
virtual
方法調用
public class MyClass
{public virtual void VirtualMethod() { }
}// 通過 VTable 進行間接調用,可能稍微慢一點(但一般無影響)。
abstract
方法調用
public abstract class MyBaseClass
{public abstract void AbstractMethod();
}public class MyClass : MyBaseClass
{public override void AbstractMethod() { }
}// abstract 方法在子類實現后,仍然是虛方法(virtual),運行時仍然使用 VTable 機制。
sealed 和 override 一起使用
sealed
關鍵字可以阻止子類進一步重寫 virtual
方法。
class Parent
{public virtual void Show(){Console.WriteLine("父類方法");}
}class Child : Parent
{public sealed override void Show(){Console.WriteLine("子類方法,不能再被重寫");}
}class GrandChild : Child
{// 這里會報錯,因為 `Show` 方法被 `sealed` 了// public override void Show() { }
}
-
子類
Child
已經sealed override
,所以GrandChild
不能再重寫Show()
方法。 -
適用于限制特定方法的繼承,比如框架設計時控制擴展性。
什么時候用 virtual,什么時候用 abstract?
用 virtual
- 當希望提供一個默認實現,但允許子類根據需要重寫時。
用 abstract
- 當基類無法提供默認實現,必須要求子類強制實現時。
用 sealed override
- 當希望限制某個方法的進一步重寫時。
用 abstract + virtual 組合
- 適用于部分方法必須實現,部分方法可以選擇重寫的情況。