C# で同等の Friend クラス
このチュートリアルでは、C# で同等のフレンド クラスについて説明します。 最初に、C++ の例を使用してプログラミングでのフレンド クラスとその使用法について説明します。
次に、C# に相当するフレンド クラスがない場合、代替手段を見ていきます。
C++ のフレンド クラス
主にオブジェクト指向プログラミングに適用される重要なプログラミング機能は、データ隠蔽です。 データ メンバーをクラスの外部から非表示にすることは、データ メンバーをクラス内でのみ更新できることを意味します。
したがって、データ メンバーの値を変更できるのは 1つのクラス (データ メンバーを保持する) だけであると言えます。
データを非表示にすることで、変更するクラスをテストする必要があるため、テストの範囲を縮小します。 他のクラスは、あるクラスのプライベート データ メンバーにアクセスできません。 したがって、コードが変更されていない場合はテストする必要はありません。
長くて複雑なコードでは、データの隠蔽が少し損なわれることがあります。 クラスは、いくつかの関数またはクラスをフレンドとして宣言できます。
それに応じて、friend
関数と friend
クラスはプライベート データ メンバーにアクセスできます。
友情の構文は次のとおりです。
class ABC {
... friend class XYZ;
friend int change_data_member(int, char);
...
クラス ABC
は、3 行目でクラス XYZ
をフレンドとして宣言します。 4 行目は、friend
メソッドを宣言するための構文を示しています。
ここで、クラス ABC
は change_data_member
メソッドを friend
関数として宣言しています。
クラス ABC
からフレンドシップ フラグを受け取った後、クラス XYZ
のすべての関数は、クラス ABC
のプライベート データ メンバーにアクセスできます。
ただし、関数にフレンドシップを付与する場合、フレンドシップの範囲は制限されます。 friend
関数 (つまり、friend
として宣言されている) だけが、友情を与えるクラスのメンバーのプライベート データにアクセスできます。
C++ での友情の概念の実装を見てみましょう。
#include <iostream>
using namespace std;
class ABC {
int privateMember;
public:
ABC(int privateMember) { this->privateMember = privateMember; }
friend int getPrivateMember(ABC &);
friend void setPrivateMember(ABC &, int);
};
int getPrivateMember(ABC &obj) { return obj.privateMember; }
void setPrivateMember(ABC &obj, int privateMember) {
obj.privateMember = privateMember;
}
int main() {
ABC abc(5);
cout << "Private Member: " << getPrivateMember(abc) << '\n';
setPrivateMember(abc, 7);
cout << "Private Member: " << getPrivateMember(abc) << '\n';
return 0;
}
このコードでは、クラス ABC
にプライベート データ メンバー privateMember
があります。 クラス本体の最後の 2 行では、2つの非クラス関数 getPrivateMember
と setPrivateMember
にフレンドシップが付与されています。
このコードの出力は以下のとおりです。
Private Member: 5
Private Member: 7
プライベート データ メンバーは、入力と出力の両方でアクセスされます。 以下は friend
クラスの例です:
#include <iostream>
using namespace std;
class ABC {
int privateMember;
friend class DEF;
};
class DEF {
ABC abc;
public:
int getPrivateMember() { return abc.privateMember; }
void setPrivateMember(int privateMember) {
abc.privateMember = privateMember;
}
};
int main() {
DEF def;
def.setPrivateMember(12);
cout << "Private Member: " << def.getPrivateMember() << '\n';
return 0;
}
クラス ABC
の 2 行目では、クラス DEF
に友情が付与されます。 フレンドシップ ステータスを持つことで、クラス DEF
の getPrivateMember
および setPrivateMember
メソッドは、クラス ABC
のプライベート メンバーにアクセスできます。
このコードの出力は以下のとおりです。
Private Member: 12
この friend
関数または friend
クラスの概念は、C# ではなく C++ で利用できます。 ただし、非メンバー データ関数にアクセスできる方法がいくつかあります。
Friend クラスに相当する C# を使用する
C# の一部の非メンバー関数から任意のクラスのデータ メンバーにアクセスするには、さまざまな方法があります。 これについて、1つずつ説明しましょう。
ネストされたクラスの使用
C++ の friend
クラスに最も近い C# の同等/代替手段は、ネストされた/内部クラスを作成することです。 ネストされたクラスは、外部クラスのプライベート データ メンバーにアクセスできます。
次の例では、Outer_class
へのネストされたクラスとして Inner_class
クラスを作成します。 包含クラスのスコープ内にある場合、Inner_class
は Outer_class
のプライベート メンバーにアクセスでき、それによって friend
クラスを模倣します。
using System;
public class Outer_class {
int privateMember;
Outer_class(int privateMember) {
this.privateMember = privateMember;
}
public class Inner_class {
public void innerClassFunction() {
Outer_class obj = new Outer_class(5);
Console.WriteLine("Private Member: " + obj.privateMember);
}
}
}
public class Driver {
static public void Main() {
Outer_class.Inner_class obj = new Outer_class.Inner_class();
obj.innerClassFunction();
}
}
上記のコードには、1つの外部クラスと 1つの内部クラスがあります。 変数 privateMember
は外部クラスのプライベート メンバーです。
Inner_class
内では、外部クラスの privateMember
であるプライベート データ メンバーにアクセスしています。
次に、Inner_class
のオブジェクトを作成した Driver
クラスがあります。 Inner_class
オブジェクトを使用して、Inner_class
内で innerClassFunction
を呼び出します。
出力:
Private Member: 5
上記のコードは問題なくビルドおよび実行されますが、以下の例に示すように、部分クラスの概念を使用して、Inner_class
を Outer_class
から分離することをお勧めします。
using System;
public partial class Outer_class {
int privateMember;
Outer_class(int privateMember) {
this.privateMember = privateMember;
}
}
public partial class Outer_class {
public class Inner_class {
public void innerClassFunction() {
Outer_class obj = new Outer_class(5);
Console.WriteLine("Private Member: " + obj.privateMember);
}
}
}
public class Driver {
static public void Main() {
Outer_class.Inner_class obj = new Outer_class.Inner_class();
obj.innerClassFunction();
}
}
Outer_class
の 2 番目の部分クラス定義には、関数 innerClassFunction
があります。 この innerClassFunction
メソッドは、外部クラスのオブジェクトを作成します。
Outer_class
オブジェクトを作成する際に、コンストラクター パラメーターの引数として 5
を渡します。 このコンストラクター パラメーターは、次の出力に示すように、外部クラスの最初の部分定義のデータ メンバーの値になります。
Private Member: 5
部分クラスは、複数の人がクラスの異なる部分を作成する大規模なプロジェクトで重要です。
ここでは、この部分クラス オプションを利用して、外部クラスの別の部分部分として Inner_class
を内部に記述しています。 この部分スキームは、部分単位を分離したままにしておくことができますが、それらを単一の単位として使用できます。
このスキームの主な利点は、実際のクラス定義が Inner_class
定義を書く人に公開されないことです。
反射オブジェクト
Reflection
のオブジェクトを使用して、実行時の型情報を取得できます。
Reflection
オブジェクトを使用して、プログラムの実行中に Type
情報を取得できます。 System.Reflection
名前空間を使用して、実行中のプログラムのメタデータにアクセスできます。
System.Reflection
は、実行時に役立つ情報を提供し、さまざまな型、値、およびクラス オブジェクトをプログラムに追加するのに役立ちます。
さらに、System.Reflection
名前空間を使用してメソッドのハンドルを取得し、クラスのプライベート メソッドをテストできます。
このようにして、クラスのプライベート メソッドを呼び出すことができます。 したがって、friend
クラスを複製できます。
たとえば、プライベート メソッド callable_fun()
を持つ次のクラスを想定します。
public class Class1 {
private char callable_fun() {
return 'C';
}
}
プライベート関数 callable_fun
をテストするには、次のコードを記述する必要があります。
using System.Reflection;
public class Driver {
static public void Main() {
Class1 c = new Class1();
Type class1Type = c.GetType();
MethodInfo callMeMethod =
class1Type.GetMethod("callable_fun", BindingFlags.Instance | BindingFlags.NonPublic);
char result = (char)callMeMethod.Invoke(c, null);
Console.WriteLine("Output:" + result);
}
}
このコードの出力は以下のとおりです。
Output:C
Visual Studio Team System を使用している場合は、メソッドを右クリックして [Create Unit Tests…] を選択することで、プライベート アクセサーを含むプロキシ クラスを VS に自動的に生成させることができます。
InternalsVisibleToAttribute
クラスの使用
このクラスは、通常は現在のアセンブリ内でのみ表示される型について、指定されたアセンブリへの可視性を高めるのに役立ちます。
// Syntax
[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
public sealed class InternalsVisibleToAttribute : Attribute
2つの署名されていないアセンブリがある場合、InternalsVisibleToAttribute
クラスは、別の署名されていないアセンブリが 1つの署名されていないアセンブリのプライベート メンバーにアクセスするのに役立ちます。 1つのアセンブリの名前 (friend
クラス/アセンブリの名前を持つ) が引数として渡されます。
次に、InternalsVisibleToAttribute
を使用して別の署名されていないアセンブリのプライベート メンバーにアクセスする例を見てみましょう。 ここで、SecondClass
は FirstClass
のプライベート メンバーにアクセスします。
以下のコードを参照してください。
using System;
using System.Runtime.CompilerServices;
using Utilities.Example;
[assembly:InternalsVisibleToAttribute("FirstClass")]
namespace Utilities.Example {
public class FirstClass {
internal static int InnerMethod(int x) {
return x * x;
}
}
}
次の例は、SecondClass
アセンブリのソース コードを示しています。
public class SecondClass {
public static void Main() {
Console.WriteLine(FirstClass.InnerMethod(10));
}
}
このコードの出力は 100
です。
アセンブリに署名した場合、ディレクトリ パスとファイル名拡張子は必要ありません。
次の例では、InternalsVisibleToAttribute
を使用して、別の署名済みアセンブリから見える署名済みアセンブリ内の innerMethod
という名前のプライベート メソッドを呼び出します。 内部 innerMethod
メソッドを含む FirstClass
クラスを定義します。
using System;
using System.IO;
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo(
"Friend_Assembly, PublicKey=002400000480000094" + "0000000602000000240000525341310004000" +
"001000100bf8c25fcd44838d87e245ab35bf7" + "3ba2615707feea295709559b3de903fb95a93" +
"3d2729967c3184a97d7b84c7547cd87e435b5" + "6bdf8621bcb62b59c00c88bd83aa62c4fcdd4" +
"712da72eec2533dc00f8529c3a0bbb4103282" + "f0d894d5f34e9f0103c473dce9f4b457a5dee" +
"fd8f920d8681ed6dfcb0a81e96bd9b176525a" + "26e0b3")]
public class FirstClass {
internal static int innerMethod(int x) {
return x * x;
}
}
次の例が、Friend_Assembly
という名前の厳密な名前のアセンブリにコンパイルされているとします。 FirstClass
の内部メソッドは innerMethod
を正常に呼び出しますが、メソッドは FirstClass
の内部にあります。
public class SecondClass {
public static void Main() {
int result = FirstClass.innerMethod(10);
Console.WriteLine(result);
}
}
このコードの出力は、やはり 100
です。
C# の C++ friend
クラスに相当するさまざまなクラスを紹介しました。 読者は、要件に応じてどれでも使用できます。