C# overrideとは?virtualとの関係・使い方・newとの違いを初心者向けに徹底解説

はじめに

C#を学んでいると、クラスの継承と一緒によく登場するのがoverrideです。

overrideは、親クラスで定義されたメソッドやプロパティの動きを、子クラス側で上書きしたいときに使います。

たとえば、動物クラスに「鳴く」という処理があり、犬クラスでは「ワン」、猫クラスでは「ニャー」と表示したい場合、共通の処理名を使いながら、クラスごとに異なる動きを実現できます。

C#のoverrideを理解するには、virtualabstractnewsealedとの関係を押さえることが重要です。特に初心者がつまずきやすいのは、「なぜvirtualが必要なのか」「newと何が違うのか」「どんなときにエラーになるのか」という点です。

この記事では、C#のoverrideについて、基本から実践的な使い方、よくあるエラー、ベストプラクティスまで初心者向けにわかりやすく解説します。

1. C#のoverrideとは?初心者向けにわかりやすく解説

1-1. overrideは親クラスの処理を子クラスで上書きする仕組み

C#のoverrideとは、親クラスで定義されたメソッドやプロパティの処理を、子クラスで上書きするためのキーワードです。

たとえば、次のような親クラスがあるとします。

C#
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}

このAnimalクラスを継承したDogクラスで、Speakメソッドの内容を変更したい場合にoverrideを使います。

C#
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}

このように、親クラスのSpeakメソッドを、子クラスで別の処理に置き換えることができます。

C#
Animal animal = new Dog();
animal.Speak();

実行結果は次のようになります。

C#
ワンワン

変数の型はAnimalですが、実際に作られているインスタンスはDogなので、Dogクラスでoverrideされたメソッドが呼び出されます。

これがoverrideの大きな特徴です。

1-2. overrideが使われる代表的な場面

overrideは、親クラスで共通の処理やルールを用意し、子クラスごとに具体的な動きを変えたい場面で使われます。

代表的な例は次のようなケースです。

動物クラスを継承して、犬や猫ごとに鳴き声を変える場合。

C#
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("ニャー");
}
}

図形クラスを継承して、四角形や円ごとに面積の計算方法を変える場合。

C#
abstract class Shape
{
public abstract double GetArea();
}

class Rectangle : Shape
{
public override double GetArea()
{
return 10 * 5;
}
}

ゲームキャラクターの攻撃処理を、戦士、魔法使い、弓使いごとに変える場合。

C#
class Character
{
public virtual void Attack()
{
Console.WriteLine("通常攻撃");
}
}

class Wizard : Character
{
public override void Attack()
{
Console.WriteLine("魔法攻撃");
}
}

このようにoverrideは、「同じメソッド名で、クラスごとに異なる動作をさせたい」ときに非常に便利です。

1-3. overrideを理解するために必要な「継承」の基本

overrideは、C#の継承と深く関係しています。

継承とは、あるクラスの機能を別のクラスが引き継ぐ仕組みです。

C#
class Parent
{
public void Hello()
{
Console.WriteLine("こんにちは");
}
}

class Child : Parent
{
}

この場合、ChildクラスはParentクラスを継承しています。

C#
Child child = new Child();
child.Hello();

ChildクラスにはHelloメソッドを書いていませんが、親クラスから引き継いでいるため呼び出すことができます。

overrideは、この継承したメソッドの動作を子クラス側で変更するための仕組みです。

ただし、すべてのメソッドを自由にoverrideできるわけではありません。親クラス側で「このメソッドは上書きしてよい」と明示されている必要があります。そのために使うのがvirtualabstractです。

1-4. overrideを使うメリット

overrideを使うメリットは、コードの拡張性と再利用性を高められることです。

たとえば、親クラスに共通の構造を用意しておけば、子クラスでは違いのある部分だけを実装できます。

C#
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}

犬や猫などの子クラスでは、鳴き声の部分だけを変更できます。

C#
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}

class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("ニャー");
}
}

さらに、親クラス型の配列やリストでまとめて扱うこともできます。

C#
List<Animal> animals = new List<Animal>
{
new Dog(),
new Cat()
};

foreach (Animal animal in animals)
{
animal.Speak();
}

実行結果は次のようになります。

C#
ワンワン
ニャー

このように、呼び出す側はSpeakという共通のメソッドを呼ぶだけで、実際のクラスに応じた処理を実行できます。

これをポリモーフィズム、多態性と呼びます。overrideはC#でポリモーフィズムを実現する重要な仕組みです。

2. C#でoverrideを使うための基本ルール

2-1. 親クラス側にはvirtual・abstract・overrideのいずれかが必要

C#でoverrideを使うには、親クラス側のメソッドがvirtualabstract、またはoverrideとして定義されている必要があります。

たとえば、次のように親クラス側にvirtualが付いていれば、子クラスでoverrideできます。

C#
class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

