C# Language
コンストラクタとファイナライザ
サーチ…
前書き
コンストラクターは、そのクラスのインスタンスが作成されるときに呼び出される、クラス内のメソッドです。主な役割は、新しいオブジェクトを有用かつ一貫した状態にすることです。
デストラクタ/ファイナライザは、クラスのインスタンスが破棄されたときに呼び出されるクラス内のメソッドです。 C#では、明示的に記述/使用されることはほとんどありません。
備考
C#にはデストラクタが実際にはなく、C ++スタイルのデストラクタ構文を使用するFinalizersがあります。デストラクタを指定すると、直接呼び出すことのできないObject.Finalize()
メソッドがオーバーライドされます。
同様の構文を持つ他の言語とは異なり、オブジェクトはスコープ外に出たときには呼び出されませんが、特定の条件下ではガベージコレクタが実行されたときに呼び出されます 。したがって、特定の順序で実行することは保証されていません。
ファイナライザは(Marshalクラスを介して取得ポインタが、P /呼び出し(システムコール)、または危険なブロック内で使用される生のポインタを介して受信) のみアンマネージリソースをクリーンアップするための責任を負わなければなりません。管理リソースをクリーンアップするには、IDisposable、Disposeパターン、 using
ステートメントを確認しusing
ください。
(詳しい読者: いつデストラクタを作成すればよいですか? )
デフォルトコンストラクタ
型がコンストラクタなしで定義されている場合:
public class Animal
{
}
コンパイラは次のようなデフォルトのコンストラクタを生成します。
public class Animal
{
public Animal() {}
}
型の任意のコンストラクタの定義は、デフォルトコンストラクタの生成を抑制します。タイプが次のように定義されている場合:
public class Animal
{
public Animal(string name) {}
}
Animal
は、宣言されたコンストラクタを呼び出すことによってのみ作成できます。
// This is valid
var myAnimal = new Animal("Fluffy");
// This fails to compile
var unnamedAnimal = new Animal();
2番目の例では、コンパイラはエラーメッセージを表示します。
'Animal'には0個の引数を取るコンストラクタが含まれていません
クラスにパラメータのないコンストラクタとパラメータを受け取るコンストラクタの両方を持たせたい場合は、両方のコンストラクタを明示的に実装することでそれを行うことができます。
public class Animal
{
public Animal() {} //Equivalent to a default constructor.
public Animal(string name) {}
}
クラスがパラメータのないコンストラクタを持たない別のクラスを拡張する場合、コンパイラはデフォルトコンストラクタを生成できません。例えば、私たちがCreature
クラスを持っていれば:
public class Creature
{
public Creature(Genus genus) {}
}
その後、 Animal
として定義されclass Animal : Creature {}
コンパイルされないであろう。
別のコンストラクタからコンストラクタを呼び出す
public class Animal
{
public string Name { get; set; }
public Animal() : this("Dog")
{
}
public Animal(string name)
{
Name = name;
}
}
var dog = new Animal(); // dog.Name will be set to "Dog" by default.
var cat = new Animal("Cat"); // cat.Name is "Cat", the empty constructor is not called.
静的コンストラクタ
静的コンストラクタは、型のいずれかのメンバが最初に初期化されるとき、静的クラスメンバが呼び出されるとき、または静的メソッドが呼び出されるときに呼び出されます。静的コンストラクタはスレッドセーフです。静的コンストラクタは、一般的に次の目的で使用されます。
- 静的状態を初期化します。つまり、同じクラスの異なるインスタンス間で共有される状態です。
- シングルトンを作成する
例:
class Animal
{
// * A static constructor is executed only once,
// when a class is first accessed.
// * A static constructor cannot have any access modifiers
// * A static constructor cannot have any parameters
static Animal()
{
Console.WriteLine("Animal initialized");
}
// Instance constructor, this is executed every time the class is created
public Animal()
{
Console.WriteLine("Animal created");
}
public static void Yawn()
{
Console.WriteLine("Yawn!");
}
}
var turtle = new Animal();
var giraffe = new Animal();
出力:
動物の初期化
動物が作成されました
動物が作成されました
最初の呼び出しが静的メソッドの場合、静的コンストラクターはインスタンスコンストラクターなしで呼び出されます。静的メソッドはインスタンスの状態にアクセスできないため、これは問題ありません。
Animal.Yawn();
これは出力されます:
動物の初期化
ハーン!
静的コンストラクタと汎用静的コンストラクタの 例外も参照してください。
シングルトンの例:
public class SessionManager
{
public static SessionManager Instance;
static SessionManager()
{
Instance = new SessionManager();
}
}
基本クラスのコンストラクタを呼び出す
基本クラスのコンストラクタは、派生クラスのコンストラクタが実行される前に呼び出されます。たとえば、 Mammal
がAnimal
拡張する場合、 Mammal
インスタンスを作成するときに、 Animal
のコンストラクタに含まれるコードが最初に呼び出されます。
派生クラスが、呼び出される基本クラスのコンストラクタを明示的に指定していない場合、コンパイラはパラメータのないコンストラクタを想定します。
public class Animal
{
public Animal() { Console.WriteLine("An unknown animal gets born."); }
public Animal(string name) { Console.WriteLine(name + " gets born"); }
}
public class Mammal : Animal
{
public Mammal(string name)
{
Console.WriteLine(name + " is a mammal.");
}
}
この場合、 new Mammal("George the Cat")
を呼び出してMammal
インスタンス化すると、
未知の動物が生まれます。
ジョージザキャットは哺乳動物です。
基本クラスの別のコンストラクタを呼び出すには、コンストラクタのシグネチャとボディの間に: base(args)
挿入します。
public class Mammal : Animal
{
public Mammal(string name) : base(name)
{
Console.WriteLine(name + " is a mammal.");
}
}
new Mammal("George the Cat")
を呼び出すと次のように表示されます:
ジョージ・ザ・キャットが生まれる。
ジョージザキャットは哺乳動物です。
派生クラスのファイナライザ
オブジェクトグラフがファイナライズされると、その順序は構築の逆です。たとえば、次のコードで示すように、スーパータイプはベースタイプの前にファイナライズされます。
class TheBaseClass
{
~TheBaseClass()
{
Console.WriteLine("Base class finalized!");
}
}
class TheDerivedClass : TheBaseClass
{
~TheDerivedClass()
{
Console.WriteLine("Derived class finalized!");
}
}
//Don't assign to a variable
//to make the object unreachable
new TheDerivedClass();
//Just to make the example work;
//this is otherwise NOT recommended!
GC.Collect();
//Derived class finalized!
//Base class finalized!
シングルトンコンストラクタパターン
public class SingletonClass
{
public static SingletonClass Instance { get; } = new SingletonClass();
private SingletonClass()
{
// Put custom constructor code here
}
}
コンストラクターはプライベートなので、コードを消費することでSingletonClass
の新しいインスタンスを作成することはできません。 SingletonClass
の単一インスタンスにアクセスする唯一の方法は、静的プロパティSingletonClass.Instance
を使用することです。
Instance
プロパティは、C#コンパイラが生成する静的コンストラクタによって割り当てられます。 .NETランタイムは、静的コンストラクターが最大で1回実行され、 Instance
が最初に読み取られる前に実行されることを保証します。したがって、すべての同期と初期化の問題は、ランタイムによって実行されます。
スタティックコンストラクタが失敗した場合、 Singleton
クラスはAppDomainの存続期間中永久に使用できなくなることに注意してください。
また、静的コンストラクターは、 Instance
最初のアクセス時に実行される保証はありません。むしろ、それはその前のある時点で実行されます。これにより、初期化が非決定論的に起こる時間が生じます。実際の場合、JITは、 Instance
参照するメソッドのコンパイル中(実行ではなく)に静的コンストラクタを呼び出すことがよくあります。これはパフォーマンスの最適化です。
シングルトンパターンを実装する他の方法については、「 シングルトン実装 」ページを参照してください。
静的コンストラクタを強制的に呼び出す
静的コンストラクタは常に型の最初の使用の前に呼び出されますが、強制的に呼び出すことができると便利なことがあり、 RuntimeHelpers
クラスはヘルパーを提供します:
using System.Runtime.CompilerServices;
// ...
RuntimeHelpers.RunClassConstructor(typeof(Foo).TypeHandle);
備考 :すべての静的初期化(フィールドイニシャライザなど)は、コンストラクタ自体だけでなく、実行されます。
潜在的な使用法 : UIアプリケーションのスプラッシュ画面で初期化を強制するか、スタティックコンストラクターが単体テストで失敗しないようにします。
コンストラクタで仮想メソッドを呼び出す
C#のC ++とは異なり、クラスコンストラクタから仮想メソッドを呼び出すことができます(これはC ++でも可能ですが、最初の動作は驚くべきことです)。例えば:
abstract class Base
{
protected Base()
{
_obj = CreateAnother();
}
protected virtual AnotherBase CreateAnother()
{
return new AnotherBase();
}
private readonly AnotherBase _obj;
}
sealed class Derived : Base
{
public Derived() { }
protected override AnotherBase CreateAnother()
{
return new AnotherDerived();
}
}
var test = new Derived();
// test._obj is AnotherDerived
あなたがC ++のバックグラウンドから来たのであればこれは驚くべきことですが、基底クラスのコンストラクタは既に派生クラスの仮想メソッドテーブルを見ています!
注意 :派生クラスはまだ完全には初期化されていないかもしれません(そのコンストラクタは基本クラスのコンストラクタの後に実行されます)。このテクニックは危険です(このためのStyleCop警告もあります)。通常、これは悪い習慣とみなされます。
一般的な静的コンストラクタ
静的コンストラクタが宣言されている型が汎用である場合、静的コンストラクタは汎用引数の一意の組み合わせごとに1回呼び出されます。
class Animal<T>
{
static Animal()
{
Console.WriteLine(typeof(T).FullName);
}
public static void Yawn() { }
}
Animal<Object>.Yawn();
Animal<String>.Yawn();
これは出力されます:
System.Object
System.String
See alsoも参照ジェネリック型の静的コンストラクタはどのように機能しますか?
静的コンストラクタの例外
静的コンストラクターが例外をスローした場合は、再試行されません。この型は、AppDomainの存続期間中は使用できません。型のこれ以上の使用法は、元の例外を包むTypeInitializationException
を発生させます。
public class Animal
{
static Animal()
{
Console.WriteLine("Static ctor");
throw new Exception();
}
public static void Yawn() {}
}
try
{
Animal.Yawn();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
try
{
Animal.Yawn();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
これは出力されます:
スタティックctor
System.TypeInitializationException: 'Animal'の型初期化子が例外をスローしました。 ---> System.Exception: 'System.Exception'型の例外がスローされました。
[...]
System.TypeInitializationException: 'Animal'の型初期化子が例外をスローしました。 ---> System.Exception: 'System.Exception'型の例外がスローされました。
実際のコンストラクタは一度だけ実行され、例外は再利用されていることがわかります。
コンストラクタとプロパティの初期化
クラスのコンストラクタの前または後に 、プロパティ値の代入を実行しますか?
public class TestClass
{
public int TestProperty { get; set; } = 2;
public TestClass()
{
if (TestProperty == 1)
{
Console.WriteLine("Shall this be executed?");
}
if (TestProperty == 2)
{
Console.WriteLine("Or shall this be executed");
}
}
}
var testInstance = new TestClass() { TestProperty = 1 };
上記の例では、クラスのコンストラクタ内またはクラスコンストラクタの後にTestProperty
値を1
にする必要がありますか?
次のようにインスタンス作成時にプロパティ値を割り当てる:
var testInstance = new TestClass() {TestProperty = 1};
コンストラクタの実行後に実行されます。ただし、C#6.0のクラスのプロパティでプロパティ値を次のように初期化します。
public class TestClass
{
public int TestProperty { get; set; } = 2;
public TestClass()
{
}
}
コンストラクタが実行される前に実行されます。
上記の2つの概念を1つの例で組み合わせると、
public class TestClass
{
public int TestProperty { get; set; } = 2;
public TestClass()
{
if (TestProperty == 1)
{
Console.WriteLine("Shall this be executed?");
}
if (TestProperty == 2)
{
Console.WriteLine("Or shall this be executed");
}
}
}
static void Main(string[] args)
{
var testInstance = new TestClass() { TestProperty = 1 };
Console.WriteLine(testInstance.TestProperty); //resulting in 1
}
最終結果:
"Or shall this be executed"
"1"
説明:
TestProperty
値は、最初に2
として割り当てられ、次にTestClass
コンストラクタが実行され、
"Or shall this be executed"
そしてその後TestProperty
ように割り当てられる1
によるにnew TestClass() { TestProperty = 1 }
の最終値製造TestProperty
によって印刷Console.WriteLine(testInstance.TestProperty)
であることが
"1"