외부 어셈블리에서 내부 클래스에 액세스하려면 어떻게해야합니까?


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);

비공개 인 경우 / BindingFlags오버로드 를 사용해야합니다 .GetFieldGetProperty

중요한 점 은 다음과 같습니다. 구현은 다음 버전에서 변경 될 수 있습니다 (코드 깨짐) 또는 난독 화 될 수 있거나 (코드 깨짐) "신뢰"가 충분하지 않을 수 있습니다 (코드 깨짐). 패턴을 발견하고 있습니까?


Marc 나는 궁금하다 ... 개인 필드 / 속성에 액세스 할 수 있지만 올바른 유형을 사용하여 GetValue에서 반환 한 개체를 캐스팅하는 방법이 있습니까?
codingadventures 2015 년

1
@GiovanniCampo 타입이 무엇인지 정적으로 알고 있다면 : 확실합니다. 유형을 정적으로 알지 못한다면-이것이 무엇을 의미
Marc Gravell

실제로 공개 API의 일부인 내부를 게시하는 사람들은 어떻습니까? 아니면 사용 InternalsVisibleTo했지만 어셈블리를 포함하지 않았습니까? 기호가 실제로 숨겨져 있지 않으면 ABI의 일부입니다.
binki 2017-10-04

208

내부 구성원에게 다른 어셈블리에 대한 노출을 허용하고 테스트 목적으로 허용하는 경우는 단 하나뿐입니다.

"친구"어셈블리가 내부에 액세스 할 수 있도록 허용하는 방법이 있다고 말합니다.

프로젝트의 AssemblyInfo.cs 파일에서 각 어셈블리에 대한 줄을 추가합니다.

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

이 정보는 여기에서 확인할 수 있습니다.

도움이 되었기를 바랍니다.


테스트 목적의 경우 확장. 서로 다른 컨텍스트에서 내부를 결합한 모든 시나리오. 예를 들어 Unity에는 런타임 어셈블리, 테스트 어셈블리 및 편집기 어셈블리가있을 수 있습니다. 런타임 내부는 테스트 및 편집 어셈블리에 표시되어야합니다.
Steve Buzonas

3
나는이 답변이 도움이 생각하지 않는다 - 영업 그가 공급 업체의 어셈블리를 수정할 수 없다는, 공급 업체의 프로젝트의 AssemblyInfo.cs 파일 수정에 대한이 답변 회담
사이먼 그린

6

[InternalsVisibleTo(...)]3pty 어셈블리에 주입 할 수있는 Mono.Cecil을 사용하여 원래 어셈블리를 확대 할 수 없다는 한 가지 요점을 주장 하고 싶습니다. 법적 의미가있을 수 있습니다. 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
나를 위해 수정할 수 없다는 것은 그것이 너겟에서 온다는 것을 의미하며 수정을 통해 로컬 너겟을 만들고 관리 할 필요가 없습니다. 또한 어떤 사람들에게는 강력한 이름을 잃는 것이 중요합니다. 그러나 이것은 흥미로운 점입니다.
binki 2017-10-04

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 - 마틴 하인 셀 우드에게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.