一方、親クラス側にvirtualがない場合はoverrideできません。

C#
class Parent
{
public void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

このコードはコンパイルエラーになります。

overrideは「上書きしてよい」と宣言されたメンバーに対してのみ使えるためです。

2-2. 子クラス側ではoverrideキーワードを付ける

親クラス側でvirtualabstractが付いているメソッドを子クラスで上書きする場合、子クラス側では必ずoverrideキーワードを付けます。

C#
class Parent
{
public virtual void Run()
{
Console.WriteLine("親クラスのRun");
}
}

class Child : Parent
{
public override void Run()
{
Console.WriteLine("子クラスのRun");
}
}

overrideを付けることで、「このメソッドは親クラスのメソッドを上書きしています」とコンパイラに明示できます。

これにより、メソッド名の入力ミスや引数の違いなどをコンパイル時に検出しやすくなります。

2-3. メソッド名・戻り値・引数は基本的に一致させる

overrideするメソッドは、親クラスのメソッドとシグネチャを一致させる必要があります。

シグネチャとは、主に次のような情報です。

メソッド名、引数の数、引数の型、引数の順番などです。

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);
}
}

これは正しい例です。

一方、引数の型が違うとoverrideにはなりません。

C#
class Child : Parent
{
public override void Print(int message)
{
Console.WriteLine(message);
}
}

この場合、親クラスにPrint(int message)という上書き対象がないためエラーになります。

なお、C#のバージョンや戻り値の型によっては共変戻り値が使えるケースもありますが、初心者のうちは「戻り値も含めて基本的に一致させる」と覚えておくと安全です。

2-4. privateメソッドはoverrideできない

privateメソッドは子クラスから見えないため、overrideできません。

C#
class Parent
{
private virtual void Show()
{
Console.WriteLine("Parent");
}
}

そもそもC#では、private virtualのような指定は基本的に使えません。

overrideは継承先で上書きするための仕組みなので、子クラスからアクセスできないprivateメンバーとは相性がありません。

overrideしたい場合は、publicprotectedinternalなど、継承先から利用できるアクセス修飾子を使います。

C#
class Parent
{
protected virtual void Show()
{
Console.WriteLine("Parent");
}
}

protectedにすると、外部からは呼び出せませんが、子クラスからは利用できます。

2-5. staticメソッドはoverrideできない

staticメソッドはクラス自体に属するメソッドであり、インスタンスごとに動作を変えるものではありません。

そのため、staticメソッドをoverrideすることはできません。

C#
class Parent
{
public static void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override static void Show()
{
Console.WriteLine("Child");
}
}

このようなコードはエラーになります。

overrideは、インスタンスの実際の型に応じて呼び出すメソッドを切り替える仕組みです。一方、staticメソッドはクラス名を使って呼び出すため、overrideの対象にはなりません。

C#
Parent.Show();
Child.Show();

staticメソッドで似た名前のメソッドを定義することはできますが、それはoverrideではなく隠蔽に近い扱いになります。

3. overrideとvirtualの関係

3-1. virtualとは何か

virtualは、親クラス側で「このメソッドは子クラスで上書きできます」と宣言するためのキーワードです。

C#
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}

このようにvirtualを付けたメソッドは、子クラスでoverrideできます。

C#
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}

つまり、virtualoverrideはセットで理解するとわかりやすいです。

親クラスではvirtualを付けて上書き可能にする。子クラスではoverrideを付けて実際に上書きする。この流れが基本です。

3-2. virtualメソッドをoverrideする流れ

virtualメソッドをoverrideする流れは、次のようになります。

まず、親クラスを作成します。

C#
class Vehicle
{
public virtual void Move()
{
Console.WriteLine("乗り物が移動します");
}
}

次に、子クラスで親クラスを継承します。

C#
class Car : Vehicle
{
}

そして、親クラスのMoveメソッドを子クラスで上書きします。

C#
class Car : Vehicle
{
public override void Move()
{
Console.WriteLine("車が走ります");
}
}

最後に、インスタンスを作成して呼び出します。

C#
Car car = new Car();
car.Move();

実行結果は次のようになります。

C#
車が走ります

3-3. virtualを付けないとoverrideできない理由

C#では、親クラスのメソッドにvirtualを付けない限り、子クラスでoverrideできません。

これは、親クラスの設計者が「このメソッドは上書きされることを想定している」と明示するためです。

もしすべてのメソッドが自由に上書きできると、親クラスの意図しない動作変更が起こりやすくなります。

たとえば、親クラスで重要な検証処理を行っているメソッドが、子クラスで勝手に別の処理に置き換えられると、プログラム全体の安全性が崩れる可能性があります。

そのためC#では、上書き可能なメソッドだけにvirtualを付ける設計になっています。

C#
class Parent
{
public void NormalMethod()
{
Console.WriteLine("通常のメソッド");
}

public virtual void VirtualMethod()
{
Console.WriteLine("上書き可能なメソッド");
}
}

