C#での「内部」キーワードの実用的な使用


420

internalC#でのキーワードの実際的な使用法を教えていただけませんか?

internal修飾子は現在のアセンブリへのアクセスを制限することを知っていますが、いつ、どのような状況で使用する必要がありますか?

回答:


379

同じアセンブリ内の他の多くのクラスからアクセスしたいが、他のアセンブリのコードがアクセスできないようにしたいユーティリティまたはヘルパークラス/メソッド。

MSDNから(archive.org経由):

内部アクセスの一般的な用途は、コンポーネントベースの開発です。これにより、コンポーネントのグループが、残りのアプリケーションコードにさらされることなく、プライベートな方法で連携できるようになります。たとえば、グラフィカルユーザーインターフェイスを構築するためのフレームワークは、内部アクセス権を持つメンバーを使用して連携するControlおよびFormクラスを提供できます。これらのメンバーは内部にあるため、フレームワークを使用しているコードに公開されません。

また、内部修飾子をInternalsVisibleToアセンブリレベル属性と共に使用して、ターゲットアセンブリの内部クラスへの特別なアクセス権が付与された「フレンド」アセンブリを作成することもできます。

これは、テストするアセンブリの内部メンバーを呼び出すことができるユニットテストアセンブリの作成に役立ちます。もちろん、他のアセンブリにはこのレベルのアクセスが許可されていないため、システムを解放してもカプセル化は維持されます。


8
InternalsVisibleTo属性は非常に役立ちます。ありがとうございました。
2011年

33
私の感じは、内部が必要になった瞬間から、どこかでデザインに問題があるということです。私見、すべての問題を解決するために純粋なオブジェクト指向を使用できるはずです。この瞬間、私は.NET Frameworkソースでの約100万回の内部使用を監視しており、自分が使用するものを活用する方法を制限しています。内部を使用することで、表面的にモジュール化されたモノリスを作成しましたが、ものを分離しようとすると壊れます。また、救済への反映があるので、セキュリティは議論ではありません。
絶望の

26
@GrimaceofDespair:ここに同意のコメントを追加します。私は個人的に、内部を大規模なマルチプロジェクトソリューションにおける「パブリック」修飾子の非常に有用なバージョンと見なしています。この修飾子を私のサブプロジェクトのクラスに追加すると、ソリューションの他のサブプロジェクトによるこのクラスの「ランダムな使用」が禁止され、ライブラリを適切に使用することができます。後で何らかのリファクタリングを行う必要がある場合は、「待つ必要がありますが、なぜこの男は、この特定の計算内で自分の目的にのみ必要なPointクラスのインスタンスを作成したのですか」と自問する必要はありません。
KT。

3
言い換えると、誰もがSmtpClientの内部に依存しているときに、ある時点でバグが発見された場合、.NETはそのバグを修正するために深刻な問題を抱えているでしょう。さまざまな内部機能とバグを利用したすべての古いプログラムとの「バグの互換性」を保つためにWindows開発者が実行しなければならない期間を説明した面白い記事を一度読んだことを覚えています。.NETでこのエラーを繰り返しても意味がありません。
KT。

13
私は内部が公共よりもずっと便利だと思います。publicを使用するのは、「ここで、ユーザー-使用するための便利な機能を提供しています」と明示的に言っているときだけです。ユーザーライブラリではなくアプリケーションを作成している場合は、外部から呼び出す関数をユーザーに提供するつもりはないので、すべてが内部である必要があります。ユーザーライブラリを作成している場合は、ユーザーに提供し、維持、サポート、および時間の終わりまで保持することが期待される便利な関数のみを公開する必要があります。それ以外の場合は、内部にします。
Darrell Plank

85

BobがBigImportantClassを必要とする場合、BobはプロジェクトAを所有する人々にサインアップして、BigImportantClassが彼のニーズを満たすように記述され、それが彼のニーズを満たすことを確認するためにテストされ、彼のニーズを満たすこと、およびそのプロセスが文書化されることを保証する必要があります。彼のニーズを満たさなくなるように変更されないようにするために配置されます。

クラスが内部クラスの場合、そのプロセスを実行する必要はありません。これにより、プロジェクトAの予算が節約され、他のことに費やすことができます。

内部のポイントは、それがボブにとって人生を困難にするということではありません。それは、プロジェクトAが機能、寿命、互換性などに関してどのような費用のかかる約束をするかを制御できるということです。


5
完全に理にかなっています。ここでは大人全員が同意しているので、内部メンバーをオーバーライドできることを願っています。
cwallenpoole 2013年

