外部アセンブリから内部クラスにアクセスするにはどうすればよいですか?


97

オブジェクトタイプを返すメソッドを持っているが、実際には内部タイプである、変更できない(ベンダー提供の)アセンブリがある。

アセンブリからオブジェクトのフィールドやメソッドにアクセスするにはどうすればよいですか?

ベンダー提供のアセンブリは変更できないことに注意してください。

要するに、これが私が持っているものです:

ベンダーから:

internal class InternalClass
  public string test;
end class

public class Vendor
  private InternalClass _internal;
  public object Tag {get{return _internal;}}
end class

ベンダーアセンブリを使用して私のアセンブリから。

public class MyClass
{
  public void AccessTest()
  {
    Vendor vendor = new Vendor();
    object value = vendor.Tag;
    // Here I want to access InternalClass.test
  }
}

回答:


83

タイプへのアクセス権がない場合(「InternalsVisibleTo」などがない場合)、リフレクションを使用する必要があります。しかし、より良い質問は次のとおりです。このデータにアクセスする必要がありますか?それはパブリックタイプのコントラクトの一部ではありません...それは不透明なオブジェクトとして扱われることを意図しているように聞こえます(それらはあなたの目的ではなく)。

パブリックインスタンスフィールドとして説明しました。これをリフレクションで取得するには:

object obj = ...
string value = (string)obj.GetType().GetField("test").GetValue(obj);

プロパティではなく(フィールドではない)場合:

string value = (string)obj.GetType().GetProperty("test").GetValue(obj,null);

非公開の場合はBindingFlagsGetField/ のオーバーロードを使用する必要がありますGetProperty

重要な脇に:このような反射に注意してください。実装は次のバージョンで変更される(コードを壊す)か、難読化される(コードを壊す)か、十分な「信頼」がない(コードを壊す)可能性があります。パターンを見つけていますか?


マーク私は疑問に思います...プライベートフィールド/プロパティにアクセスすることは可能ですが、正しい値を使用してGetValueによって返されたオブジェクトをキャストする方法はありますか?
コーディングアドベンチャー2015

1
@GiovanniCampo静的に型がわかっている場合:確かに-キャストするだけです。タイプが静的にわからない場合-これが何を意味するの
マークグラベル

実際にパブリックAPIの一部である内部として物を公開する人々はどうですか?または、彼らは使用しましたInternalsVisibleToが、アセンブリを含めませんでしたか?シンボルが本当に隠されていない場合、それはABIの一部です。
binki 2017年

208

内部メンバーを別のアセンブリに公開することを許可するケースが1つだけあります。これはテスト目的です。

「Friend」アセンブリが内部にアクセスできるようにする方法があると言います:

プロジェクトのAssemblyInfo.csファイルで、各アセンブリの行を追加します。

[assembly: InternalsVisibleTo("name of assembly here")]

この情報はここから入手できます。

お役に立てれば。


テスト目的の場合の拡張。さまざまなコンテキストで内部を結合したシナリオ。たとえば、Unityには、ランタイムアセンブリ、テストアセンブリ、およびエディターアセンブリがある場合があります。ランタイム内部は、アセンブリをテストおよび編集するために表示される必要があります。
Steve Buzonas

3
私はこの回答は役に立たないと思います-OPはベンダーのアセンブリを変更できないと言っています。この回答はベンダーのプロジェクトのAssemblyInfo.csファイルの変更について話します
Simon Green

6

元のアセンブリを拡張できないという1つのポイントを主張したいと思います。Mono.Cecilを使用すると、3ptyアセンブリに注入できます[InternalsVisibleTo(...)]。法的な影響-3ptyアセンブリと技術的な影響-アセンブリに厳密な名前がある場合、アセンブリを取り除くか、別のキーで再署名する必要があることに注意してください。

 Install-Package Mono.Cecil

そして次のようなコード:

static readonly string[] s_toInject = {
  // alternatively "MyAssembly, PublicKey=0024000004800000... etc."
  "MyAssembly"
};

static void Main(string[] args) {
  const string THIRD_PARTY_ASSEMBLY_PATH = @"c:\folder\ThirdPartyAssembly.dll";

   var parameters = new ReaderParameters();
   var asm = ModuleDefinition.ReadModule(INPUT_PATH, parameters);
   foreach (var toInject in s_toInject) {
     var ca = new CustomAttribute(
       asm.Import(typeof(InternalsVisibleToAttribute).GetConstructor(new[] {
                      typeof(string)})));
     ca.ConstructorArguments.Add(new CustomAttributeArgument(asm.TypeSystem.String, toInject));
     asm.Assembly.CustomAttributes.Add(ca);
   }
   asm.Write(@"c:\folder-modified\ThirdPartyAssembly.dll");
   // note if the assembly is strongly-signed you need to resign it like
   // asm.Write(@"c:\folder-modified\ThirdPartyAssembly.dll", new WriterParameters {
   //   StrongNameKeyPair = new StrongNameKeyPair(File.ReadAllBytes(@"c:\MyKey.snk"))
   // });
}

1
私にとって、変更できないとは、それがnugetからのものであり、変更を加えたローカルnugetを作成および管理する必要がないことを意味します。また、一部の人々にとっては、厳密な名前を失うことが重要になります。しかし、これは興味深い点です。
binki 2017年

3

反射。

using System.Reflection;

Vendor vendor = new Vendor();
object tag = vendor.Tag;

Type tagt = tag.GetType();
FieldInfo field = tagt.GetField("test");

string value = field.GetValue(tag);

力を賢く使ってください。エラーチェックを忘れないでください。:)


-2

まあ、できません。内部クラスはアセンブリの外からは見えないので、直接アクセスする明示的な方法はありません-もちろんAFAIK。唯一の方法は、リフレクションを介してランタイムレイトバインディングを使用することです。これにより、内部クラスから間接的にメソッドとプロパティを呼び出すことができます。


5
リフレクションで何でも呼び出すことができます
MrHinsh-Martin Hinshelwood 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.