この場合、NormalMethodoverrideできませんが、VirtualMethodoverrideできます。

3-4. virtualとoverrideの基本サンプルコード

virtualoverrideの基本的なコードは次のとおりです。

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 Animal();
animal.Speak();

Dog dog = new Dog();
dog.Speak();

Animal dogAsAnimal = new Dog();
dogAsAnimal.Speak();
}
}

実行結果は次のようになります。

C#
動物が鳴きます
ワンワン
ワンワン

注目すべき点は、次の部分です。

C#
Animal dogAsAnimal = new Dog();
dogAsAnimal.Speak();

変数の型はAnimalですが、実体はDogです。そのため、DogクラスでoverrideされたSpeakメソッドが呼び出されます。

3-5. overrideしたメソッドが呼び出される仕組み

overrideされたメソッドは、変数の型ではなく、実際のインスタンスの型に応じて呼び出されます。

C#
Animal animal = new Dog();
animal.Speak();

このコードでは、変数の型はAnimalです。しかし、実際に入っているオブジェクトはDogです。

そのため、実行時にはDogSpeakメソッドが呼ばれます。

この仕組みを動的ディスパッチと呼びます。

動的ディスパッチにより、呼び出し側は具体的な子クラスを意識せず、共通の親クラス型として扱うことができます。

C#
List<Animal> animals = new List<Animal>
{
new Dog(),
new Cat()
};

foreach (Animal animal in animals)
{
animal.Speak();
}

このようなコードでは、Dogなら犬の鳴き声、Catなら猫の鳴き声が呼び出されます。

これがoverrideを使う大きな理由です。

4. C# overrideの基本的な使い方

4-1. 親クラスを作成する

まずは、親クラスを作成します。

ここでは、従業員を表すEmployeeクラスを例にします。

C#
class Employee
{
public string Name { get; set; }

public Employee(string name)
{
Name = name;
}
}

このクラスは、従業員の名前を保持するだけのシンプルなクラスです。

次に、この親クラスに、後から子クラスで上書きしたいメソッドを追加します。

4-2. virtualメソッドを定義する

親クラス側にvirtualメソッドを定義します。

C#
class Employee
{
public string Name { get; set; }

public Employee(string name)
{
Name = name;
}

public virtual void Work()
{
Console.WriteLine($"{Name}は仕事をしています");
}
}

Workメソッドにvirtualを付けることで、子クラス側でoverrideできるようになります。

この時点では、Employeeクラスの基本的な仕事の処理を定義している状態です。

4-3. 子クラスでoverrideする

次に、Employeeクラスを継承したEngineerクラスを作成します。

C#
class Engineer : Employee
{
public Engineer(string name) : base(name)
{
}

public override void Work()
{
Console.WriteLine($"{Name}はプログラムを書いています");
}
}

Engineerクラスでは、親クラスのWorkメソッドをoverrideしています。

これにより、EngineerのインスタンスでWorkを呼び出すと、親クラスの処理ではなく、子クラスで定義した処理が実行されます。

4-4. インスタンスを作成して動作を確認する

実際にインスタンスを作成して動作を確認してみます。

C#
using System;

class Employee
{
public string Name { get; set; }

public Employee(string name)
{
Name = name;
}

public virtual void Work()
{
Console.WriteLine($"{Name}は仕事をしています");
}
}

class Engineer : Employee
{
public Engineer(string name) : base(name)
{
}

public override void Work()
{
Console.WriteLine($"{Name}はプログラムを書いています");
}
}

class Program
{
static void Main()
{
Employee employee = new Employee("田中");
employee.Work();

Engineer engineer = new Engineer("佐藤");
engineer.Work();

Employee engineerAsEmployee = new Engineer("鈴木");
engineerAsEmployee.Work();
}
}

実行結果は次のようになります。

C#
田中は仕事をしています
佐藤はプログラムを書いています
鈴木はプログラムを書いています

最後の例では、変数の型はEmployeeですが、実体はEngineerです。そのため、EngineerクラスでoverrideされたWorkメソッドが呼び出されます。

4-5. baseキーワードで親クラスの処理を呼び出す方法

overrideしたメソッドの中で、親クラスの処理も呼び出したい場合はbaseキーワードを使います。

C#
class Engineer : Employee
{
public Engineer(string name) : base(name)
{
}

public override void Work()
{
base.Work();
Console.WriteLine($"{Name}はプログラムを書いています");
}
}

このように書くと、まず親クラスのWorkメソッドが実行され、その後に子クラス独自の処理が実行されます。

C#
Engineer engineer = new Engineer("佐藤");
engineer.Work();

実行結果は次のようになります。

C#
佐藤は仕事をしています
佐藤はプログラムを書いています

baseは、親クラスに共通処理がある場合に便利です。