6
@EricLippert私が言ったことと全く同じではありません。「内部」が含まれているコンパイル済みコードを継承すると、行き詰まりますよね?リフレクションを使用しない限り、コンパイラに「いいえ、他の開発者があなたに嘘をついた。代わりに私を信頼してください」と単純に伝える方法はありません。
cwallenpoole 2013年

11
@cwallenpoole:自分が好きではないコードを書く嘘つきが書いたコードを使用している場合は、そのコードの使用をやめて、自分の好きなコードを書いてください。
Eric Lippert 2013年

2
@EricLippertそれは頬の舌であるはずだった、私は気分を害するつもりはなかった。(私は主に、私がそのような場合にSOLであることを確認しようとしていました)。
cwallenpoole 2013年

5
@cwallenpoole:私は少なくとも気分を害していません。あなたがその不幸な状況で行き詰まったと感じたら、私は良いアドバイスを提供します。
Eric Lippert、2013年

60

internalを使用するもう1つの理由は、バイナリを難読化する場合です。難読化ツールは、内部クラスのクラス名をスクランブルしても安全であることを知っていますが、パブリッククラスの名前はスクランブルできません。これは、既存の参照を破壊する可能性があるためです。


4
Pff。逆アセンブラでバイトコードを見ると、コードが何を行うかが明らかになります。難読化ツールは、内部に侵入しようとするすべての人にとって、ほとんど穏やかな抑止力にはなりません。ハッカーが便利な関数名を取得できなかっただけで停止したという話を聞いたことはありません。
Nyerguds

28
泥棒が鍵で止められたことを聞いたことがないので、寝ているときにドアを閉めないでください。
セルジオ

4
そのため、ソースコードのクラス名と変数名をスクランブル化しています。あなたはPumpStateComparatorが何であるかを理解することができるかもしれませんが、LpWj4mWEが何であるかを推測できますか?#jobsecurity(私はこれをしません、そして実際にはこれをしないでください、インターネットの人々。)
Slothario

32

大量の複雑な機能を単純なパブリックAPIにカプセル化するDLLを作成している場合、パブリックに公開されないクラスメンバーで「内部」が使用されます。

複雑さを隠すこと(別名カプセル化)は、高品質なソフトウェアエンジニアリングの主要な概念です。


20

内部キーワードは、非マネージコードのラッパーを構築するときに頻繁に使用されます。

DllImportしたいC / C ++ベースのライブラリがある場合、これらの関数をクラスの静的関数としてインポートし、それらを内部にすることができるため、ユーザーはラッパーにのみアクセスでき、元のAPIにはアクセスできないため、それを行うことはできません。何でも台無しにします。関数は静的なので、必要な複数のラッパークラスに対して、アセンブリ内のどこでも使用できます。

Mono.Cairoを見てください。これは、このアプローチを使用するcairoライブラリーのラッパーです。


12

「できる限り厳密な修飾子として使用する」ルールに基づいて、別のアセンブリから明示的にアクセスする必要があるまで、別のクラスからメソッドにアクセスする必要がある場合はどこでも内部を使用します。

アセンブリインターフェイスは通常、そのクラスインターフェイスの合計よりも狭いため、使用する場所はかなり多くあります。


3
「できる限り厳密な修飾子を使用する」なら、なぜ非公開ではないのですか?
R.マルティーニョフェルナンデス

6
クラスのプロパティ/メソッドがクラスの外部にアクセスする必要がある場合、および別のasseからアクセスする必要がある場合に、privateは厳しすぎるため、blyはそれほど頻繁ではありません。
Ilya Komakhin 09

11

私は内部がかなり使いすぎていると思います。他のコンシューマに公開しない特定のクラスにのみ特定の機能を公開するべきではありません。

これは私の意見では、インターフェースを壊し、抽象化を壊します。これは決して使用してはならないということではありませんが、別のクラスにリファクタリングするか、可能であれば別の方法で使用することをお勧めします。ただし、これが常に可能であるとは限りません。

それが問題を引き起こす可能性がある理由は、別の開発者があなたのクラスと同じアセンブリで別のクラスをビルドすることを課される可能性があることです。内部構造があると、抽象化の明確さが低下し、誤用された場合に問題が発生する可能性があります。公開したのと同じ問題です。他の開発者によって構築されている他のクラスは、外部クラスと同じように、まだコンシューマです。クラスの抽象化とカプセル化は、外部クラスの保護のためだけではなく、すべてのクラスのためのものです。

もう1つの問題は、多くの開発者が、アセンブリの別の場所でそれを使用する必要があると考え、その時点でそれを必要としない場合でも、とにかく内部としてマークすることです。別の開発者は、そのためにそこにあると考えるかもしれません。通常、決定的な必要があるまでプライベートにマークを付けます。

