Як я можу отримати доступ до внутрішнього класу із зовнішньої збірки?


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перевантаження GetField/ GetProperty.

Важливий бік : будьте обережні з таким відображенням; реалізація може змінитись у наступній версії (зламати код), або вона може бути затуманена (зламати ваш код), або у вас може не вистачити "довіри" (зламати ваш код). Ви помічаєте візерунок?


Марк Цікаво ... чи можна отримати доступ до приватних полів / властивостей, але чи є спосіб передавати об'єкт, повернутий GetValue, використовуючи потрібний тип?
кодування пригод

1
@GiovanniCampo, якщо ти статистично знаєш, що це за тип: впевнений - просто передай. Якщо ви статично не знаєте типу - незрозуміло, що це може означати
Марк Гравелл

Що з людьми, які публікують речі як внутрішні, які дійсно є частиною публічного API? Або вони використовували, InternalsVisibleToале не включали вашу збірку? Якщо символ не справді прихований, він є частиною ABI.
бінкі

208

Я бачу лише один випадок, коли ви могли б дозволити впливу своїх внутрішніх членів на іншу збірку, і це для тестування.

Кажучи, що існує спосіб дозволити збіркам "Друг" доступ до внутрішніх служб:

У файлі AssemblyInfo.cs проекту ви додаєте рядок для кожної збірки.

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

ця інформація доступна тут.

Сподіваюся, це допомагає.


Розширюється на випадок цілей тестування. Будь-який сценарій, коли ви поєднали внутрішню інформацію в різних контекстах. Наприклад, в Unity у вас можуть бути збірка часу виконання, тестова збірка та збірка редактора. Внутрішні інтервали виконання повинні бути видимими для тестових і редакторних зборок.
Стів Бузонас

3
Я не думаю, що ця відповідь допомагає - ОП каже, що він не може змінити збірку постачальника, і ця відповідь говорить про зміну файлу AssemblyInfo.cs проекту постачальника
Simon Green

6

Я хотів би заперечити один момент - про те, що ви не можете доповнити оригінальну збірку - за допомогою Mono.Cecil ви можете ввести [InternalsVisibleTo(...)]в 3pty збірку. Зауважте, що можуть виникнути юридичні наслідки - ви заплутаєтесь із 3 пустим складанням та технічними наслідками - якщо збірка має чітке ім’я, вам потрібно або зняти її, або переписати за допомогою іншого ключа.

 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
Для мене не може бути змінено означає, що він походить від нута, і мені не хочеться створювати та керувати локальним нугом за допомогою модифікацій. Також для деяких людей буде мати значення втрата міцного імені. Але це цікавий момент.
бінкі

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

Ну, ти не можеш. Внутрішні класи не можуть бути видимими поза їх складання, тому немає явного способу доступу до нього безпосередньо - Звичайно АФАІК. Єдиний спосіб - використовувати пізнє прив'язування часу виконання через рефлексію, тоді ви можете посилатися методами та властивостями з внутрішнього класу опосередковано.


5
Ви можете зателефонувати що завгодно з роздумом
MrHinsh - Martin Hinshelwood
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.