Jak mogę uzyskać dostęp do klasy wewnętrznej z zestawu zewnętrznego?


97

Posiadanie zestawu, którego nie mogę zmodyfikować (dostarczonego przez dostawcę), który ma metodę zwracającą typ obiektu, ale w rzeczywistości jest typu wewnętrznego.

Jak mogę uzyskać dostęp do pól i / lub metod obiektu z mojego zestawu?

Należy pamiętać, że nie mogę modyfikować zespołu dostarczonego przez dostawcę.

W skrócie, oto co mam:

Od dostawcy:

internal class InternalClass
  public string test;
end class

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

Z mojego zespołu przy użyciu zestawu dostawcy.

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

Odpowiedzi:


83

Bez dostępu do typu (i bez „InternalsVisibleTo” itp.) Musiałbyś użyć refleksji. Ale lepszym pytaniem byłoby: czy powinieneś mieć dostęp do tych danych? To nie jest część kontraktu typu publicznego ... wydaje mi się, że ma być traktowany jako nieprzezroczysty przedmiot (dla ich celów, a nie dla ciebie).

Opisałeś to jako pole wystąpienia publicznego; aby uzyskać to poprzez refleksję:

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

Jeśli faktycznie jest to właściwość (nie pole):

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

Jeśli jest niepubliczny, musisz użyć BindingFlagsprzeciążenia GetField/ GetProperty.

Poza tym ważne : bądź ostrożny z taką refleksją; implementacja może ulec zmianie w następnej wersji (złamanie kodu), może zostać zaciemniona (złamanie kodu) lub możesz nie mieć wystarczającego „zaufania” (złamanie kodu). Czy dostrzegasz wzór?


Marc, zastanawiam się ... czy jest możliwy dostęp do prywatnych pól / właściwości, ale czy istnieje sposób na rzutowanie obiektu zwróconego przez GetValue przy użyciu odpowiedniego typu?
codingadventures

1
@GiovanniCampo, jeśli statycznie wiesz, jaki jest typ: jasne - po prostu rzuć. Jeśli nie znasz typu statycznie - nie jest jasne, co to w ogóle oznaczałoby
Marc Gravell

A co z ludźmi, którzy publikują elementy wewnętrzne, które są w rzeczywistości częścią publicznego API? Albo używali, InternalsVisibleToale nie obejmowali twojego zestawu? Jeśli symbol nie jest naprawdę ukryty, jest częścią ABI.
binki

208

Widzę tylko jeden przypadek, w którym pozwoliłbyś ujawnić swoim członkom wewnętrznym innym zgromadzenie, a to jest w celach testowych.

Mówiąc, że istnieje sposób, aby umożliwić zespołom „znajomych” dostęp do elementów wewnętrznych:

W pliku AssemblyInfo.cs projektu dodajesz wiersz dla każdego zespołu.

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

te informacje są dostępne tutaj.

Mam nadzieję że to pomoże.


Rozwinięcie w przypadku celów testowych. Dowolny scenariusz, w którym połączyłeś elementy wewnętrzne w różnych kontekstach. Na przykład w Unity możesz mieć zestaw środowiska uruchomieniowego, zestaw testowy i zespół edytora. Elementy wewnętrzne środowiska wykonawczego powinny być widoczne, aby testować i edytować zestawy.
Steve Buzonas

3
Nie sądzę, że ta odpowiedź pomaga - OP mówi, że nie może modyfikować zestawu dostawcy, a ta odpowiedź mówi o modyfikacji pliku AssemblyInfo.cs projektu dostawcy
Simon Green

6

Chciałbym spierać się o jedną kwestię - że nie można rozszerzyć oryginalnego zespołu - za pomocą Mono.Cecil można wstrzyknąć [InternalsVisibleTo(...)]do zespołu 3pty. Zwróć uwagę, że mogą mieć konsekwencje prawne - mieszasz się z 3pty montażem i implikacjami technicznymi - jeśli zestaw ma silną nazwę, musisz go rozebrać lub ponownie podpisać innym kluczem.

 Install-Package Mono.Cecil

A kod taki jak:

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
Dla mnie nie można zmodyfikować oznacza, że ​​pochodzi z nugetu i nie chcę tworzyć lokalnego nugetu i zarządzać nim z modyfikacjami. Dla niektórych osób utrata silnego imienia będzie miała znaczenie. Ale to interesujący punkt.
binki

3

Odbicie.

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

Używaj tej mocy mądrze. Nie zapomnij o sprawdzaniu błędów. :)


-2

Nie możesz. Klasy wewnętrzne nie mogą być widoczne poza ich montażem, więc nie ma jawnego sposobu, aby uzyskać do nich bezpośredni dostęp - oczywiście AFAIK. Jedynym sposobem jest użycie późnego wiązania środowiska uruchomieniowego za pośrednictwem odbicia, a następnie można pośrednio wywoływać metody i właściwości z klasy wewnętrznej.


5
Możesz nazwać wszystko z refleksją
MrHinsh - Martin Hinshelwood
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.