しかし、これのいくつかは主観的である可能性があり、私はそれが決して使用されるべきではないと言っていません。必要なときにだけ使用してください。


私はしばしば、同じアセンブリ内の他のドメインオブジェクトに対してパブリックである必要があるメンバーを持つドメインオブジェクトを構築します。ただし、これらの同じメンバーは、アセンブリの外部にあるクライアントコードでは使用できません。このような状況では、「内部」が非常に適切です。
ロックアンソニージョンソン

8

先日、たぶん週に、私が覚えていないブログで興味深い記事を見ました。基本的に私はこれを信用することはできませんが、いくつかの便利なアプリケーションがあるかもしれないと思いました。

抽象クラスを別のアセンブリに表示したいが、誰かがそれから継承できないようにしたいとします。シールは理由により抽象的であるため機能しません。そのアセンブリ内の他のクラスはそれから継承します。他のアセンブリのどこかでParentクラスを宣言したい場合があるため、Privateは機能しません。

名前空間Base.Assembly
{
  パブリック抽象クラスParent
  {
    内部抽象void SomeMethod();
  }

  //同じアセンブリ内にあるため、これは問題なく機能します。
  パブリッククラスChildWithin:親
  {
    内部オーバーライドvoid SomeMethod()
    {
    }
  }
}

名前空間Another.Assembly
{
  // Kaboom、内部メソッドをオーバーライドできないため
  パブリッククラスChildOutside:親
  {
  }

  公開クラスTest 
  { 

    //大丈夫
    プライベートペアレント_parent;

    公開テスト()
    {
      //それでも問題ない
      _parent = new ChildWithin();
    }
  }
}

ご覧のとおり、継承することなく親クラスを効果的に使用できます。


7

この例には、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


6

現在のアセンブリのスコープ内でアクセスできる必要があるメソッドやクラスなどがある場合。

たとえば、DALにORMがある場合でも、オブジェクトをビジネスレイヤーに公開しないでください。すべての対話は静的メソッドを介して実行し、必要なパラメーターを渡す必要があります。


6

internalの非常に興味深い使用法-もちろん内部メンバーは宣言されたアセンブリのみに限定されます-から "フレンド"機能がある程度得られます。フレンドメンバーとは、宣言されているアセンブリの外部にある特定の他のアセンブリにのみ表示されるものです。C#にはフレンドの組み込みサポートはありませんが、CLRにはあります。

InternalsVisibleToAttributeを使用できますをしてフレンドアセンブリを宣言フレンドアセンブリ内からのすべての参照は、宣言アセンブリの内部メンバーをフレンドアセンブリのスコープ内でパブリックとして扱います。これに関する問題は、すべての内部メンバーが表示されることです。選ぶことはできません。

InternalsVisibleToの適切な用途は、さまざまな内部メンバーを単体テストアセンブリに公開することです。これにより、これらのメンバーをテストするための複雑なリフレクション作業の必要がなくなります。すべての内部メンバーが表示されることはそれほど問題ではありませんが、このアプローチを取ると、クラスインターフェイスがかなり重くなり、宣言アセンブリ内のカプセル化が損なわれる可能性があります。


5

経験則として、2種類のメンバーがあります。

  • パブリックサーフェス:外部アセンブリから可視(パブリック、保護、内部保護):呼び出し元は信頼されていないため、パラメーターの検証、メソッドのドキュメント化などが必要です。
  • プライベートサーフェス:外部アセンブリ(プライベートクラスと内部クラス、または内部クラス)からは見えません。呼び出し側は通常信頼されているため、パラメーターの検証、メソッドのドキュメントなどは省略できます。

4

ノイズ低減、公開する型が少ないほど、ライブラリはシンプルになります。改ざん防止/セキュリティは別のものです(Reflectionはこれに勝つことができます)。


4

内部クラスを使用すると、アセンブリのAPIを制限できます。これには、APIを理解しやすくするなどの利点があります。

また、アセンブリにバグが存在する場合は、修正によって重大な変更が発生する可能性が低くなります。内部クラスがないと、クラスのパブリックメンバーを変更すると重大な変更になると想定する必要があります。内部クラスでは、パブリックメンバーを変更しても、アセンブリ(およびInternalsVisibleTo属性で参照されているすべてのアセンブリ)の内部APIのみが壊れると想定できます。

クラスレベルとアセンブリレベルでカプセル化を行うのが好きです。これに反対する人もいますが、機能が利用できることを知っておくのは良いことです。