たとえば、ログ出力、入力チェック、初期化処理などを親クラス側に置いておき、子クラスでは追加処理だけを書くことができます。

ただし、常にbaseを呼べばよいわけではありません。親クラスの処理を完全に置き換えたい場合は、baseを呼ばない設計もあります。

5. overrideとnewの違い

5-1. newは親クラスのメンバーを隠す仕組み

C#には、overrideと似た場面で登場するnewというキーワードがあります。

newは、親クラスのメンバーを上書きするのではなく、子クラス側で同じ名前のメンバーを定義して、親クラスのメンバーを隠す仕組みです。

C#
class Parent
{
public void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public new void Show()
{
Console.WriteLine("Child");
}
}

このコードでは、ChildクラスにShowメソッドがありますが、これは親クラスのShowoverrideしているわけではありません。

子クラス側で同じ名前のメソッドを新しく定義し、親クラスのメソッドを隠しているだけです。

5-2. overrideは親クラスのメソッドを上書きする仕組み

一方、overrideは、親クラスでvirtualとして定義されたメソッドを子クラスで上書きします。

C#
class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

この場合、ChildクラスのShowメソッドは、親クラスのShowメソッドを正式に上書きしています。

そのため、親クラス型の変数に子クラスのインスタンスを入れて呼び出しても、子クラスのメソッドが実行されます。

5-3. overrideとnewの動作の違いをコードで比較

まず、overrideの例を見てみます。

C#
class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

class Program
{
static void Main()
{
Parent obj = new Child();
obj.Show();
}
}

実行結果は次のようになります。

C#
Child

変数の型はParentですが、実体がChildなので、ChildShowが呼び出されます。

次に、newの例です。

C#
class Parent
{
public void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public new void Show()
{
Console.WriteLine("Child");
}
}

class Program
{
static void Main()
{
Parent obj = new Child();
obj.Show();
}
}

実行結果は次のようになります。

C#
Parent

実体はChildですが、変数の型がParentなので、ParentShowが呼び出されます。

この違いは非常に重要です。

5-4. 親クラス型の変数で呼び出したときの違い

overridenewの最大の違いは、親クラス型の変数で呼び出したときの動作です。

overrideの場合は、実際のインスタンスの型に基づいてメソッドが呼び出されます。

C#
Parent obj = new Child();
obj.Show(); // Child

一方、newの場合は、変数の型に基づいてメソッドが呼び出されます。

C#
Parent obj = new Child();
obj.Show(); // Parent

また、子クラス型の変数で呼び出した場合は、newでも子クラス側のメソッドが呼び出されます。

C#
Child child = new Child();
child.Show(); // Child

つまり、newは「どの型の変数として扱っているか」によって呼び出されるメソッドが変わります。

この動作は初心者にとってわかりにくく、バグの原因になりやすいです。

5-5. 基本的にはnewよりoverrideを使うべき理由

継承したメソッドの動きを子クラスで変えたい場合、基本的にはnewではなくoverrideを使うべきです。

理由は、overrideのほうがポリモーフィズムを正しく活用できるからです。

C#
List<Parent> list = new List<Parent>
{
new Child(),
new Child()
};

foreach (Parent item in list)
{
item.Show();
}

このように親クラス型でまとめて扱う設計では、overrideを使っていれば子クラスの処理が正しく呼び出されます。

一方、newを使うと、親クラス型として扱ったときに親クラスのメソッドが呼び出されるため、期待した動作にならないことがあります。

newは、親クラスのメンバーを意図的に隠したい特殊な場面で使います。通常の継承設計では、overrideを選ぶほうが自然です。

6. overrideとabstract・sealedの関係

6-1. abstractメソッドは必ずoverrideが必要

abstractメソッドは、親クラス側では処理内容を書かず、子クラスで必ず実装させるためのメソッドです。

C#
abstract class Animal
{
public abstract void Speak();
}

abstractメソッドには本体がありません。

C#
public abstract void Speak();

このAnimalクラスを継承する通常のクラスは、Speakメソッドを必ずoverrideしなければなりません。

C#
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}

abstractメソッドは、子クラスに実装を強制したい場合に使います。

6-2. abstractクラスでoverrideを使う例

図形クラスを例にして、abstractoverrideの関係を見てみましょう。

C#
using System;

abstract class Shape
{
public abstract double GetArea();
}

class Circle : Shape
{
public double Radius { get; }

public Circle(double radius)
{
Radius = radius;
}

public override double GetArea()
{
return Radius * Radius * Math.PI;
}
}

class Rectangle : Shape
{
public double Width { get; }
public double Height { get; }

public Rectangle(double width, double height)
{
Width = width;
Height = height;
}

public override double GetArea()
{
return Width * Height;
}
}

class Program
{
static void Main()
{
Shape circle = new Circle(3);
Shape rectangle = new Rectangle(4, 5);

Console.WriteLine(circle.GetArea());
Console.WriteLine(rectangle.GetArea());
}
}

