thisってどういう意味?
継承やオーバーロードがよく分からない…
Classについて学習するうえで最初に覚えなくてはならない知識の一つが、コンストラクタです。コンストラクタは正しい処理で動作させるうえでも大切な役割を持っています。
こんにちは、現役エンジニア5年目の遠藤です!
今回の記事では、C#におけるコンストラクタについて解説致します。
この記事は以下のような方に向けて書きました
- コンストラクタ自体がよく分からない方
- コンストラクタの書き方が分からない方
- オーバーロードや継承などが分からない方
- デストラクタについて知りたい方
この記事を読んでいただければ、コンストラクタについての基礎知識をある程度網羅できるかと思います。
是非最後までお付き合いください!
コンストラクタとは
クラスの中に変数等を使う場合、初期値を指定しておきたいケースなどがあります。そんな時に使うメソッドが、コンストラクタです。
コンストラクタはインスタンス(クラスから生成される実体)が生成された時点で最初に実行される、初期化用メソッドの事を言います。
コンストラクタにコードを書くと、インスタンス生成時に自動的に記述した処理が実行されます。
コンストラクタの書き方
基本形
まずはコンストラクタの基本的な書き方を紹介致します。コンストラクタは以下のように記述しましょう。
using System;
namespace SampleApplication1
{
class Class1 //クラス
{
public Class1() //これがコンストラクタ
{
Console.WriteLine("Class1 Constructor()");
}
public static void Main()
{
Class1 cls1 = new Class1(); //インスタンスを生成
}
}
}
ポイントは、クラス名と同じ名前のメソッドであるという点です。
実行結果:
Class1 Constructor()
このように、メソッドの呼び出しをせずとも自動的に呼び出されます。
引数を使用した形
続いて、初期化する値をインスタンス生成時に指定したい場合の方法を紹介します。
値を指定したい場合は、コンストラクタに引数を追加します。
サンプルコード:
using System;
namespace SampleApplication1
{
class Class1 //クラス
{
public string message; //変数messageを宣言
public Class1(string message) //引数付きのコンストラクタ
{
this.message = message; //変数messageを与えられた値で初期化
Console.WriteLine("Class1 Constructor() message: " + this.message);
}
public static void Main()
{
Class1 cls1 = new Class1("Hello World!"); //コンストラクタに"Hello World!"という値を与える
}
}
}
実行結果:
Class1 Constructor() message: Hello World!
この時出てくるthisは、インスタンス自身を表します。上記の例のthis.nameは、「このインスタンスにおける変数name」という意味になります。
オーバーロード
C#には、オーバーロードという機能があります。これは、1つのクラス内に引数の異なる同じ名前のメソッドを扱うことができる機能です。
引数が1つの場合はこちらを使い、二つの場合これを使う。といったような使い方が可能です。
コンストラクタでもこの機能が活用できます。実際の例を見てみましょう。
using System;
namespace SampleApplication1
{
class Class1 //クラス
{
public string message;
public Class1() //引数なし
{
Console.WriteLine("Class1 Constructor0()");
}
public Class1(string message) //引数1つ
{
this.message = message;
Console.WriteLine("Class1 Constructor1() message: " + this.message);
}
public static void Main()
{
Class1 cls1_1 = new Class1();
Class1 cls1_2 = new Class1("Hello World!");
}
}
}
実行結果:
Class1 Constructor0()
Class1 Constructor1() message: Hello World!
Main()メソッドの2行を片方ずつコメントアウトして実行してみると、引数の個数にあったコンストラクタが実行されている事が分かるかと思います。
継承
継承はオブジェクト指向のプログラミング言語におけるとても重要な考え方です。継承とは、あるクラスの機能を受け継いで新しいクラスを作ることを言います。
継承をする際には、クラスの宣言時に「Class名() : 継承したいClass名」というように記述します。実際のサンプルを確認しましょう。
using System;
namespace SampleApplication1
{
class Class1 //クラス
{
public Class1()
{
Console.WriteLine("Class1()");
}
}
class Class2 : Class1 // Class1を継承
{
public Class2()
{
Console.WriteLine("Class2()");
}
public Class2(string message)
{
Console.WriteLine("Class2 Message: " + message);
}
}
class Class3 : Class2 // Class2を継承
{
public Class3(string message)
{
Console.WriteLine("Class3 Message: " + message);
}
public static void Main()
{
Class3 cls3 = new Class3("Hello World!");
}
}
}
上記の例では、Class3がClass2を、Class2がClass1を継承しています。Class1が親、Class2が子、Class3が孫という関係性ですね。
なお、2つのClass間の関係性を表すとき、親を基底クラスと言い、子を派生クラスと言います。
サンプルコードを実行してみると以下のようになります。
実行結果:
Class1()
Class2()
Class3 Message: Hello World!
Class3のインスタンスが生成された際、先にその親のClass2のコンストラクタが実行されようとし、さらにその前に親のClass1のコンストラクタが実行されたため、このような実行結果となります。
順番を簡単に言うと、親 > 子 > 孫 となります。継承を行うと、基底クラスのコンストラクタが優先的に実行されていきます。
また、Class2の引数が1つある場合のコンストラクタを実行したい場合、baseを使う事ができます。
using System;
namespace SampleApplication1
{
class Class1 //クラス
{
public Class1()
{
Console.WriteLine("Class1()");
}
}
class Class2 : Class1 // Class1を継承
{
public Class2()
{
Console.WriteLine("Class2()");
}
public Class2(string message)
{
Console.WriteLine("Class2 Message: " + message);
}
}
class Class3 : Class2 // Class2を継承
{
public Class3(string message) : base(message) //基底クラスに引数を与えている
{
Console.WriteLine("Class3 Message: " + message);
}
public static void Main()
{
Class3 cls3 = new Class3("Hello World!");
}
}
}
実行結果:
Class1()
Class2 Message: Hello World!
Class3 Message: Hello World!
Class3のコンストラクタにbase()が追加されていますね。これにより、基底クラス(Class2)に引数を一つ渡すことができます。
※普段継承を行う際、指定がない場合は引数無しのbase()が省略されているということになります。
デストラクタ
C++などの知識がある方は、デストラクタについても意識が行くかと思います。デストラクタはコンストラクタの逆で、インスタンスが消滅するタイミングで最後に実行される処理です。
ただ、デストラクタに関してはC#初心者の方はあまり使わないかと思いますので、「こういう概念もあるんだ」という程度で見ていただければと思います。
デストラクタですが、C++がインスタンス消滅のタイミングを厳密に管理しなくてはならないのに対し、C#は消滅タイミングが自動で決められるのでプログラマが管理することはできません。
そのため、デストラクタに処理を記述することはバグの原因になってしまう可能性がありオススメされていません。もしそれでもデストラクタが必要な場合は、~Class名()というようにコンストラクタの記述に「~」を付ける事で実行できます。
なお、終了処理を行いたい場合はIDisposableというインタフェースとusingというステートメントを使うことで実現させる事をオススメされています。
サンプル:
using System;
namespace SampleApplication1
{
class Class1 : IDisposable
{
public Class1()
{
Console.WriteLine("Constructor called");
}
void IDisposable.Dispose()
{
Console.WriteLine("Dispose called");
}
}
class TestCls
{
public static void Main(string[] args)
{
using(Class1 sample = new Class1())
{
Console.WriteLine("任意の処理を実行します");
}
Console.WriteLine("using finished");
}
}
}
実行結果:
Constructor called
任意の処理を実行します
Dispose called
using finished
まとめ
今回の記事では、以下コンストラクタの点について解説致しました。
- コンストラクタとは
- コンストラクタの書きかた
- オーバーロードについて
- 継承について
- デストラクタについて
コンストラクタは詳しく知ろうとすると難しいですが、まずは基本形だけでも覚えてしまうことをお勧めします。
ここで使い方を覚えて、ぜひ活用していってください!