2

データのバックエンドに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多くを使用したのは初めてであり、非常に便利であることが証明されています。


2

クラスのメンバーにすることが理にかなっている場合がありますinternal。1つの例は、クラスのインスタンス化方法を制御する場合です。クラスのインスタンスを作成するためのある種のファクトリを提供するとします。コンストラクターを作成しinternalて、ファクトリー(同じアセンブリーに常駐)がクラスのインスタンスを作成できるようにすることができますが、そのアセンブリーの外部のコードは作成できません。

ただし、クラスやメンバーinternalを特定の理由なしに作成することはpublic、それらを作成するのが理にかなっているほど、またはprivate特定の理由なしに、まったく意味がありません。



1

internalキーワードの1つの用途は、アセンブリのユーザーからの具体的な実装へのアクセスを制限することです。

オブジェクトを構築するためのファクトリーまたはその他の中心的な場所がある場合、アセンブリのユーザーは、パブリックインターフェイスまたは抽象基本クラスを処理するだけで済みます。

また、内部コンストラクターを使用すると、パブリッククラスがインスタンス化される場所とインスタンスをいつでも制御できます。


1

これはどうですか:通常、Listオブジェクトをアセンブリの外部ユーザーに公開せず、IEnumerableを公開することをお勧めします。ただし、配列構文やその他すべてのListメソッドを取得できるため、アセンブリ内でListオブジェクトを使用する方がはるかに簡単です。したがって、私は通常、アセンブリ内で使用されるリストを公開する内部プロパティを持っています。

このアプローチについてのコメントは大歓迎です。


@Samik-「通常、Listオブジェクトをアセンブリの外部ユーザーに公開せず、IEnumerableを公開することをお勧めします。」なぜ列挙可能が好ましいのですか?
Howiecamp 2010

1
@Howiecamp:ほとんどの場合、IEnumerableはリストへの読み取り専用アクセスを提供するため、リストを直接公開するかのように、意図せずにクライアントによって変更される可能性があります(削除要素など)。
Samik R 2010

また、ListまたはIListを公開すると、常に(たとえば)データへの高速ランダムアクセスを許可するコントラクトが作成されます。いつかメソッドを変更して別のコレクションを使用するか、コレクションをまったく使用しない(yield return)と決めた場合は、値を返すためだけにリストを作成するか、メソッドの戻り値の型を変更して契約を解除する必要があります。より具体的でない戻り値の型を使用すると、柔軟性が得られます。

1

として定義されたクラスpublicは、誰かがプロジェクトの名前空間を見ると自動的にインテリセンスに表示されることに注意してください。APIの観点からは、プロジェクトのユーザーに、使用できるクラスのみを示すことが重要です。使用internalキーワードを表示すべきでないものを非表示にします。

あなたの場合はBig_Important_Class、プロジェクトAは、プロジェクト外の使用を意図しているため、その後、あなたはそれをマークしてはいけませんinternal

ただし、多くのプロジェクトでは、プロジェクト内での使用のみを意図したクラスがしばしば存在します。たとえば、パラメーター化されたスレッド呼び出しへの引数を保持するクラスがあるとします。これらの場合、internal意図しないAPIの変更から自分を保護する以外の理由がないように、それらをマークする必要があります。


代わりにプライベートを使用しないのはなぜですか?
囚人ZERO

1
privateキーワードは、別のクラスまたは構造体の中に定義されているクラスまたは構造体に使用することができます。名前空間内で定義されたクラスは、publicまたはのみを宣言できinternalます。
マット・デイビス

1

ライブラリを設計する場合、外部から(ライブラリのクライアントが)使用することを目的としたクラスのみを公開する必要があるという考え方です。このようにして、

  1. 将来のリリースで変更される可能性があります(公開されている場合、クライアントコードが破損します)
  2. クライアントには役に立たず、混乱を引き起こす可能性があります
  3. 安全ではありません(したがって、不適切に使用すると、ライブラリがかなり破損する可能性があります)

社内ソリューションを開発している場合、内部要素を使用することはそれほど重要ではないと思います。通常、クライアントは常にあなたと接触したり、コードにアクセスしたりするためです。ただし、ライブラリ開発者にとっては非常に重要です。


-7

オブジェクト指向パラダイムに完全に適合しないクラスやメソッドがあり、危険なことを行い、制御下にある他のクラスやメソッドから呼び出す必要があり、他の人に使用させたくない場合。

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}

何を言おうとしているのですか?それはあまり明確ではありません。少なくとも私には。
pqsk 2012

@pqskに同意
Anynomous Khan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.