Shapeクラスでは、面積を求めるGetAreaメソッドだけを定義しています。

しかし、円と四角形では面積の計算方法が違うため、具体的な計算は子クラスに任せています。

このように、共通のルールだけを親クラスで定義し、具体的な処理を子クラスで実装したい場合にabstractoverrideを組み合わせます。

6-3. sealed overrideでそれ以上の上書きを禁止する

overrideしたメソッドは、さらに派生したクラスで再びoverrideできます。

C#
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}

class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}

class ShibaInu : Dog
{
public override void Speak()
{
Console.WriteLine("柴犬がワンと鳴きます");
}
}

しかし、これ以上上書きされたくない場合はsealed overrideを使います。

C#
class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("ワンワン");
}
}

このようにすると、Dogより下のクラスではSpeakメソッドをoverrideできなくなります。

C#
class ShibaInu : Dog
{
public override void Speak()
{
Console.WriteLine("柴犬がワンと鳴きます");
}
}

このコードはコンパイルエラーになります。

sealed overrideは、「ここで処理を確定させたい」「これ以上変更されると困る」という場面で使います。

6-4. virtual・abstract・sealedの使い分け

virtualabstractsealedは、継承設計でよく使われるキーワードです。

virtualは、親クラスに標準の処理を用意しつつ、必要なら子クラスで上書きできるようにしたい場合に使います。

C#
public virtual void Execute()
{
Console.WriteLine("標準処理");
}

abstractは、親クラスでは処理を書かず、子クラスに必ず実装させたい場合に使います。

C#
public abstract void Execute();

sealed overrideは、一度overrideしたメソッドを、それ以上派生クラスで上書きさせたくない場合に使います。

C#
public sealed override void Execute()
{
Console.WriteLine("確定した処理");
}

使い分けの目安は次のとおりです。

標準の処理を用意したいならvirtual

子クラスごとに必ず処理を書かせたいならabstract

それ以上変更されたくないならsealed override

この3つを理解すると、C#の継承設計がかなりわかりやすくなります。

7. overrideでよくあるエラーと対処法

7-1. 「オーバーライドする適切なメソッドが見つかりません」の原因

C#でoverrideを使ったとき、次のようなエラーが出ることがあります。

C#
オーバーライドする適切なメソッドが見つかりません

これは、子クラスでoverrideしようとしているメソッドに対して、親クラス側に対応するメソッドが見つからないときに発生します。

主な原因は次のようなものです。

親クラスのメソッドにvirtualabstractが付いていない。

メソッド名が間違っている。

引数の型や数が一致していない。

戻り値の型が一致していない。

アクセス修飾子が一致していない。

privatestaticのメソッドをoverrideしようとしている。

このエラーが出た場合は、まず親クラス側のメソッド定義を確認しましょう。

7-2. virtualを付け忘れているケース

最もよくある原因が、親クラス側にvirtualを付け忘れているケースです。

C#
class Parent
{
public void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

このコードはエラーになります。

親クラスのShowメソッドがvirtualではないためです。

正しくは次のように書きます。

C#
class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

overrideしたいメソッドには、親クラス側でvirtualabstract、またはoverrideが必要です。

7-3. メソッドのシグネチャが一致していないケース

親クラスと子クラスで、メソッドのシグネチャが一致していない場合もエラーになります。

C#
class Parent
{
public virtual void Print(string text)
{
Console.WriteLine(text);
}
}

class Child : Parent
{
public override void Print(int text)
{
Console.WriteLine(text);
}
}

親クラスではstring型の引数を受け取っていますが、子クラスではint型になっています。

この場合、上書き対象のメソッドが見つからないためエラーになります。

正しくは次のようにします。

C#
class Child : Parent
{
public override void Print(string text)
{
Console.WriteLine("Child: " + text);
}
}

引数の数、型、順番が親クラスと一致しているかを確認しましょう。

7-4. アクセス修飾子が間違っているケース

overrideする場合、アクセス修飾子も基本的に親クラス側と合わせる必要があります。

C#
class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
protected override void Show()
{
Console.WriteLine("Child");
}
}

親クラスではpublicなのに、子クラスではprotectedになっています。

このようにアクセス範囲を変えることはできないため、エラーになります。

正しくは次のようにします。

C#
class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

親クラスのメソッドがprotected virtualなら、子クラス側もprotected overrideにします。

C#
class Parent
{
protected virtual void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
protected override void Show()
{
Console.WriteLine("Child");
}
}

7-5. staticやprivateをoverrideしようとしているケース

staticメソッドやprivateメソッドはoverrideできません。

