C#のinternal
キーワードの実際の使用方法について教えてください。
internal
修飾子は現在のアセンブリへのアクセスを制限することを知っていますが、いつ、どの状況でそれを使用すべきですか?
同じアセンブリ内の他の多くのクラスからアクセスしたいが、他のアセンブリ内のコードにアクセスできないようにするためのユーティリティクラスまたはヘルパークラス/メソッド。
MSDN から(archive.orgを介して):
内部アクセスの一般的な用途は、コンポーネントベースの開発です。これにより、コンポーネントのグループが残りのアプリケーションコードに晒されることなく、プライベートな方法で協調することが可能になります。たとえば、グラフィカルユーザインタフェースを構築するためのフレームワークは、内部アクセス権を持つメンバを使用して協調するControlクラスとFormクラスを提供できます。これらのメンバは内部的なものなので、フレームワークを使用しているコードには公開されません。
内部修飾子を InternalsVisibleTo
アセンブリレベル属性と一緒に使用して、ターゲットのアセンブリ内部クラスへの特別なアクセスを許可された "フレンド"アセンブリを作成することもできます。
これは、テストされるアセンブリの内部メンバを呼び出すことを許可されているユニットテストアセンブリの作成に役立ちます。もちろん、他のアセンブリにはこのレベルのアクセス権が付与されていないため、システムを解放してもカプセル化は維持されます。
BobがBigImportantClassを必要とする場合、Bobは、プロジェクトAを所有する人々に、BigImportantClassが自分のニーズを満たすように作成され、自分のニーズを満たすことを確認するためにテストされ、それはもはや彼のニーズを満たさないように変更されないことを確実にするために設置されるでしょう。
あるクラスが内部クラスの場合は、そのプロセスを経る必要はありません。これにより、プロジェクトAの予算を節約し、他のことに費やすことができます。
内部のポイントはそれがボブの人生を困難にするということではありません。プロジェクトAが機能、寿命、互換性などについてどのような高額の約束をしているのかを制御できるということです。
Internalを使用するもう1つの理由は、バイナリを難読化する場合です。難読化担当者は、パブリッククラスの名前をスクランブルすることはできませんが、内部クラスのクラス名をスクランブルすることは安全であることを認識しています。
大量の複雑な機能を単純なパブリックAPIにカプセル化するDLLを作成している場合は、パブリックに公開されないクラスメンバーには "internal"が使用されます。
隠れている複雑さ(別名カプセル化)は、高品質ソフトウェア工学の主要な概念です。
Internalキーワードは、管理されていないコードの上にラッパーを構築するときに頻繁に使用されます。
DllImportを使用するC/C++ベースのライブラリがある場合は、これらの関数をクラスの静的関数としてインポートし、それらを内部にすることができます。したがって、ユーザーはラッパーにのみアクセスでき、元のAPIにはアクセスできません。何でも台無しに。関数は静的なので、必要な複数のラッパークラスのために、アセンブリ内のあらゆる場所で使用できます。
Mono.Cairoを見てください。これは、このアプローチを使用するcairoライブラリーのラッパーです。
私は他のアセンブリから明示的にアクセスする必要があるまで、他のクラスのメソッドなど、アクセスする必要がある場所では、「厳密に修飾子として使用する」という規則に基づいています。
Assemblyインターフェースは通常そのクラスのインターフェースの合計よりも狭いので、私がそれを使用する場所はかなりたくさんあります。
私は内部がはるかに酷使されていると思います。特定の機能を特定のクラスだけに公開して、他の消費者には公開しないようにするべきです。
私の考えでは、これはインタフェースを壊し、抽象を壊します。これは決して使われるべきではないと言うことではありませんが、より良い解決策は異なるクラスにリファクタリングするか、可能ならば異なる方法で使われることです。しかし、これは必ずしも可能ではないかもしれません。
それが問題を引き起こす可能性がある理由は、他の開発者があなたのものと同じアセンブリの中で別のクラスを構築することに対して課金される可能性があることです。内部構造を持つことは抽象化の明快さを減少させ、そして誤用されると問題を引き起こす可能性があります。それはあなたがそれを公表したのと同じ問題になるでしょう。他の開発者によって構築されている他のクラスは、他のクラスと同じように、依然としてコンシューマです。クラスの抽象化とカプセル化は、外部クラスを保護するためのものではありませんが、すべてのクラスを保護するためのものではありません。
別の問題は、多くの開発者がthinkアセンブリの他の場所でそれを使用し、とにかく内部としてそれをマークする必要があるかもしれないということです。 。他の開発者はそれからそのためにそこにいると考えるかもしれません。通常は、最終的なニーズがあるまでプライベートにします。
しかし、これのいくつかは主観的になることがあり、私はそれが決して使われるべきではないと言っているのではありません。必要に応じて使用してください。
私が覚えていないことができますブログで、興味深い日を先日、たぶん週に見ました。基本的に私はこれを信用することはできませんが、私はそれがいくつかの有用なアプリケーションを持っているかもしれないと思いました。
抽象的なクラスが他のアセンブリによって見られることを望みましたが、誰かがそれから継承できるようにしたくないとしましょう。密封されていると動作しません。理由は抽象的だからです。そのアセンブリの他のクラスはそれを継承しています。他のアセンブリ内のどこかでParentクラスを宣言したい場合があるため、Privateは機能しません。
名前空間Base.Assembly { パブリック抽象クラスParent { 内部抽象void SomeMethod(); } [。 //これは同じアセンブリ内にあるため、問題なく動作します。 パブリッククラスChildWithin:Parent { 内部オーバーライドvoid SomeMethod()[。 { } } } 名前空間Another.Assembly { // Kaboom内部メソッドをオーバーライドすることはできないためです。パブリッククラスChildOutside:Parent { } パブリッククラスTest { //丁度良い 私立の親_親; public Test() { /まだ細かい _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/ja-jp/library/7c5ka91b(VS.80).aspx =
Internalの非常に興味深い使い方 - もちろん、internalのメンバーは宣言されているAssemblyだけに限定されている - はそれからある程度「友達」の機能を得ています。友人のメンバーは、それが宣言されている議会の外の特定の他の議会にのみ表示されるものです。 C#はFriendをサポートしていませんが、CLRはサポートしています。
フレンドアセンブリを宣言するには InternalsVisibleToAttribute を使用できます。フレンドアセンブリ内からの参照はすべて、宣言しているアセンブリの内部メンバをフレンドアセンブリの範囲内でパブリックとして扱います。これに関する問題は、すべての内部メンバーが見えることです。あなたが選ぶことはできません。
InternalsVisibleToの良い使い方は、さまざまな内部メンバを単体テストアセンブリに公開することです。これにより、これらのメンバをテストするための複雑なリフレクションの回避策が不要になります。目に見えるすべての内部メンバーはそれほど問題ではありませんが、このアプローチをとることはあなたのクラスインターフェースをかなりひどく台無しにし、宣言しているAssembly内のカプセル化を台無しにする可能性があります。
現在の国会の範囲内でアクセス可能でなければならないメソッド、クラスなどがあるとき。
たとえば、DALはORMを持つことができますが、オブジェクトはビジネスレイヤに公開されるべきではなく、すべての対話は静的メソッドを介して行われ、必要なパラメータを渡す必要があります。
経験則として、2種類のメンバーがあります。
内部クラスを使用すると、アセンブリのAPIを制限することができます。これは、APIを理解しやすくするなどの利点があります。
また、アセンブリにバグがある場合は、修正によって重大な変更が加えられる可能性は低くなります。内部クラスがないと、クラスのパブリックメンバーを変更することは重大な変更になると考える必要があります。内部クラスでは、そのパブリックメンバーを変更してもアセンブリ(およびInternalsVisibleTo属性で参照されるアセンブリ)の内部APIのみが破損すると見なすことができます。
クラスレベルとアセンブリレベルでのカプセル化が好きです。これに同意しない人もいますが、機能が利用可能であることを知っているのはいいことです。
ノイズリダクション、公開するタイプが少ないほど、ライブラリは簡単になります。改ざん防止/セキュリティは別の方法です(Reflectionはそれに勝つことができますが)。
データバックエンドにLINQ-to-SQLを使用するプロジェクトがあります。私は2つの主要な名前空間を持っています:BizとDataです。 LINQデータモデルはDataにあり、 "internal"とマークされています。 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
にすることが理にかなっている場合があります。一例として、クラスのインスタンス化方法を制御したい場合があります。クラスのインスタンスを作成するための何らかのファクトリを用意したとしましょう。コンストラクタをinternal
にすると、ファクトリ(同じアセンブリ内に存在する)がクラスのインスタンスを作成できるようになりますが、そのアセンブリ外のコードでは作成できなくなります。
ただし、クラスやメンバーをinternal
にすることには、特別な理由がないと意味がありません。特別な理由なくpublic
やprivate
にするのは意味がありません。
internalキーワードを使ったことがあるのは私の製品のライセンスチェックコードだけです;-)
Internalキーワードの用途の1つは、アセンブリのユーザーから具体的な実装へのアクセスを制限することです。
オブジェクトを構築するためのファクトリまたはその他の中心的な場所がある場合は、アセンブリのユーザーはパブリックインターフェイスまたは抽象基本クラスを扱うだけで済みます。
また、内部コンストラクタを使用すると、パブリッククラスをどこでいつインスタンス化するかを制御できます。
public
として定義されたクラスは、誰かがあなたのプロジェクトの名前空間を見たときに自動的にインテリセンスに現れることを覚えておいてください。 APIの観点からは、プロジェクトのユーザーには、自分が使用できるクラスのみを表示することが重要です。見えないはずのものを隠すには、internal
キーワードを使用します。
プロジェクトAのBig_Important_Class
がプロジェクト外での使用を目的としている場合は、internal
とマークしないでください。
ただし、多くのプロジェクトでは、実際にはプロジェクト内での使用のみを目的としたクラスがよくあります。たとえば、パラメータ化されたスレッド呼び出しへの引数を保持するクラスがあるとします。このような場合、意図しないAPIの変更から身を守ること以外に理由がない場合は、それらをinternal
としてマークする必要があります。
これはどうですか:通常はListオブジェクトをAssemblyの外部ユーザーに公開せず、IEnumerableを公開することをお勧めします。しかし、アセンブリ内でListオブジェクトを使用する方がはるかに簡単です。これは、配列構文やその他のすべてのListメソッドを取得できるためです。したがって、私は通常、アセンブリ内で使用されるListを公開する内部プロパティを持っています。
このアプローチについてのコメントは大歓迎です。
その考え方は、あなたがライブラリをデザインしているとき、(あなたのライブラリのクライアントによって)外部からの使用を意図されているクラスだけが公開されるべきであるということです。このようにして、そのクラスを隠すことができます。
等.
内部要素を使用するよりも社内ソリューションを開発している場合、それほど重要ではありません。通常、クライアントは常にあなたと連絡を取ったり、コードにアクセスしたりするためです。とはいえ、それらは図書館開発者にとってかなり重要です。