C#のvirtualとは?overrideとの違い・使い方・実行結果を初心者向けにわかりやすく解説
はじめに
C#のvirtualは、継承を使ったプログラムを書くときに登場する重要なキーワードです。
特に、次のような疑問を持つ初心者は多いです。
「C#のvirtualとは何?」
「overrideとの違いがわからない」
「virtualを付けると実行結果がどう変わるの?」
「newとの違いは?」
「abstractやinterfaceとは何が違うの?」
virtualは、親クラスに定義したメソッドやプロパティなどを、子クラス側で上書きできるようにするための修飾子です。
C#では、クラスを継承していても、すべてのメソッドを自由に上書きできるわけではありません。子クラスで処理を変更したい場合は、親クラス側にvirtualを付け、子クラス側でoverrideを使う必要があります。
この記事では、C#のvirtualについて、初心者にもわかりやすいように、基本的な意味、overrideとの違い、使い方、実行結果、newやabstractとの違いまで順番に解説します。
1. C#のvirtualとは?初心者向けに意味をわかりやすく解説
1-1. virtualは「派生クラスで動作を上書きできるようにする」修飾子
C#のvirtualとは、親クラスに定義したメンバーを、派生クラス、つまり子クラスで上書きできるようにするための修飾子です。
たとえば、次のような親クラスがあるとします。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
このSpeakメソッドにはvirtualが付いています。
そのため、子クラスでは次のようにoverrideを使って処理を上書きできます。
C#class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}
このように、virtualは「このメソッドは子クラスで別の動作に変更してもよい」という意味を持ちます。
C#では、親クラスのメソッドを子クラスで上書きしたい場合、親クラス側にvirtualが必要です。そして、子クラス側ではoverrideを使います。
1-2. virtualを使うと何ができるのか
virtualを使うと、親クラスに共通の処理を用意しながら、子クラスごとに異なる動作を実装できます。
たとえば、動物を表すAnimalクラスを作り、犬や猫などのクラスを継承で作る場合を考えます。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("犬がワンワンと鳴きます");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("猫がニャーと鳴きます");
}
}
この例では、Animalクラスに共通のSpeakメソッドを用意しています。
しかし、実際の鳴き声は犬と猫で異なります。そのため、DogクラスとCatクラスでSpeakメソッドをoverrideして、それぞれ別の処理に変更しています。
これにより、同じSpeakというメソッド名を使いながら、実際のクラスに応じて異なる動作を実現できます。
1-3. virtualが使えるメンバーの種類
C#のvirtualは、主に次のようなメンバーに使えます。
| 種類 | virtualを使えるか | 例 |
|---|---|---|
| メソッド | 使える | public virtual void Run() |
| プロパティ | 使える | public virtual string Name { get; set; } |
| インデクサー | 使える | public virtual string this[int index] |
| イベント | 使える | public virtual event EventHandler Changed |
一方で、次のようなものにはvirtualを付けられません。
| 種類 | 理由 |
|---|---|
privateメソッド | 子クラスから見えないため |
staticメソッド | インスタンスごとの動作ではないため |
| コンストラクタ | 継承先で上書きするものではないため |
たとえば、次のような書き方はできません。
C#class Sample
{
private virtual void Test()
{
}
}
privateメソッドは、そのクラスの中だけで使うメソッドです。子クラスから見えないため、上書きする意味がなく、virtualを付けることはできません。
また、次のようにstaticメソッドにvirtualを付けることもできません。
C#class Sample
{
public static virtual void Test()
{
}
}
virtualはインスタンスごとの動的な動作を扱うための仕組みです。そのため、クラス自体に属するstaticメンバーとは組み合わせられません。
1-4. virtualを使う場面の具体例
virtualは、実務でもよく使われる考え方です。
たとえば、次のような場面で使います。
| 場面 | 例 |
|---|---|
| 子クラスごとに処理を変えたい | 動物ごとに鳴き声を変える |
| 共通処理を残しつつ一部だけ変更したい | 基本の計算処理に追加処理を入れる |
| テストしやすい設計にしたい | 一部の処理を差し替え可能にする |
| フレームワーク側の処理を拡張したい | ライブラリのクラスを継承して動作を変更する |
具体例として、通知処理を考えてみましょう。
C#class Notifier
{
public virtual void Send(string message)
{
Console.WriteLine("通知: " + message);
}
}
class EmailNotifier : Notifier
{
public override void Send(string message)
{
Console.WriteLine("メール送信: " + message);
}
}
class SmsNotifier : Notifier
{
public override void Send(string message)
{
Console.WriteLine("SMS送信: " + message);
}
}
このように、親クラスで「通知する」という共通の考え方を用意し、子クラスでメール通知やSMS通知などに処理を変えられます。
virtualを使うことで、コードの再利用性と拡張性を高めることができます。
2. C#でvirtualが必要になる理由
2-1. 継承したクラスごとに処理を変えたい場合
virtualが必要になる代表的な理由は、継承したクラスごとに処理を変えたいからです。
たとえば、親クラスとしてEmployeeクラスを作り、正社員とアルバイトで給与計算の方法を変えたい場合を考えます。
C#class Employee
{
public virtual int CalculatePay()
{
return 0;
}
}
class FullTimeEmployee : Employee
{
public override int CalculatePay()
{
return 300000;
}
}
class PartTimeEmployee : Employee
{
public override int CalculatePay()
{
return 120000;
}
}
この例では、EmployeeクラスにCalculatePayメソッドを用意しています。
しかし、正社員とアルバイトでは給与計算の方法が異なります。そのため、FullTimeEmployeeとPartTimeEmployeeでoverrideして、処理を変えています。
このように、同じメソッド名でありながら、クラスごとに異なる処理を実行したい場合にvirtualが役立ちます。
2-2. 共通処理を残しながら一部だけ変更したい場合
virtualは、親クラスにある共通処理を残しながら、子クラスで一部だけ処理を追加・変更したい場合にも使います。
たとえば、基本のログ出力処理を親クラスに用意し、子クラスで追加のログを出したい場合です。
C#class Processor
{
public virtual void Execute()
{
Console.WriteLine("基本処理を実行します");
}
}
class CustomProcessor : Processor
{
public override void Execute()
{
base.Execute();
Console.WriteLine("追加処理を実行します");
}
}
base.Execute()を呼び出すことで、親クラスの処理を実行できます。
実行すると、次のようになります。
基本処理を実行します
追加処理を実行します
このように、virtualとoverrideを使うと、親クラスの処理を完全に置き換えるだけでなく、親クラスの処理に子クラス独自の処理を追加することもできます。
2-3. ポリモーフィズムを実現したい場合
C#でvirtualが重要な理由のひとつが、ポリモーフィズムを実現できることです。
ポリモーフィズムとは、同じ型として扱っていても、実際のオブジェクトの種類に応じて異なる処理が呼び出される仕組みです。
たとえば、次のコードを見てください。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("ニャー");
}
}
class Program
{
static void Main()
{
Animal[] animals =
{
new Dog(),
new Cat()
};
foreach (Animal animal in animals)
{
animal.Speak();
}
}
}
実行結果は次のようになります。
ワンワン
ニャー
変数の型はAnimalですが、実際に入っているオブジェクトはDogやCatです。
Speakメソッドがvirtualで定義され、子クラス側でoverrideされているため、実行時のオブジェクトに応じた処理が呼ばれます。
これがポリモーフィズムです。
2-4. virtualを使わない場合に起きる問題
virtualを使わない場合、子クラスで同じ名前のメソッドを定義しても、正しい意味での上書きにはなりません。
次のコードを見てください。
C#class Animal
{
public void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public void Speak()
{
Console.WriteLine("ワンワン");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog();
animal.Speak();
}
}
一見すると、DogクラスのSpeakが呼ばれそうに見えます。
しかし、実行結果は次のようになります。
動物が鳴きます
なぜなら、親クラスのSpeakメソッドにvirtualが付いていないためです。
この場合、子クラスのSpeakメソッドは親クラスのメソッドを上書きしているのではなく、同じ名前のメソッドを別に定義しているだけです。
そのため、親クラス型の変数から呼び出すと、親クラスのメソッドが実行されます。
この問題を避けるために、上書きしたいメソッドにはvirtualを付ける必要があります。
3. virtualとoverrideの違い
3-1. virtualは「上書きされる側」に付ける
virtualは、親クラス側、つまり上書きされる側に付けます。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
このコードでは、AnimalクラスのSpeakメソッドにvirtualが付いています。
これは、「このSpeakメソッドは、子クラスで上書きしてもよい」という意味です。
virtualを付けないメソッドは、基本的にoverrideできません。
つまり、子クラスで動作を変えたい可能性があるメソッドには、親クラス側であらかじめvirtualを付けておく必要があります。
3-2. overrideは「上書きする側」に付ける
overrideは、子クラス側、つまり上書きする側に付けます。
C#class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}
このコードでは、DogクラスがAnimalクラスのSpeakメソッドを上書きしています。
overrideを付けることで、「これは親クラスのvirtualメソッドを上書きしています」とC#コンパイラに明示できます。
親クラスにvirtualがないのにoverrideを書こうとすると、コンパイルエラーになります。
3-3. virtualとoverrideの基本構文
virtualとoverrideの基本構文は次のとおりです。
C#class 親クラス
{
public virtual 戻り値の型 メソッド名()
{
// 親クラスの処理
}
}
class 子クラス : 親クラス
{
public override 戻り値の型 メソッド名()
{
// 子クラスの処理
}
}
具体例は次のようになります。
C#class Character
{
public virtual void Attack()
{
Console.WriteLine("通常攻撃");
}
}
class Warrior : Character
{
public override void Attack()
{
Console.WriteLine("剣で攻撃");
}
}
ポイントは、親クラス側にvirtual、子クラス側にoverrideを書くことです。
また、overrideする場合は、基本的に次の内容を一致させる必要があります。
メソッド名
戻り値の型
引数の数
引数の型
アクセス修飾子
たとえば、親クラスがpublic virtual void Attack()なら、子クラスもpublic override void Attack()のように書きます。
3-4. virtualとoverrideの関係を図解で理解する
virtualとoverrideの関係を図で表すと、次のようになります。
親クラス Animal
└─ public virtual void Speak()
「子クラスで上書きしてもよい」
↓ 継承
子クラス Dog
└─ public override void Speak()
「親クラスのSpeakを上書きする」
もう少し処理の流れとして見ると、次のようになります。
Animal animal = new Dog();
変数の型: Animal
実体の型: Dog
animal.Speak();
↓ virtual / override があるため
実体であるDogのSpeakが呼ばれる
つまり、virtualとoverrideを使うと、変数の型ではなく、実際に作られたオブジェクトの型に応じてメソッドが呼ばれます。
これがC#におけるvirtualの大きな特徴です。
3-5. virtualとoverrideの違いを表で比較
virtualとoverrideの違いを表にまとめると、次のようになります。
| 項目 | virtual | override |
|---|---|---|
| 付ける場所 | 親クラス | 子クラス |
| 役割 | 上書きを許可する | 実際に上書きする |
| 単独で使えるか | 使える | 親にvirtualなどが必要 |
| 処理の中身 | 既定の処理を書ける | 子クラス独自の処理を書く |
| 主な目的 | 拡張可能にする | 動作を変更する |
簡単に言うと、virtualは「上書きされる準備」、overrideは「実際の上書き」です。
C#class BaseClass
{
public virtual void Method()
{
Console.WriteLine("親クラスの処理");
}
}
class DerivedClass : BaseClass
{
public override void Method()
{
Console.WriteLine("子クラスの処理");
}
}
このように、virtualとoverrideはセットで理解するとわかりやすくなります。
4. C#のvirtualの基本的な使い方
4-1. 親クラスにvirtualメソッドを定義する
まずは、親クラスにvirtualメソッドを定義します。
C#class Person
{
public virtual void Introduce()
{
Console.WriteLine("私は人です");
}
}
このIntroduceメソッドは、子クラスで上書き可能です。
virtualを付けることで、親クラスは次のような意味を持つようになります。
このメソッドには標準の処理がある。
ただし、子クラスで必要に応じて変更してもよい。
このように、親クラスに既定の処理を持たせられる点がvirtualの特徴です。
4-2. 子クラスでoverrideして処理を変更する
次に、子クラスでoverrideを使って処理を変更します。
C#class Student : Person
{
public override void Introduce()
{
Console.WriteLine("私は学生です");
}
}
これで、StudentクラスのオブジェクトからIntroduceメソッドを呼び出すと、親クラスではなく子クラスの処理が実行されます。
全体のコードは次のようになります。
C#using System;
class Person
{
public virtual void Introduce()
{
Console.WriteLine("私は人です");
}
}
class Student : Person
{
public override void Introduce()
{
Console.WriteLine("私は学生です");
}
}
class Program
{
static void Main()
{
Student student = new Student();
student.Introduce();
}
}
4-3. 実行結果を確認する
上のコードを実行すると、次の結果になります。
私は学生です
StudentクラスでIntroduceメソッドをoverrideしているため、子クラス側の処理が呼ばれています。
ここで重要なのは、親クラスのIntroduceメソッドにvirtualが付いている点です。
もし親クラス側にvirtualがなければ、子クラス側でoverrideできません。
4-4. virtualメソッドを呼び出したときの動作
virtualメソッドを呼び出したとき、C#では実行時のオブジェクトの型に応じて、どのメソッドを実行するかが決まります。
次のコードを見てください。
C#using System;
class Person
{
public virtual void Introduce()
{
Console.WriteLine("私は人です");
}
}
class Student : Person
{
public override void Introduce()
{
Console.WriteLine("私は学生です");
}
}
class Program
{
static void Main()
{
Person person = new Student();
person.Introduce();
}
}
変数の型はPersonです。
C#Person person
しかし、実際に入っているオブジェクトはStudentです。
C#new Student()
実行結果は次のようになります。
私は学生です
これは、Introduceがvirtualメソッドであり、Studentクラスでoverrideされているためです。
virtualを使うと、変数の型ではなく、実際のオブジェクトの型に応じてメソッドが呼び出されます。
4-5. baseを使って親クラスの処理を呼び出す方法
子クラスでoverrideしたメソッドの中から、親クラスの処理を呼び出したい場合はbaseを使います。
C#using System;
class Person
{
public virtual void Introduce()
{
Console.WriteLine("私は人です");
}
}
class Student : Person
{
public override void Introduce()
{
base.Introduce();
Console.WriteLine("私は学生です");
}
}
class Program
{
static void Main()
{
Student student = new Student();
student.Introduce();
}
}
実行結果は次のようになります。
私は人です
私は学生です
base.Introduce()によって、親クラスであるPersonのIntroduceメソッドが呼び出されています。
その後、子クラス独自の処理であるConsole.WriteLine("私は学生です");が実行されます。
このように、baseを使うと、親クラスの共通処理を活かしながら、子クラスで追加処理を書くことができます。
5. virtualを使ったサンプルコードと実行結果
5-1. 動物クラスを例にしたvirtualの基本コード
ここでは、動物クラスを使ってvirtualの基本コードを確認します。
まず、親クラスとしてAnimalクラスを作ります。
C#using System;
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
Speakメソッドにはvirtualが付いています。
そのため、このメソッドは子クラスで上書きできます。
5-2. overrideで犬クラス・猫クラスの動作を変える例
次に、Animalクラスを継承して、DogクラスとCatクラスを作ります。
C#class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("犬: ワンワン");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("猫: ニャー");
}
}
DogクラスとCatクラスでは、どちらもSpeakメソッドをoverrideしています。
これにより、同じSpeakメソッドでも、犬と猫で異なる動作になります。
全体のコードは次のとおりです。
C#using System;
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("犬: ワンワン");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("猫: ニャー");
}
}
class Program
{
static void Main()
{
Animal animal = new Animal();
Animal dog = new Dog();
Animal cat = new Cat();
animal.Speak();
dog.Speak();
cat.Speak();
}
}
5-3. 実行結果からvirtualの動きを確認する
上のコードを実行すると、次の結果になります。
動物が鳴きます
犬: ワンワン
猫: ニャー
ここで注目したいのは、変数の型がすべてAnimalである点です。
C#Animal animal = new Animal();
Animal dog = new Dog();
Animal cat = new Cat();
変数の型は同じAnimalですが、実際のオブジェクトはそれぞれ異なります。
| 変数 | 変数の型 | 実際のオブジェクト |
|---|---|---|
animal | Animal | Animal |
dog | Animal | Dog |
cat | Animal | Cat |
Speakメソッドがvirtualで定義され、子クラスでoverrideされているため、実際のオブジェクトに応じた処理が実行されます。
5-4. 親クラス型の変数で子クラスの処理が呼ばれる仕組み
次のコードに注目してください。
C#Animal dog = new Dog();
dog.Speak();
変数の型はAnimalです。
しかし、実際に作られているオブジェクトはDogです。
Speakメソッドがvirtualで、Dogクラス側でoverrideされているため、実行時にはDogクラスのSpeakメソッドが呼ばれます。
つまり、C#では次のような流れで処理が決まります。
1. dog.Speak() が呼ばれる
2. Speakはvirtualメソッドである
3. 実際のオブジェクトの型を確認する
4. 実体はDogである
5. DogのoverrideされたSpeakを実行する
この仕組みを理解すると、virtualの役割がわかりやすくなります。
5-5. virtualなしの場合との実行結果の違い
次に、virtualを使わない場合と比較してみましょう。
C#using System;
class Animal
{
public void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public new void Speak()
{
Console.WriteLine("犬: ワンワン");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog();
animal.Speak();
Dog dog = new Dog();
dog.Speak();
}
}
実行結果は次のようになります。
動物が鳴きます
犬: ワンワン
最初の呼び出しでは、変数の型がAnimalなので、親クラスのSpeakが呼ばれます。
C#Animal animal = new Dog();
animal.Speak();
一方、次の呼び出しでは、変数の型がDogなので、子クラスのSpeakが呼ばれます。
C#Dog dog = new Dog();
dog.Speak();
このように、virtualとoverrideを使わない場合、変数の型によって呼び出されるメソッドが変わります。
一方、virtualとoverrideを使うと、実際のオブジェクトの型に応じて処理が呼び出されます。
6. virtual・override・newの違い
6-1. newは上書きではなく「隠す」ための修飾子
C#には、virtualやoverrideのほかに、newという修飾子があります。
ここでのnewは、オブジェクトを作るときのnewとは意味が異なります。
C#Dog dog = new Dog();
このnewはインスタンスを作るためのキーワードです。
一方、メソッドに付けるnewは、親クラスのメンバーを「隠す」ための修飾子です。
C#class Animal
{
public void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public new void Speak()
{
Console.WriteLine("犬が鳴きます");
}
}
この場合、DogクラスのSpeakは、親クラスのSpeakを上書きしているわけではありません。
親クラスのSpeakを、子クラス側で同じ名前のメソッドによって隠しているだけです。
6-2. overrideとnewの実行結果の違い
overrideとnewの違いは、親クラス型の変数で呼び出したときにわかりやすく現れます。
まず、overrideの場合です。
C#using System;
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("犬がワンワンと鳴きます");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog();
animal.Speak();
}
}
実行結果は次のようになります。
犬がワンワンと鳴きます
overrideの場合、変数の型がAnimalでも、実体がDogなのでDogの処理が呼ばれます。
次に、newの場合です。
C#using System;
class Animal
{
public void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public new void Speak()
{
Console.WriteLine("犬がワンワンと鳴きます");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog();
animal.Speak();
}
}
実行結果は次のようになります。
動物が鳴きます
newの場合、親クラス型の変数から呼び出すと、親クラスのメソッドが呼ばれます。
6-3. newを使うと初心者が混乱しやすい理由
newを使うと、同じオブジェクトを扱っているのに、変数の型によって呼び出されるメソッドが変わります。
次のコードを見てください。
C#using System;
class Animal
{
public void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public new void Speak()
{
Console.WriteLine("犬がワンワンと鳴きます");
}
}
class Program
{
static void Main()
{
Dog dog1 = new Dog();
dog1.Speak();
Animal dog2 = new Dog();
dog2.Speak();
}
}
実行結果は次のようになります。
犬がワンワンと鳴きます
動物が鳴きます
どちらも実際にはDogオブジェクトです。
しかし、変数の型がDogかAnimalかによって、呼び出されるメソッドが変わっています。
この動きは初心者にとって混乱しやすいポイントです。
newは上書きではなく隠蔽です。親クラスのメソッドと子クラスのメソッドは、別のメソッドとして扱われます。
6-4. 基本的にはoverrideを使うべきケース
親クラスの動作を子クラスで変更したい場合は、基本的にはoverrideを使うべきです。
理由は、overrideのほうが「実際のオブジェクトに応じて処理が変わる」という自然な動作になるからです。
C#Animal animal = new Dog();
animal.Speak();
このコードを見たとき、多くの場合、実体がDogなら犬の処理が呼ばれることを期待します。
その期待どおりに動くのがvirtualとoverrideです。
一方、newは親クラスのメンバーを意図的に隠したい場合に使います。ただし、実務では慎重に使うべきです。
特に初心者のうちは、親クラスの処理を変更したいなら、まずvirtualとoverrideを使うと考えて問題ありません。
6-5. virtual・override・newの比較表
virtual、override、newの違いを表にまとめると、次のようになります。
| 修飾子 | 付ける場所 | 役割 | 親クラス型で呼んだ場合 |
|---|---|---|---|
virtual | 親クラス | 上書きを許可する | 子クラスでoverrideされていれば子の処理 |
override | 子クラス | 親のvirtualメンバーを上書きする | 子クラスの処理が呼ばれる |
new | 子クラス | 親のメンバーを隠す | 親クラスの処理が呼ばれる |
コードで比較すると、次のようになります。
C#class Parent
{
public virtual void Method()
{
Console.WriteLine("Parent");
}
}
class Child : Parent
{
public override void Method()
{
Console.WriteLine("Child");
}
}
この場合、親クラス型の変数でも子クラスの処理が呼ばれます。
C#Parent obj = new Child();
obj.Method();
実行結果は次のとおりです。
Child
一方、newの場合は次のようになります。
C#class Parent
{
public void Method()
{
Console.WriteLine("Parent");
}
}
class Child : Parent
{
public new void Method()
{
Console.WriteLine("Child");
}
}
C#Parent obj = new Child();
obj.Method();
実行結果は次のようになります。
Parent
この違いは、C#のvirtualを理解するうえで非常に重要です。
7. virtualとabstract・interfaceの違い
7-1. virtualとabstractの違い
virtualと似た言葉にabstractがあります。
どちらも継承と関係がありますが、意味は異なります。
virtualは、親クラスに既定の処理を書いたうえで、子クラスで必要に応じて上書きできるようにするものです。
一方、abstractは、親クラスでは処理を書かず、子クラスで必ず実装させるものです。
C#abstract class Animal
{
public abstract void Speak();
}
この場合、Speakメソッドには中身がありません。
そのため、Animalを継承する子クラスは、必ずSpeakを実装しなければなりません。
C#class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}
abstractメソッドを実装するときも、子クラス側ではoverrideを使います。
7-2. virtualは親クラスに既定の処理を書ける
virtualの特徴は、親クラスに既定の処理を書けることです。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
この場合、子クラスでoverrideしなくても、親クラスの処理をそのまま使えます。
C#class Bird : Animal
{
}
このBirdクラスはSpeakをoverrideしていません。
しかし、親クラスのSpeakをそのまま使えるため、次のように呼び出せます。
C#Bird bird = new Bird();
bird.Speak();
実行結果は次のようになります。
動物が鳴きます
つまり、virtualは「必要なら上書きしてよい」という仕組みです。
7-3. abstractは子クラスで必ず実装させる
abstractは、子クラスで必ず実装させたい場合に使います。
C#abstract class Animal
{
public abstract void Speak();
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}
もし、DogクラスでSpeakを実装しなければ、コンパイルエラーになります。
C#class Dog : Animal
{
// Speakを実装していないのでエラー
}
abstractは、親クラスでは具体的な処理を決められないが、子クラスには必ずその処理を持たせたい場合に使います。
たとえば、動物には鳴くという動作があるものの、具体的な鳴き声は動物ごとに違う場合、abstractが向いています。
7-4. interfaceとの違い
interfaceも、クラスに特定のメンバーを実装させるために使います。
C#interface IAnimal
{
void Speak();
}
インターフェースを実装するクラスは、定義されたメソッドを実装する必要があります。
C#class Dog : IAnimal
{
public void Speak()
{
Console.WriteLine("ワンワン");
}
}
virtual、abstract、interfaceの違いを簡単に整理すると、次のようになります。
| 種類 | 処理の中身 | 継承・実装 |
|---|---|---|
virtual | 親クラスに書ける | クラス継承 |
abstract | 抽象メソッドには中身を書けない | 抽象クラスを継承 |
interface | 実装すべき機能の約束を定義する | インターフェースを実装 |
interfaceは、「このクラスはこの機能を持つ」という約束を表すときによく使われます。
一方、virtualは、親クラスに共通の処理を持たせつつ、必要に応じて子クラスで変更したい場合に使います。
7-5. virtual・abstract・interfaceの使い分け
virtual、abstract、interfaceは、次のように使い分けるとわかりやすいです。
| 使いたいこと | 選ぶもの |
|---|---|
| 親クラスに標準処理を書きたい | virtual |
| 子クラスで必ず実装させたい | abstract |
| 複数のクラスに共通の機能を約束させたい | interface |
| 継承関係を使って共通処理も持たせたい | abstract classやvirtual |
| 実装の詳細より機能の契約を重視したい | interface |
たとえば、次のような考え方ができます。
virtual:
「基本の処理はこれ。変えたければ子クラスで変更してよい」
abstract:
「ここでは処理を決めない。子クラスで必ず決めてください」
interface:
「この機能を持っていることを保証してください」
実務では、単純な共通処理の拡張にはvirtual、必ず実装してほしい処理にはabstract、柔軟な設計や依存関係の分離にはinterfaceがよく使われます。
8. virtualを使うときの注意点
8-1. privateメソッドにはvirtualを付けられない
privateメソッドにはvirtualを付けられません。
C#class Sample
{
private virtual void Test()
{
}
}
このようなコードはコンパイルエラーになります。
理由は、privateメソッドはそのクラスの中からしか呼び出せず、子クラスから見えないためです。
virtualは子クラスで上書きするための仕組みなので、子クラスから見えないprivateメソッドに付ける意味がありません。
子クラスで上書きしたい場合は、publicやprotectedなど、子クラスから利用できるアクセス修飾子を使います。
C#class Sample
{
protected virtual void Test()
{
Console.WriteLine("親クラスの処理");
}
}
protectedにすると、外部からは直接呼び出せませんが、子クラスからは利用できます。
8-2. staticメソッドにはvirtualを付けられない
staticメソッドにもvirtualは付けられません。
C#class Sample
{
public static virtual void Test()
{
}
}
このようなコードはエラーになります。
staticメソッドは、インスタンスではなくクラス自体に属するメソッドです。
一方、virtualは、実行時のオブジェクトの型に応じて呼び出すメソッドを切り替える仕組みです。
つまり、virtualはインスタンスの動作に関係します。
そのため、クラス自体に属するstaticメソッドとは組み合わせられません。
8-3. sealed overrideでそれ以上の上書きを禁止できる
virtualメソッドを子クラスでoverrideしたあと、さらにその子クラスで上書きされたくない場合は、sealed overrideを使います。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("犬がワンワンと鳴きます");
}
}
class Poodle : Dog
{
// これはエラー
// public override void Speak()
// {
// Console.WriteLine("プードルが鳴きます");
// }
}
DogクラスのSpeakメソッドにはsealed overrideが付いています。
そのため、Dogを継承したPoodleクラスでは、Speakメソッドをさらにoverrideできません。
sealed overrideは、「ここで上書きは終わりにする」という意味です。
8-4. 何でもvirtualにすると設計が複雑になる
virtualは便利ですが、何でもかんでも付ければよいわけではありません。
すべてのメソッドをvirtualにしてしまうと、子クラスでどこでも処理を変更できるようになります。
一見便利に見えますが、次のような問題が起きやすくなります。
| 問題 | 内容 |
|---|---|
| 動作を追いにくい | どのクラスの処理が呼ばれるかわかりにくくなる |
| 予期しない上書きが起きる | 子クラスで想定外の変更をされる可能性がある |
| 保守が難しくなる | 親クラスを変更したときの影響範囲が広がる |
| 設計が不安定になる | クラスの責任があいまいになる |
virtualを付けるのは、「子クラスで変更されることを想定しているメンバー」に限定するのが基本です。
つまり、virtualは設計上の意図を表すキーワードでもあります。
8-5. コンストラクタ内でvirtualメソッドを呼ぶときの注意点
C#では、コンストラクタ内からvirtualメソッドを呼ぶことができます。
しかし、これは注意が必要です。
次のコードを見てください。
C#using System;
class BaseClass
{
public BaseClass()
{
ShowMessage();
}
public virtual void ShowMessage()
{
Console.WriteLine("BaseClass");
}
}
class DerivedClass : BaseClass
{
private string message = "DerivedClass";
public override void ShowMessage()
{
Console.WriteLine(message);
}
}
このコードでは、BaseClassのコンストラクタ内でShowMessage()を呼んでいます。
しかし、実際にDerivedClassのインスタンスを作ると、overrideされたDerivedClass側のShowMessage()が呼ばれる可能性があります。
オブジェクトの初期化中に子クラスの処理が呼ばれると、子クラス側のフィールドがまだ想定どおりに初期化されていない場合があり、バグの原因になります。
そのため、コンストラクタ内でvirtualメソッドを呼ぶ設計は避けたほうが安全です。
9. virtualでよくあるエラーと解決方法
9-1. overrideできない原因
C#でoverrideできない場合、主な原因は次のとおりです。
| 原因 | 内容 |
|---|---|
親クラスにvirtualがない | 上書き対象になっていない |
| メソッド名が違う | 親クラスのメソッドと一致していない |
| 戻り値の型が違う | シグネチャが一致していない |
| 引数が違う | 別のメソッドとして扱われる |
| アクセス修飾子が違う | 上書き条件を満たしていない |
staticになっている | staticはoverrideできない |
sealed overrideされている | それ以上上書きできない |
たとえば、次のコードはエラーになります。
C#class Parent
{
public void Test()
{
}
}
class Child : Parent
{
public override void Test()
{
}
}
親クラスのTestメソッドにvirtualが付いていないため、子クラスでoverrideできません。
9-2. virtualを付け忘れた場合のエラー
親クラスにvirtualを付け忘れると、子クラスでoverrideしたときにエラーになります。
C#class Parent
{
public void Show()
{
Console.WriteLine("Parent");
}
}
class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}
このコードはコンパイルエラーになります。
解決方法は、親クラス側にvirtualを付けることです。
C#class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}
class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}
これで、ChildクラスのShowメソッドは親クラスのShowメソッドを正しく上書きできます。
9-3. メソッド名・戻り値・引数が一致していない場合
overrideする場合、親クラスのメソッドと子クラスのメソッドは、基本的に同じ形である必要があります。
次のコードはエラーになります。
C#class Parent
{
public virtual void Print(string message)
{
Console.WriteLine(message);
}
}
class Child : Parent
{
public override void Print()
{
Console.WriteLine("Child");
}
}
親クラスのPrintメソッドは、string messageという引数を持っています。
しかし、子クラスのPrintメソッドには引数がありません。
そのため、これは同じメソッドの上書きではなく、別のメソッドとして扱われます。
正しくは次のように書きます。
C#class Parent
{
public virtual void Print(string message)
{
Console.WriteLine(message);
}
}
class Child : Parent
{
public override void Print(string message)
{
Console.WriteLine("Child: " + message);
}
}
戻り値の型も基本的に一致させる必要があります。
C#class Parent
{
public virtual int GetNumber()
{
return 1;
}
}
class Child : Parent
{
public override int GetNumber()
{
return 2;
}
}
このように、親クラスと子クラスでメソッドの形をそろえることが大切です。
9-4. アクセス修飾子が一致していない場合
overrideでは、アクセス修飾子も親クラス側と一致させる必要があります。
次のコードはエラーになります。
C#class Parent
{
public virtual void Show()
{
}
}
class Child : Parent
{
protected override void Show()
{
}
}
親クラスではpublicなのに、子クラスではprotectedになっています。
このように、アクセスレベルを勝手に変更することはできません。
正しくは次のように書きます。
C#class Parent
{
public virtual void Show()
{
}
}
class Child : Parent
{
public override void Show()
{
}
}
親クラスがprotected virtualなら、子クラスもprotected overrideにします。
C#class Parent
{
protected virtual void Show()
{
}
}
class Child : Parent
{
protected override void Show()
{
}
}
overrideできないときは、メソッド名や引数だけでなく、アクセス修飾子も確認しましょう。
9-5. newとoverrideを間違えた場合の対処法
親クラスのメソッドを上書きしたいのにnewを使ってしまうと、期待した動作にならないことがあります。
C#class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}
class Child : Parent
{
public new void Show()
{
Console.WriteLine("Child");
}
}
このコードでは、親クラスのShowはvirtualです。
しかし、子クラス側でoverrideではなくnewを使っているため、上書きではなく隠蔽になります。
C#Parent obj = new Child();
obj.Show();
実行結果は次のようになります。
Parent
親クラス型の変数で呼び出しているため、親クラスのShowが呼ばれます。
期待どおりに子クラスの処理を呼びたい場合は、newではなくoverrideを使います。
C#class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}
この場合、実行結果は次のようになります。
Child
親クラスの処理を変更したい場合はoverride、親クラスのメンバーを隠したい場合はnewと覚えておきましょう。
10. C#のvirtualに関するよくある質問
10-1. virtualは必ずoverrideしないといけない?
いいえ、virtualメソッドは必ずoverrideしなければならないわけではありません。
virtualは、「子クラスで上書きしてもよい」という意味です。
そのため、子クラスで上書きしない場合は、親クラスの処理がそのまま使われます。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Bird : Animal
{
}
この場合、BirdクラスはSpeakをoverrideしていません。
しかし、次のように呼び出すことができます。
C#Bird bird = new Bird();
bird.Speak();
実行結果は次のようになります。
動物が鳴きます
必ず実装させたい場合は、virtualではなくabstractを使います。
10-2. virtualとoverrideはセットで使う?
基本的には、親クラス側のvirtualと子クラス側のoverrideはセットで使うと考えてよいです。
親クラスで上書きを許可するにはvirtualを使います。
C#public virtual void Method()
{
}
子クラスで上書きするにはoverrideを使います。
C#public override void Method()
{
}
ただし、親クラス側がabstractメソッドの場合も、子クラス側ではoverrideを使います。
C#abstract class Parent
{
public abstract void Method();
}
class Child : Parent
{
public override void Method()
{
Console.WriteLine("Child");
}
}
つまり、overrideは、親クラスのvirtual、abstract、または既にoverrideされたメンバーを上書きするときに使います。
10-3. virtualはプロパティにも使える?
はい、virtualはプロパティにも使えます。
C#class Person
{
public virtual string Name
{
get { return "名前なし"; }
}
}
class Student : Person
{
public override string Name
{
get { return "学生"; }
}
}
使用例は次のとおりです。
C#Person person = new Student();
Console.WriteLine(person.Name);
実行結果は次のようになります。
学生
プロパティでもメソッドと同じように、親クラス側でvirtualを付け、子クラス側でoverrideします。
自動プロパティにもvirtualを付けられます。
C#class Person
{
public virtual string Name { get; set; } = "名前なし";
}
class Student : Person
{
public override string Name { get; set; } = "学生";
}
このように、virtualはメソッドだけでなく、プロパティの上書きにも使えます。
10-4. virtualは実務でよく使う?
はい、virtualは実務でも使われます。
特に、次のような場面で使われることがあります。
| 場面 | 例 |
|---|---|
| 共通処理を持つ基底クラスを作る | 基本サービスクラス、基本画面クラス |
| 一部の処理だけ差し替えたい | 通知方法、ログ出力、計算ロジック |
| フレームワークの拡張ポイントを作る | 派生クラスで動作を変更できるようにする |
| テストしやすくする | 特定の処理をモックに差し替える |
ただし、最近の設計では、何でも継承で解決するのではなく、interfaceや依存性注入を使うことも多いです。
そのため、virtualは便利ですが、乱用せず、継承による拡張が自然な場合に使うのがよいです。
10-5. virtualを使うメリット・デメリットは?
virtualを使うメリットは次のとおりです。
| メリット | 内容 |
|---|---|
| 子クラスで処理を変更できる | クラスごとに動作を変えられる |
| 共通処理を再利用できる | 親クラスに基本処理を書ける |
| ポリモーフィズムを実現できる | 親クラス型で子クラスの処理を呼べる |
| 拡張しやすい設計になる | 後から派生クラスを追加しやすい |
一方、デメリットもあります。
| デメリット | 内容 |
|---|---|
| 動作を追いにくくなる | 実際にどのメソッドが呼ばれるか確認が必要 |
| 設計が複雑になりやすい | 継承関係が深くなると理解しにくい |
| 予期しない上書きが起きる | 子クラスで処理を変えられるため |
| 保守が難しくなる場合がある | 親クラス変更時の影響範囲が広がる |
virtualは、拡張性を高める強力な仕組みです。
しかし、上書きされることを想定していないメソッドにまでvirtualを付けると、設計が不安定になります。
そのため、「この処理は子クラスで変更される可能性があるか」を考えて使うことが大切です。
まとめ
C#のvirtualは、親クラスのメソッドやプロパティを、子クラスで上書きできるようにするための修飾子です。
親クラス側にvirtualを付け、子クラス側でoverrideを使うことで、実際のオブジェクトの型に応じた処理を実行できます。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}
このように書くと、親クラス型の変数で扱っていても、実体がDogであればDogのSpeakメソッドが呼ばれます。
C#Animal animal = new Dog();
animal.Speak();
実行結果は次のようになります。
ワンワン
virtualとoverrideの違いは、次のように整理できます。
| 修飾子 | 役割 |
|---|---|
virtual | 親クラスで上書きを許可する |
override | 子クラスで親の処理を上書きする |
new | 上書きではなく親のメンバーを隠す |
abstract | 子クラスで必ず実装させる |
interface | 実装すべき機能の約束を定義する |
C#のvirtualを理解すると、継承、ポリモーフィズム、オブジェクト指向設計の理解が深まります。
初心者のうちは、まず次の3点を押さえておくとよいです。
1. virtualは親クラスに付ける
2. overrideは子クラスに付ける
3. virtualとoverrideを使うと、実体の型に応じた処理が呼ばれる
virtualは便利な機能ですが、何でも付ければよいわけではありません。
子クラスで処理を変更する必要がある場合や、共通処理を残しつつ一部だけ拡張したい場合に使うと、わかりやすく保守しやすいコードになります。