C#
class Parent
{
public static void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

staticメソッドはインスタンスに属さないため、overrideの対象にはなりません。

また、privateメソッドは子クラスから見えないため、overrideできません。

C#
class Parent
{
private void Show()
{
Console.WriteLine("Parent");
}
}

overrideしたい場合は、親クラス側をpublic virtualprotected virtualにする必要があります。

C#
class Parent
{
protected virtual void Show()
{
Console.WriteLine("Parent");
}
}

エラーが出たときは、まず「親クラス側が本当に上書き可能なメソッドになっているか」を確認しましょう。

8. overrideを使う実践的なサンプル

8-1. ToStringメソッドをoverrideする例

C#では、すべてのクラスは最終的にobjectクラスを継承しています。

objectクラスにはToStringメソッドがあり、これはoverrideできます。

C#
class Person
{
public string Name { get; }
public int Age { get; }

public Person(string name, int age)
{
Name = name;
Age = age;
}

public override string ToString()
{
return $"{Name}さん({Age}歳)";
}
}

使い方は次のとおりです。

C#
Person person = new Person("田中", 30);
Console.WriteLine(person);

実行結果は次のようになります。

C#
田中さん(30歳)

ToStringoverrideすると、オブジェクトを文字列として表示するときに、わかりやすい形式で出力できます。

デバッグやログ出力でも便利です。

8-2. Equalsメソッドをoverrideする例

Equalsメソッドをoverrideすると、オブジェクト同士が等しいかどうかの判定方法を自分で定義できます。

C#
class Product
{
public int Id { get; }
public string Name { get; }

public Product(int id, string name)
{
Id = id;
Name = name;
}

public override bool Equals(object? obj)
{
if (obj is not Product other)
{
return false;
}

return Id == other.Id;
}

public override int GetHashCode()
{
return Id.GetHashCode();
}
}

使い方は次のとおりです。

C#
Product product1 = new Product(1, "ノートPC");
Product product2 = new Product(1, "ノートPC");

Console.WriteLine(product1.Equals(product2));

実行結果は次のようになります。

C#
True

Equalsoverrideする場合は、基本的にGetHashCodeも一緒にoverrideします。

これは、DictionaryHashSetなどのコレクションで正しく動作させるために重要です。

8-3. ゲームキャラクターの攻撃処理をoverrideする例

ゲーム開発では、キャラクターごとに異なる攻撃処理を実装する場面があります。

C#
class Character
{
public string Name { get; }

public Character(string name)
{
Name = name;
}

public virtual void Attack()
{
Console.WriteLine($"{Name}は通常攻撃をした");
}
}

class Warrior : Character
{
public Warrior(string name) : base(name)
{
}

public override void Attack()
{
Console.WriteLine($"{Name}は剣で攻撃した");
}
}

class Mage : Character
{
public Mage(string name) : base(name)
{
}

public override void Attack()
{
Console.WriteLine($"{Name}は魔法で攻撃した");
}
}

実行例です。

C#
List<Character> characters = new List<Character>
{
new Warrior("アレン"),
new Mage("ミリア")
};

foreach (Character character in characters)
{
character.Attack();
}

実行結果は次のようになります。

C#
アレンは剣で攻撃した
ミリアは魔法で攻撃した

呼び出し側はAttackを呼ぶだけで、実際のキャラクターに応じた攻撃処理が実行されます。

8-4. 図形クラスの面積計算をoverrideする例

図形ごとに面積の計算方法が違う場合も、overrideが役立ちます。

C#
abstract class Shape
{
public abstract double GetArea();
}

class Circle : Shape
{
public double Radius { get; }

public Circle(double radius)
{
Radius = radius;
}

public override double GetArea()
{
return Radius * Radius * Math.PI;
}
}

class Square : Shape
{
public double Side { get; }

public Square(double side)
{
Side = side;
}

public override double GetArea()
{
return Side * Side;
}
}

使い方は次のとおりです。

C#
List<Shape> shapes = new List<Shape>
{
new Circle(3),
new Square(4)
};

foreach (Shape shape in shapes)
{
Console.WriteLine(shape.GetArea());
}

Shape型としてまとめて扱いながら、円なら円の面積、正方形なら正方形の面積を計算できます。

このように、共通の操作名を用意し、具体的な処理を子クラスに任せる設計は、実務でもよく使われます。

8-5. 実務でoverrideが使われやすい設計パターン

実務では、overrideは次のような場面でよく使われます。

まず、フレームワークが用意した基底クラスのメソッドを上書きする場面です。

たとえば、画面表示、初期化処理、イベント処理などで、決められたメソッドをoverrideして独自の動作を追加することがあります。

次に、テンプレートメソッドパターンです。

親クラスで処理の流れを定義し、一部の処理だけを子クラスで差し替える設計です。

C#
abstract class ReportGenerator
{
public void Generate()
{
LoadData();
FormatData();
Output();
}

protected virtual void LoadData()
{
Console.WriteLine("データを読み込みます");
}

protected abstract void FormatData();

protected virtual void Output()
{
Console.WriteLine("レポートを出力します");
}
}

class CsvReportGenerator : ReportGenerator
{
protected override void FormatData()
{
Console.WriteLine("CSV形式に変換します");
}
}

この例では、Generateメソッドで全体の流れを固定し、FormatDataだけを子クラスで実装しています。

また、テストしやすい設計を作るために、特定の処理をvirtualにして差し替え可能にすることもあります。

ただし、何でもvirtualにすればよいわけではありません。上書きされることを想定したメソッドだけをvirtualにすることが大切です。

9. overrideを使うときの注意点とベストプラクティス

9-1. override前提のメソッドだけvirtualにする

virtualを付けると、そのメソッドは子クラスでoverrideできるようになります。

便利な一方で、むやみにvirtualを付けると、クラスの動作が予想しにくくなることがあります。

C#
class Order
{
public virtual void Validate()
{
Console.WriteLine("注文内容を検証します");
}
}

このようにValidatevirtualにすると、子クラスで検証処理を変更できます。

しかし、重要な検証処理が子クラスで消されてしまうと、不正なデータが通る可能性もあります。

そのため、virtualを付けるのは「子クラスで上書きされることを前提に設計したメソッド」だけにしましょう。

9-2. 親クラスの設計を壊さないようにする

overrideは親クラスの処理を変更できる強力な仕組みです。

そのため、子クラスで親クラスの前提を壊さないように注意する必要があります。

たとえば、親クラスのメソッドが「必ずログを出力する」「必ず状態を更新する」「必ず例外チェックを行う」といった役割を持っている場合、子クラスでそれを無視すると不具合の原因になります。

C#
class BaseProcessor
{
public virtual void Process()
{
Console.WriteLine("ログ出力");
Console.WriteLine("処理実行");
}
}

class CustomProcessor : BaseProcessor
{
public override void Process()
{
Console.WriteLine("独自処理");
}
}

この例では、親クラスのログ出力が失われています。

必要であれば、base.Process()を呼び出して親クラスの処理を残します。

C#
class CustomProcessor : BaseProcessor
{
public override void Process()
{
base.Process();
Console.WriteLine("追加の独自処理");
}
}

9-3. base呼び出しが必要か確認する

overrideしたメソッドの中でbaseを呼ぶべきかどうかは、親クラスの設計によって変わります。

親クラスの処理を残したい場合は、baseを呼びます。

C#
public override void Save()
{
base.Save();
Console.WriteLine("追加の保存処理");
}

一方、親クラスの処理を完全に置き換えたい場合は、baseを呼ばないこともあります。

C#
public override void Save()
{
Console.WriteLine("独自の保存処理");
}

大切なのは、何となくbaseを呼ぶのではなく、「親クラスの処理が必要か」を確認することです。

特にフレームワークのメソッドをoverrideする場合は、ドキュメントでbase呼び出しが必要か確認しましょう。

9-4. newによる隠蔽は意図がある場合だけ使う

newを使うと、親クラスのメンバーを隠すことができます。

しかし、overrideと違ってポリモーフィズムの対象にはなりません。

C#
class Parent
{
public void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public new void Show()
{
Console.WriteLine("Child");
}
}

この場合、次のように親クラス型で扱うと、親クラスのメソッドが呼び出されます。

C#
Parent obj = new Child();
obj.Show(); // Parent

この動作はわかりにくいため、意図せずnewを使うのは避けるべきです。

親クラスのメソッドを本当に上書きしたい場合は、親クラス側をvirtualにし、子クラス側でoverrideします。

newは、既存の親クラスとの互換性を保ちつつ、子クラス側で同名メンバーを定義したい場合など、明確な理由があるときだけ使いましょう。

9-5. 可読性を高めるために役割を明確にする

overrideを使うときは、親クラスと子クラスの役割を明確にすることが大切です。

親クラスには共通のルールや基本処理を書き、子クラスには具体的な違いを書くようにします。

C#
abstract class Notification
{
public abstract void Send(string message);
}

class EmailNotification : Notification
{
public override void Send(string message)
{
Console.WriteLine("メール送信: " + message);
}
}

class SmsNotification : Notification
{
public override void Send(string message)
{
Console.WriteLine("SMS送信: " + message);
}
}

このように設計すると、コードを読んだ人が「通知方法ごとにSendの処理が違う」と理解しやすくなります。

逆に、親クラスと子クラスの責務があいまいなままoverrideを多用すると、どのクラスで何をしているのかわかりにくくなります。

overrideは便利ですが、設計の意図が伝わるように使うことが重要です。

10. C# overrideに関するよくある質問

10-1. overrideとオーバーロードの違いは?

overrideとオーバーロードは名前が似ていますが、意味はまったく違います。

overrideは、親クラスのメソッドを子クラスで上書きすることです。

C#
class Parent
{
public virtual void Show()
{
Console.WriteLine("Parent");
}
}

class Child : Parent
{
public override void Show()
{
Console.WriteLine("Child");
}
}

一方、オーバーロードは、同じクラス内で同じメソッド名を使い、引数の数や型を変えて複数定義することです。

C#
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}

