internal
C#でのキーワードの実際的な使用法を教えていただけませんか?
internal
修飾子は現在のアセンブリへのアクセスを制限することを知っていますが、いつ、どのような状況で使用する必要がありますか?
internal
C#でのキーワードの実際的な使用法を教えていただけませんか?
internal
修飾子は現在のアセンブリへのアクセスを制限することを知っていますが、いつ、どのような状況で使用する必要がありますか?
回答:
同じアセンブリ内の他の多くのクラスからアクセスしたいが、他のアセンブリのコードがアクセスできないようにしたいユーティリティまたはヘルパークラス/メソッド。
MSDNから(archive.org経由):
内部アクセスの一般的な用途は、コンポーネントベースの開発です。これにより、コンポーネントのグループが、残りのアプリケーションコードにさらされることなく、プライベートな方法で連携できるようになります。たとえば、グラフィカルユーザーインターフェイスを構築するためのフレームワークは、内部アクセス権を持つメンバーを使用して連携するControlおよびFormクラスを提供できます。これらのメンバーは内部にあるため、フレームワークを使用しているコードに公開されません。
また、内部修飾子をInternalsVisibleTo
アセンブリレベル属性と共に使用して、ターゲットアセンブリの内部クラスへの特別なアクセス権が付与された「フレンド」アセンブリを作成することもできます。
これは、テストするアセンブリの内部メンバーを呼び出すことができるユニットテストアセンブリの作成に役立ちます。もちろん、他のアセンブリにはこのレベルのアクセスが許可されていないため、システムを解放してもカプセル化は維持されます。
BobがBigImportantClassを必要とする場合、BobはプロジェクトAを所有する人々にサインアップして、BigImportantClassが彼のニーズを満たすように記述され、それが彼のニーズを満たすことを確認するためにテストされ、彼のニーズを満たすこと、およびそのプロセスが文書化されることを保証する必要があります。彼のニーズを満たさなくなるように変更されないようにするために配置されます。
クラスが内部クラスの場合、そのプロセスを実行する必要はありません。これにより、プロジェクトAの予算が節約され、他のことに費やすことができます。
内部のポイントは、それがボブにとって人生を困難にするということではありません。それは、プロジェクトAが機能、寿命、互換性などに関してどのような費用のかかる約束をするかを制御できるということです。
internalを使用するもう1つの理由は、バイナリを難読化する場合です。難読化ツールは、内部クラスのクラス名をスクランブルしても安全であることを知っていますが、パブリッククラスの名前はスクランブルできません。これは、既存の参照を破壊する可能性があるためです。
大量の複雑な機能を単純なパブリックAPIにカプセル化するDLLを作成している場合、パブリックに公開されないクラスメンバーで「内部」が使用されます。
複雑さを隠すこと(別名カプセル化)は、高品質なソフトウェアエンジニアリングの主要な概念です。
内部キーワードは、非マネージコードのラッパーを構築するときに頻繁に使用されます。
DllImportしたいC / C ++ベースのライブラリがある場合、これらの関数をクラスの静的関数としてインポートし、それらを内部にすることができるため、ユーザーはラッパーにのみアクセスでき、元のAPIにはアクセスできないため、それを行うことはできません。何でも台無しにします。関数は静的なので、必要な複数のラッパークラスに対して、アセンブリ内のどこでも使用できます。
Mono.Cairoを見てください。これは、このアプローチを使用するcairoライブラリーのラッパーです。
「できる限り厳密な修飾子として使用する」ルールに基づいて、別のアセンブリから明示的にアクセスする必要があるまで、別のクラスからメソッドにアクセスする必要がある場合はどこでも内部を使用します。
アセンブリインターフェイスは通常、そのクラスインターフェイスの合計よりも狭いため、使用する場所はかなり多くあります。
私は内部がかなり使いすぎていると思います。他のコンシューマに公開しない特定のクラスにのみ特定の機能を公開するべきではありません。
これは私の意見では、インターフェースを壊し、抽象化を壊します。これは決して使用してはならないということではありませんが、別のクラスにリファクタリングするか、可能であれば別の方法で使用することをお勧めします。ただし、これが常に可能であるとは限りません。
それが問題を引き起こす可能性がある理由は、別の開発者があなたのクラスと同じアセンブリで別のクラスをビルドすることを課される可能性があることです。内部構造があると、抽象化の明確さが低下し、誤用された場合に問題が発生する可能性があります。公開したのと同じ問題です。他の開発者によって構築されている他のクラスは、外部クラスと同じように、まだコンシューマです。クラスの抽象化とカプセル化は、外部クラスの保護のためだけではなく、すべてのクラスのためのものです。
もう1つの問題は、多くの開発者が、アセンブリの別の場所でそれを使用する必要があると考え、その時点でそれを必要としない場合でも、とにかく内部としてマークすることです。別の開発者は、そのためにそこにあると考えるかもしれません。通常、決定的な必要があるまでプライベートにマークを付けます。
しかし、これのいくつかは主観的である可能性があり、私はそれが決して使用されるべきではないと言っていません。必要なときにだけ使用してください。
先日、たぶん週に、私が覚えていないブログで興味深い記事を見ました。基本的に私はこれを信用することはできませんが、いくつかの便利なアプリケーションがあるかもしれないと思いました。
抽象クラスを別のアセンブリに表示したいが、誰かがそれから継承できないようにしたいとします。シールは理由により抽象的であるため機能しません。そのアセンブリ内の他のクラスはそれから継承します。他のアセンブリのどこかでParentクラスを宣言したい場合があるため、Privateは機能しません。
名前空間Base.Assembly { パブリック抽象クラスParent { 内部抽象void SomeMethod(); } //同じアセンブリ内にあるため、これは問題なく機能します。 パブリッククラスChildWithin:親 { 内部オーバーライドvoid SomeMethod() { } } } 名前空間Another.Assembly { // Kaboom、内部メソッドをオーバーライドできないため パブリッククラスChildOutside:親 { } 公開クラスTest { //大丈夫 プライベートペアレント_parent; 公開テスト() { //それでも問題ない _parent = new ChildWithin(); } } }
ご覧のとおり、継承することなく親クラスを効果的に使用できます。
この例には、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のにのアクセスレベルを変更公衆。また、メンバー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
internalの非常に興味深い使用法-もちろん内部メンバーは宣言されたアセンブリのみに限定されます-から "フレンド"機能がある程度得られます。フレンドメンバーとは、宣言されているアセンブリの外部にある特定の他のアセンブリにのみ表示されるものです。C#にはフレンドの組み込みサポートはありませんが、CLRにはあります。
InternalsVisibleToAttributeを使用できますをしてフレンドアセンブリを宣言フレンドアセンブリ内からのすべての参照は、宣言アセンブリの内部メンバーをフレンドアセンブリのスコープ内でパブリックとして扱います。これに関する問題は、すべての内部メンバーが表示されることです。選ぶことはできません。
InternalsVisibleToの適切な用途は、さまざまな内部メンバーを単体テストアセンブリに公開することです。これにより、これらのメンバーをテストするための複雑なリフレクション作業の必要がなくなります。すべての内部メンバーが表示されることはそれほど問題ではありませんが、このアプローチを取ると、クラスインターフェイスがかなり重くなり、宣言アセンブリ内のカプセル化が損なわれる可能性があります。
内部クラスを使用すると、アセンブリのAPIを制限できます。これには、APIを理解しやすくするなどの利点があります。
また、アセンブリにバグが存在する場合は、修正によって重大な変更が発生する可能性が低くなります。内部クラスがないと、クラスのパブリックメンバーを変更すると重大な変更になると想定する必要があります。内部クラスでは、パブリックメンバーを変更しても、アセンブリ(およびInternalsVisibleTo属性で参照されているすべてのアセンブリ)の内部APIのみが壊れると想定できます。
クラスレベルとアセンブリレベルでカプセル化を行うのが好きです。これに反対する人もいますが、機能が利用できることを知っておくのは良いことです。
データのバックエンドに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
は、誰かがプロジェクトの名前空間を見ると自動的にインテリセンスに表示されることに注意してください。APIの観点からは、プロジェクトのユーザーに、使用できるクラスのみを示すことが重要です。使用internal
キーワードを表示すべきでないものを非表示にします。
あなたの場合はBig_Important_Class
、プロジェクトAは、プロジェクト外の使用を意図しているため、その後、あなたはそれをマークしてはいけませんinternal
。
ただし、多くのプロジェクトでは、プロジェクト内での使用のみを意図したクラスがしばしば存在します。たとえば、パラメーター化されたスレッド呼び出しへの引数を保持するクラスがあるとします。これらの場合、internal
意図しないAPIの変更から自分を保護する以外の理由がないように、それらをマークする必要があります。
private
キーワードは、別のクラスまたは構造体の中に定義されているクラスまたは構造体に使用することができます。名前空間内で定義されたクラスは、public
またはのみを宣言できinternal
ます。
ライブラリを設計する場合、外部から(ライブラリのクライアントが)使用することを目的としたクラスのみを公開する必要があるという考え方です。このようにして、
等
社内ソリューションを開発している場合、内部要素を使用することはそれほど重要ではないと思います。通常、クライアントは常にあなたと接触したり、コードにアクセスしたりするためです。ただし、ライブラリ開発者にとっては非常に重要です。
オブジェクト指向パラダイムに完全に適合しないクラスやメソッドがあり、危険なことを行い、制御下にある他のクラスやメソッドから呼び出す必要があり、他の人に使用させたくない場合。
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}