プライベート、静的、および入れ子になったクラスを実装する
インスタンス コンストラクターは、new 演算子を使用してクラスの新しいインスタンスを作成するときに実行されるコードを指定するために使用されます。 インスタンス コンストラクターが public アクセス修飾子を使用する場合、結果のオブジェクトには、アプリケーション内の他のコードからアクセスできます。 オブジェクトとそのメンバーへのこの種類のアクセスは、多くの場合、望ましいですが、クラスまたはクラスのメンバーへのアクセスを制限する必要がある場合があります。
クラスの公開を非表示または縮小する必要がある場合は、アクセス修飾子を使用して、クラスとそのメンバーの可視性を制御できます。
アクセス修飾子
すべての型と型のメンバーには、コンパイル済みアプリケーション (または他のアセンブリ) 内の他のコードからアクセスできるかどうかを制御するアクセシビリティ レベルがあります。
名前空間内で直接宣言されたクラスは、public、internal、または file アクセスを持つことができます。 アクセス修飾子が指定されていない場合、クラスは既定でアクセス internal 割り当てられます。
型またはメンバーを宣言するときにアクセシビリティを指定するには、次のアクセス修飾子を使用します。
-
public: 任意のアセンブリ内のコードがこの型またはメンバーにアクセスできます。 包含型のアクセシビリティ レベルは、型のパブリック メンバーのアクセシビリティ レベルを制御します。 -
private: このメンバーにアクセスできるのは、同じclassまたはstructで宣言されたコードだけです。 -
protected: この型またはメンバーにアクセスできるのは、同じclassまたは派生class内のコードだけです。 -
internal: この型またはメンバーにアクセスできるのは、同じアセンブリ内のコードだけです。 -
protected internal: この型またはメンバーにアクセスできるのは、同じアセンブリ内のコードまたは別のアセンブリ内の派生クラス内のコードだけです。 -
private protected: 型またはメンバーにアクセスできるのは、同じアセンブリ内および同じクラスまたは派生クラス内のコードだけです。 -
file: 型またはメンバーにアクセスできるのは、同じファイル内のコードだけです。
型の record 修飾子により、コンパイラは追加のメンバーを合成します。
record 修飾子は、record class または record structの既定のアクセシビリティには影響しません。
プライベート クラス コンストラクター
プライベート コンストラクターは、特殊なインスタンス コンストラクターです。 プライベート コンストラクターは、静的メンバーのみを含むクラスでよく使用されます。 クラスに 1 つ以上のプライベート コンストラクターがあり、パブリック コンストラクターがない場合、他のクラス (入れ子になったクラスを除く) はクラスのインスタンスを作成できません。 例えば:
class NLog
{
// Private Constructor:
private NLog() { }
public static double e = Math.E; //2.71828...
}
空のコンストラクターの宣言により、パラメーターなしのコンストラクターが自動生成されません。 コンストラクターのアクセス修飾子を指定しない場合、既定では privateになります。 ただし、クラスをインスタンス化できないことを明確にするために、private 修飾子を使用する必要があります。
プライベート コンストラクターは、.NET ライブラリの Math クラスなどのインスタンス フィールドまたはメソッドがない場合や、クラスのインスタンスを取得するためにメソッドが呼び出されたときに、クラスのインスタンスを作成しないようにするために使用されます。 クラス内のすべてのメソッドが静的である場合は、完全なクラスを静的にすることを検討してください。
次の例は、プライベート コンストラクターを使用するクラスを示しています。
public class Counter
{
private Counter() { }
public static int currentCount;
public static int IncrementCount()
{
return ++currentCount;
}
}
class TestCounter
{
static void Main()
{
// If you uncomment the following statement, it generates
// an error because the constructor is inaccessible:
// Counter aCounter = new Counter(); // Error
Counter.currentCount = 100;
Counter.IncrementCount();
Console.WriteLine("New count: {0}", Counter.currentCount);
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
// Output: New count: 101
この例から次のステートメントのコメントを解除すると、コンパイラによってビルド エラーが生成されます。 コンパイラは、プライベート コンストラクターにアクセスできないことを認識します。
// Counter aCounter = new Counter(); // Error
静的クラス
静的クラスは基本的に非静的クラスと同じですが、1 つの重要な違いがあります。静的クラスはインスタンス化できません。 つまり、new 演算子を使用して静的クラスから変数を作成することはできません。 クラスのインスタンスを作成することはできませんが、クラス名を参照することで静的クラスのメンバーにアクセスできます。 たとえば、UtilityClassという名前のパブリック静的メソッドを持つ MethodA という名前の静的クラスがある場合は、クラス名とメソッド名の組み合わせを使用してメソッドを呼び出すことができます。 この構文を次の例に示します。
UtilityClass.MethodA();
静的クラスは、入力パラメーターを操作し、内部インスタンス フィールドを get または set する必要がないメソッドのコンテナーとして使用できます。
たとえば、.NET クラス ライブラリの静的 System.Math クラスには、数学的演算を実行するメソッドが含まれています。 これらのメソッドは、Math クラスの特定のインスタンスに固有のデータを格納または取得する必要なく動作します。 次の例に示すように、クラス名とメソッド名を指定して、クラスのメンバーを呼び出します。
double dub = -3.14;
Console.WriteLine(Math.Abs(dub));
Console.WriteLine(Math.Floor(dub));
Console.WriteLine(Math.Round(Math.Abs(dub)));
// Output:
// 3.14
// -4
// 3
すべてのクラス型の場合と同様に、.NET ランタイムは、クラスを参照するプログラムが読み込まれるときに静的クラスの型情報を読み込みます。 プログラムでは、クラスが読み込まれるときに正確に指定することはできません。 ただし、クラスがプログラムで初めて参照される前に、フィールドを読み込んで初期化し、静的コンストラクターを呼び出す必要があります。 静的コンストラクターは 1 回だけ呼び出され、静的クラスは、プログラムが存在するアプリケーション ドメインの有効期間中、メモリ内に残ります。
静的クラスの主な機能を次に示します。
- 静的メンバーのみを含みます。
- インスタンス化できません。
- シールされています。
- インスタンス コンストラクターを含めることはできません。
したがって、静的クラスの作成は、静的メンバーとプライベート コンストラクターのみを含むクラスの作成と基本的に同じです。 プライベート コンストラクターは、クラスがインスタンス化されないようにします。 静的クラスを使用する利点は、インスタンス メンバーが誤って追加されていないことをコンパイラが確認できることです。 コンパイラは、このクラスのインスタンスを作成できないことを保証します。
静的クラスはシールされているため、継承できません。
Object以外のクラスまたはインターフェイスから継承することはできません。 静的クラスにインスタンス コンストラクターを含めることはできません。 ただし、static コンストラクターを含めることができます。 また、非静的クラスには、単純でない初期化を必要とする static メンバーがクラスに含まれている場合は、static コンストラクターも定義する必要があります。
温度を摂氏から華氏に、華氏から華氏に変換する 2 つのメソッドを含む静的クラスの例を次に示します。
public static class TemperatureConverter
{
public static double CelsiusToFahrenheit(string temperatureCelsius)
{
// Convert argument to double for calculations.
double celsius = Double.Parse(temperatureCelsius);
// Convert Celsius to Fahrenheit.
double fahrenheit = (celsius * 9 / 5) + 32;
return fahrenheit;
}
public static double FahrenheitToCelsius(string temperatureFahrenheit)
{
// Convert argument to double for calculations.
double fahrenheit = Double.Parse(temperatureFahrenheit);
// Convert Fahrenheit to Celsius.
double celsius = (fahrenheit - 32) * 5 / 9;
return celsius;
}
}
class TestTemperatureConverter
{
static void Main()
{
Console.WriteLine("Please select the convertor direction");
Console.WriteLine("1. From Celsius to Fahrenheit.");
Console.WriteLine("2. From Fahrenheit to Celsius.");
Console.Write(":");
string? selection = Console.ReadLine();
double F, C = 0;
switch (selection)
{
case "1":
Console.Write("Please enter the Celsius temperature: ");
F = TemperatureConverter.CelsiusToFahrenheit(Console.ReadLine() ?? "0");
Console.WriteLine("Temperature in Fahrenheit: {0:F2}", F);
break;
case "2":
Console.Write("Please enter the Fahrenheit temperature: ");
C = TemperatureConverter.FahrenheitToCelsius(Console.ReadLine() ?? "0");
Console.WriteLine("Temperature in Celsius: {0:F2}", C);
break;
default:
Console.WriteLine("Please select a convertor.");
break;
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Example Output:
Please select the convertor direction
1. From Celsius to Fahrenheit.
2. From Fahrenheit to Celsius.
:2
Please enter the Fahrenheit temperature: 20
Temperature in Celsius: -6.67
Press any key to exit.
*/
静的メンバー
非静的クラスには、静的メソッド、フィールド、プロパティ、またはイベントを含めることができます。 静的メンバーは、クラスのインスタンスが存在しない場合でも、クラスで呼び出し可能です。 静的メンバーには、インスタンス名ではなく、常にクラス名によってアクセスされます。 作成されるクラスのインスタンス数に関係なく、静的メンバーのコピーは 1 つだけ存在します。 静的メソッドとプロパティは、非静的フィールドとその包含型のイベントにアクセスできません。また、メソッド パラメーターで明示的に渡されない限り、オブジェクトのインスタンス変数にはアクセスできません。
クラス全体を静的として宣言するよりも、静的でないクラスを静的メンバーで宣言する方が一般的です。 静的フィールドの 2 つの一般的な用途は、インスタンス化されるオブジェクトの数を保持するか、すべてのインスタンス間で共有する必要がある値を格納することです。
静的メソッドは、クラスに属しており、クラスのインスタンスには属していないため、オーバーロードすることはできますが、オーバーライドできません。
フィールドを static constとして宣言することはできませんが、const フィールドは基本的にその動作で静的です。 これは型に属し、型のインスタンスには属していません。 したがって、const フィールドには、静的フィールドに使用されるのと同じ ClassName.MemberName 表記を使用してアクセスできます。 オブジェクト インスタンスは必要ありません。
C# では、静的ローカル変数 (つまり、メソッド スコープで宣言されている変数) はサポートされていません。
次の例に示すように、メンバーの戻り値の型の前に static キーワードを使用して静的クラス メンバーを宣言します。
public class Automobile
{
public static int NumberOfWheels = 4;
public static int SizeOfGasTank
{
get
{
return 15;
}
}
public static void Drive() { }
public static event EventType? RunOutOfGas;
// Other nonstatic fields and properties...
}
静的メンバーは、静的メンバーが初めてアクセスされる前に初期化され、静的コンストラクターがある場合は静的コンストラクターが呼び出される前に初期化されます。 静的クラス メンバーにアクセスするには、次の例に示すように、変数名の代わりにクラスの名前を使用してメンバーの場所を指定します。
Automobile.Drive();
int i = Automobile.NumberOfWheels;
クラスに静的フィールドが含まれている場合は、クラスの読み込み時にそれらを初期化する静的コンストラクターを指定します。
静的メソッドの呼び出しでは共通中間言語 (CIL) の呼び出し命令が生成されますが、インスタンス メソッドの呼び出しでは callvirt 命令が生成され、null オブジェクト参照もチェックされます。 ただし、ほとんどの場合、2 つのパフォーマンスの違いは重要ではありません。
入れ子になったクラス
別のクラス内で定義されている class 型は、入れ子になったクラスと呼ばれます。 例えば:
public class Container
{
class Nested
{
Nested() { }
}
}
入れ子になったクラスのアクセシビリティは、既定で privateに設定されます。 つまり、入れ子になったクラスには、その包含クラスからのみアクセスできます。 前の例では、Nested クラスは外部型にアクセスできません。
次のように、入れ子になった型のアクセシビリティを定義するアクセス修飾子を指定できます。
- クラスの入れ子になった型は、
public、protected、internal、protected internal、private、またはprivate protectedできます。
ただし、シール クラス内で protected、protected internal、または入れ子になったクラス private protected 定義すると、コンパイラ警告 CS0628 "sealed クラスで宣言された新しい保護されたメンバー" が生成されます。
注意
入れ子になった型を外部から表示すると、コード品質規則 CA1034 "入れ子になった型は表示できません" に違反することに注意してください。
次の例では、Nested クラスをパブリックにします。
public class Container
{
public class Nested
{
Nested() { }
}
}
入れ子になったクラスまたは内部クラスは、包含クラスまたは外部クラスにアクセスできます。 包含クラスにアクセスするには、入れ子になったクラスのコンストラクターに引数として渡します。 例えば:
public class Container
{
public class Nested
{
private Container? parent;
public Nested()
{
}
public Nested(Container parent)
{
this.parent = parent;
}
}
}
入れ子になったクラスは、その包含クラスからアクセスできるすべてのメンバーにアクセスできます。 継承された保護されたメンバーを含め、包含クラスのプライベート メンバーと保護されたメンバーにアクセスできます。
前の宣言では、クラス Nested の完全な名前が Container.Nested。 これは、次のように、入れ子になったクラスの新しいインスタンスを作成するために使用される名前です。
Container.Nested nest = new Container.Nested();