public double Add(double a, double b)
{
return a + b;
}

public int Add(int a, int b, int c)
{
return a + b + c;
}
}

overrideは継承に関係します。

オーバーロードは、同じ名前のメソッドを引数違いで複数作る仕組みです。

10-2. overrideとinterface実装の違いは?

overrideは、親クラスのvirtualメソッドやabstractメソッドを上書きする仕組みです。

一方、interface実装は、インターフェースで定義されたメンバーをクラスで実装する仕組みです。

C#
interface IAnimal
{
void Speak();
}

class Dog : IAnimal
{
public void Speak()
{
Console.WriteLine("ワンワン");
}
}

この場合、Speakには通常overrideを付けません。

なぜなら、インターフェースのメソッドを実装しているだけで、親クラスのメソッドを上書きしているわけではないからです。

ただし、抽象クラスのメソッドを実装する場合はoverrideを使います。

C#
abstract class Animal
{
public abstract void Speak();
}

class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}

interfaceは「この機能を持つ」という約束を定義するもの、overrideは「親クラスの処理を上書きするもの」と考えると理解しやすいです。

10-3. overrideしたメソッドをさらにoverrideできる?

はい、できます。

overrideしたメソッドは、さらに派生したクラスで再びoverrideできます。

C#
class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}

class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("ワンワン");
}
}

