internal
C# でのキーワードの実際の使用法を教えてください。
修飾子が現在のアセンブリへのアクセスを制限していることは知っていinternal
ますが、いつ、どのような状況で使用する必要がありますか?
internal
C# でのキーワードの実際の使用法を教えてください。
修飾子が現在のアセンブリへのアクセスを制限していることは知っていinternal
ますが、いつ、どのような状況で使用する必要がありますか?
同じアセンブリ内の他の多くのクラスからアクセスしたいが、他のアセンブリのコードがアクセスできないようにしたいユーティリティまたはヘルパー クラス/メソッド。
MSDNから(archive.org 経由):
内部アクセスの一般的な用途は、コンポーネント ベースの開発です。これにより、コンポーネントのグループがアプリケーション コードの残りの部分に公開されることなく、プライベートな方法で連携できるようになるためです。たとえば、グラフィカル ユーザー インターフェイスを構築するためのフレームワークは、内部アクセスを持つメンバーを使用して連携する Control クラスと Form クラスを提供できます。これらのメンバーは内部であるため、フレームワークを使用しているコードには公開されません。
また、アセンブリ レベル属性と共に internal 修飾子を使用しInternalsVisibleTo
て、ターゲット アセンブリの内部クラスへの特別なアクセス権を付与された "フレンド" アセンブリを作成することもできます。
これは、テスト対象のアセンブリの内部メンバーを呼び出すことができる単体テスト アセンブリの作成に役立ちます。もちろん、他のアセンブリにはこのレベルのアクセス権が付与されていないため、システムをリリースしてもカプセル化は維持されます。
Bob が BigImportantClass を必要とする場合、Bob はプロジェクト A の所有者にサインアップしてもらい、BigImportantClass が彼のニーズを満たすように作成され、彼のニーズを満たしていることを確認するためにテストされ、彼のニーズを満たしていることが文書化されていることを保証する必要があります。彼のニーズを満たさなくなるような変更が決して行われないように、適切な場所に配置されます。
クラスが内部の場合、そのプロセスを経る必要がないため、プロジェクト A の予算を節約して、他のことに費やすことができます。
内部のポイントは、それがボブの生活を困難にするということではありません。それは、プロジェクト A が機能、寿命、互換性などについて行っている高価な約束を制御できるようにすることです。
internal を使用するもう 1 つの理由は、バイナリを難読化する場合です。難読化ツールは、内部クラスのクラス名をスクランブルしても安全であることを認識していますが、パブリック クラスの名前はスクランブルできないため、既存の参照が壊れる可能性があります。
大量の複雑な機能を単純なパブリック API にカプセル化する DLL を作成している場合、「内部」は公開されないクラス メンバーで使用されます。
複雑さを隠すこと (別名カプセル化) は、品質ソフトウェア エンジニアリングの主な概念です。
internal キーワードは、非マネージ コードのラッパーを構築するときに頻繁に使用されます。
DllImport したい C/C++ ベースのライブラリがある場合、これらの関数をクラスの静的関数としてインポートし、それらを内部にすることができるため、ユーザーは元の API ではなくラッパーにのみアクセスできるため、何でもいじる。関数は静的であるため、必要な複数のラッパー クラスに対して、アセンブリ内のどこでも使用できます。
Mono.Cairo を見ることができます。これは、このアプローチを使用する cairo ライブラリのラッパーです。
「可能な限り厳密な修飾子を使用する」というルールに従っているため、別のアセンブリから明示的にアクセスする必要があるまで、別のクラスのメソッドなど、アクセスする必要があるすべての場所で internal を使用します。
アセンブリ インターフェイスは通常、そのクラス インターフェイスの合計よりも狭いため、使用する場所は非常に多くあります。
internal は使いすぎだと思います。特定の機能を特定のクラスだけに公開し、他の消費者には公開しないようにするべきではありません。
私の意見では、これはインターフェースを壊し、抽象化を壊します。これは絶対に使用してはならないということではありませんが、別のクラスにリファクタリングするか、可能であれば別の方法で使用することをお勧めします。ただし、これが常に可能であるとは限りません。
問題が発生する理由は、別の開発者が、同じアセンブリ内に別のクラスを構築する責任を負う可能性があるためです。内部構造を持つと抽象化の明確さが低下し、誤用すると問題が発生する可能性があります。公開したのと同じ問題です。他の開発者によって構築されている他のクラスは、他の外部クラスと同様に、依然としてコンシューマーです。クラスの抽象化とカプセル化は、外部クラスを保護するためだけではなく、すべてのクラスを保護するためのものです。
もう 1 つの問題は、多くの開発者が、アセンブリ内の他の場所でそれを使用する必要があると考え、その時点では必要ないにもかかわらず、とにかく内部としてマークすることです。その後、別の開発者は、それが必要だと考えるかもしれません。通常、決定的な必要性があるまでは非公開としてマークします。
しかし、これには主観的なものもあり、決して使用すべきではないと言っているわけではありません。必要なときにだけ使用してください。
覚えていないブログで、先日、おそらく一週間、興味深いものを見ました。基本的に私はこれを信用することはできませんが、何か役に立つアプリケーションがあるかもしれないと思いました.
抽象クラスを別のアセンブリから見られるようにしたいが、誰かがそれを継承できるようにしたくないとします。Sealed は何らかの理由で抽象的であるため機能しません。そのアセンブリの他のクラスはそれを継承します。他のアセンブリのどこかで親クラスを宣言したい場合があるため、プライベートは機能しません。
名前空間 Base.Assembly { パブリック抽象クラス 親 { 内部抽象 void SomeMethod(); } //これは同じアセンブリにあるため、問題なく動作します。 public class ChildWithin : 親 { 内部オーバーライド void SomeMethod() { } } } 名前空間 Another.Assembly { //Kaboom、内部メソッドをオーバーライドできないため public class ChildOutside : 親 { } パブリック クラス テスト { //大丈夫 プライベート親 _parent; 公開テスト() { //まだ大丈夫 _parent = new ChildWithin(); } } }
ご覧のように、誰かが継承することなく Parent クラスを効果的に使用できるようにします。
この例には、Assembly1.cs と Assembly2.cs の 2 つのファイルが含まれています。最初のファイルには、内部基本クラス BaseClass が含まれています。2 番目のファイルでは、BaseClass をインスタンス化しようとするとエラーが発生します。
// Assembly1.cs
// compile with: /target:library
internal class BaseClass
{
public static int intM = 0;
}
// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // CS0122
}
}
この例では、例 1 で使用したのと同じファイルを使用し、BaseClass のアクセシビリティ レベルを public に変更します。また、メンバー IntM のアクセシビリティ レベルをinternalに変更します。この場合、クラスをインスタンス化できますが、内部メンバーにはアクセスできません。
// Assembly2.cs
// compile with: /target:library
public class BaseClass
{
internal static int intM = 0;
}
// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // Ok.
BaseClass.intM = 444; // CS0117
}
}
ソース: http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx
現在のアセンブリのスコープ内でアクセス可能であり、外部では決してアクセスできないメソッド、クラスなどがある場合。
たとえば、DAL には ORM がある場合がありますが、オブジェクトはビジネス層に公開されるべきではありません。すべての対話は、静的メソッドと必要なパラメーターの受け渡しによって行われる必要があります。
経験則として、次の 2 種類のメンバーがあります。
internal の非常に興味深い使用法 (もちろん、内部メンバーはそれが宣言されているアセンブリにのみ限定されます) は、そこから「フレンド」機能をある程度取得することです。フレンド メンバーは、それが宣言されているアセンブリの外部にある特定の他のアセンブリにのみ表示されるものです。C# にはフレンドの組み込みサポートはありませんが、CLR にはサポートされています。
InternalsVisibleToAttributeを使用してフレンド アセンブリを宣言すると、フレンド アセンブリ内からのすべての参照は、宣言アセンブリの内部メンバーをフレンド アセンブリのスコープ内でパブリックとして扱います。これに関する問題は、すべての内部メンバーが表示されることです。選んで選ぶことはできません。
InternalsVisibleTo の適切な使用法は、さまざまな内部メンバーを単体テスト アセンブリに公開することです。これにより、それらのメンバーをテストするための複雑なリフレクションの回避策が不要になります。すべての内部メンバーが表示されることはそれほど問題ではありませんが、このアプローチを採用すると、クラス インターフェイスがかなり混乱し、宣言アセンブリ内のカプセル化が台無しになる可能性があります。
ノイズリダクション。公開するタイプが少ないほど、ライブラリはよりシンプルになります。改ざん防止/セキュリティは別のものです (ただし、リフレクションはそれに対して勝つことができます)。
内部クラスを使用すると、アセンブリの API を制限できます。これには、API を理解しやすくするなどの利点があります。
また、アセンブリにバグが存在する場合、修正によって重大な変更が発生する可能性は低くなります。内部クラスがない場合、クラスのパブリック メンバーを変更すると重大な変更になると想定する必要があります。内部クラスでは、パブリック メンバーを変更しても、アセンブリ (および InternalsVisibleTo 属性で参照されているすべてのアセンブリ) の内部 API のみが壊れると想定できます。
クラス レベルとアセンブリ レベルでカプセル化するのが好きです。これに同意しない人もいますが、機能が利用可能であることを知ってうれしいです.
internal キーワードの用途の 1 つは、アセンブリのユーザーから具体的な実装へのアクセスを制限することです。
オブジェクトを構築するためのファクトリまたはその他の中心的な場所がある場合、アセンブリのユーザーは、パブリック インターフェイスまたは抽象基本クラスを処理するだけで済みます。
また、内部コンストラクターを使用すると、パブリック クラスをインスタンス化する場所とタイミングを制御できます。
データ バックエンドに LINQ-to-SQL を使用するプロジェクトがあります。Biz と Data という 2 つの主要な名前空間があります。LINQ データ モデルは Data にあり、「内部」とマークされています。Biz 名前空間には、LINQ データ クラスをラップするパブリック クラスがあります。
Data.Client
とBiz.Client
;があります。後者は、データ オブジェクトの関連するすべてのプロパティを公開します。
private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }
Biz オブジェクトには、プライベート コンストラクター (ファクトリ メソッドの使用を強制するため) と、次のような内部コンストラクターがあります。
internal Client(Data.Client client) {
this._client = client;
}
これはライブラリ内のどのビジネス クラスでも使用できますが、フロントエンド (UI) にはデータ モデルに直接アクセスする方法がないため、ビジネス レイヤーが常に仲介役として機能します。
こんなにたくさん使ったのは初めてでinternal
、とても役に立っています。
クラスのメンバーを作成することが理にかなっている場合がありますinternal
。1 つの例として、クラスのインスタンス化方法を制御したい場合があります。クラスのインスタンスを作成するためのある種のファクトリを提供するとします。コンストラクターを作成しinternal
て、ファクトリ (同じアセンブリに存在する) がクラスのインスタンスを作成できるようにすることができますが、そのアセンブリの外部のコードは作成できません。
ただし、特定の理由なしにクラスまたはメンバーを作成しても意味がありません。internal
それらを作成する意味がほとんどないpublic
か、またはprivate
特定の理由がありません。
これはどうでしょうか。通常は、List オブジェクトをアセンブリの外部ユーザーに公開するのではなく、IEnumerable を公開することをお勧めします。ただし、アセンブリ内で List オブジェクトを使用する方がはるかに簡単です。これは、配列構文と他のすべての List メソッドを取得できるためです。したがって、私は通常、アセンブリ内で使用されるリストを公開する内部プロパティを持っています。
このアプローチに関するコメントは大歓迎です。
として定義されたクラスpublic
は、誰かがプロジェクトの名前空間を見ると自動的に IntelliSense に表示されることに注意してください。API の観点からは、プロジェクトのユーザーに使用できるクラスのみを表示することが重要です。キーワードを使用して、internal
表示してはいけないものを非表示にします。
Big_Important_Class
プロジェクト A がプロジェクト外で使用することを意図している場合は、それをマークしないでくださいinternal
。
ただし、多くのプロジェクトでは、実際にはプロジェクト内での使用のみを目的としたクラスを使用することがよくあります。たとえば、パラメータ化されたスレッド呼び出しへの引数を保持するクラスがあるとします。internal
このような場合、意図しない API の変更から身を守る以外の理由がないかのようにマークする必要があります。
ライブラリを設計するときは、(ライブラリのクライアントによって) 外部から使用することを意図したクラスのみを公開する必要があるという考えです。このようにして、クラスを非表示にすることができます
等
社内ソリューションを開発している場合、内部要素を使用することはそれほど重要ではないと思います。通常、クライアントは常にあなたと連絡を取ったり、コードにアクセスしたりするためです。ただし、ライブラリ開発者にとってはかなり重要です。
internal キーワードを使用したことがある唯一のものは、製品のライセンス チェック コードです ;-)
オブジェクト指向パラダイムにうまく適合しないクラスまたはメソッドがあり、危険なことを行い、自分の制御下にある他のクラスやメソッドから呼び出す必要があり、他の人に使用させたくない場合.
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}