class RobotDog : Dog
{
public override void Speak()
{
Console.WriteLine("電子音でワンワン");
}
}

このように、AnimalからDog、さらにRobotDogへと継承した場合でも、メソッドを上書きできます。

ただし、途中でsealed overrideが使われている場合は、それ以上overrideできません。

C#
class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("ワンワン");
}
}

この場合、Dogを継承したクラスではSpeakoverrideできません。

10-4. コンストラクタはoverrideできる?

コンストラクタはoverrideできません。

コンストラクタはクラスのインスタンスを初期化するための特別なメソッドであり、通常のメソッドのように継承して上書きするものではありません。

ただし、子クラスのコンストラクタから親クラスのコンストラクタを呼び出すことはできます。

C#
class Parent
{
public Parent(string name)
{
Console.WriteLine($"Parent: {name}");
}
}

class Child : Parent
{
public Child(string name) : base(name)
{
Console.WriteLine($"Child: {name}");
}
}

このように、: base(name)を使うことで親クラスのコンストラクタを呼び出せます。

overrideするのではなく、baseを使って親クラスの初期化処理を実行すると覚えておきましょう。

10-5. overrideはいつ使うべき?

overrideは、親クラスで定義された共通のメソッドを、子クラスごとに異なる動きにしたいときに使います。

たとえば、次のような場合です。

動物ごとに鳴き声を変えたい。

図形ごとに面積の計算方法を変えたい。

ゲームキャラクターごとに攻撃方法を変えたい。

通知方法ごとに送信処理を変えたい。

フレームワークが用意したメソッドに独自処理を追加したい。

重要なのは、「同じ操作として扱いたいが、具体的な処理はクラスごとに変えたい」という場面です。

C#
Animal animal = new Dog();
animal.Speak();

このように親クラス型で扱いながら、実際の子クラスに応じた処理を実行したい場合にoverrideが役立ちます。

反対に、単に似た名前のメソッドを作りたいだけなら、overrideではなく通常のメソッドやオーバーロードで十分な場合もあります。

まとめ

C#のoverrideは、親クラスで定義されたメソッドやプロパティを、子クラスで上書きするためのキーワードです。

親クラス側ではvirtualabstractを使って上書き可能なメソッドを定義し、子クラス側ではoverrideを使って具体的な処理を書きます。

基本的な関係は次のように考えるとわかりやすいです。

virtualは、子クラスで上書きしてもよいメソッド。

abstractは、子クラスで必ず実装する必要があるメソッド。

overrideは、親クラスのメソッドを子クラスで上書きするキーワード。

newは、親クラスのメンバーを上書きするのではなく隠すキーワード。

sealed overrideは、それ以上の上書きを禁止する書き方です。

特にoverridenewの違いは重要です。

overrideは、親クラス型の変数で呼び出しても、実際の子クラスのメソッドが実行されます。

一方、newは変数の型によって呼び出されるメソッドが変わるため、意図しない動作になりやすいです。

そのため、親クラスのメソッドを子クラスで正しく上書きしたい場合は、基本的にoverrideを使います。

C# overrideを理解すると、継承、ポリモーフィズム、抽象クラス、フレームワーク設計などが一気に理解しやすくなります。まずはvirtualoverrideの基本サンプルを実際に書いて動かし、親クラス型で呼び出したときの動作を確認してみましょう。