2009-05-28 3 views
72

Имея сборку, которую я не могу изменить (поставщик в комплекте поставки), которые имеют метод возвращающую объекта тип, но на самом деле внутренний типа.C# - Как получить доступ к внутреннему классу из внешней сборки

Как я могу получить доступ к полям и/или методам объекта из моей сборки?

Имейте в виду, что я не могу изменить сборку поставщика.

В сущности, вот что у меня есть:

От производителя:

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 
    } 
} 

ответ

70

Без доступа к типу (и нет «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.

Важно отметить: будьте осторожны с таким отражением; реализация может измениться в следующей версии (взломать ваш код), или она может быть запутана (сломать ваш код), или вам может не хватить «доверия» (нарушение вашего кода). Вы заметили паттерн?

+1

Wooo .. 2 минуты! Это было близко! Хорошо сказал Марк (как всегда). : D – Galilyou

+0

Отлично! Это работает. Я думал, что я не мог получить доступ к внутренностям таким образом ... Большое спасибо –

+0

Marc Интересно ... можно получить доступ к закрытым полям/свойствам, но есть ли способ передать объект, возвращенный GetValue, используя правильный тип? – codingadventures

-3

Ну, вы не можете. Внутренние классы не могут быть видны за пределами их сборки, поэтому нет прямого доступа к нему напрямую - АФАИК, конечно. Единственный способ - использовать позднее связывание во время выполнения через отражение, тогда вы можете косвенно ссылаться на методы и свойства из внутреннего класса.

+0

Решение zonkflut для меня работало в прошлом – phlip

+0

, хотя оно полагается на редактирование вызываемой сборки – phlip

+4

Вы можете называть что-либо с отражением –

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

Используйте силу с умом. Не забывайте проверять ошибки. :)

171

Я вижу только один случай, когда вы разрешаете подвергать свои внутренние элементы другому собранию и это предназначено для тестирования.

Сказать, что есть способ, чтобы «друг» сборки доступа к внутренним:

В файле AssemblyInfo.cs проекта необходимо добавить строку для каждой сборки.

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

эта информация доступен here.

Надеется, что это помогает.

+1

Как раз сегодня я отлично разбирался и слышал о том, чтобы разоблачить внутренние элементы данной сборки, так что другой (тест сборка) имеет доступ к нему, но понятия не имела, как это сделать. Спасибо! –

+3

Голосовать, потому что это была полезная информация для меня ... хотя это не может быть ответом на вопрос :-D – yeyeyerman

+4

В вопросе конкретно говорится, что сборник-партнер не может быть изменен. Проголосовать вниз. – danielpops

3

Я хотел бы утверждать, что вы не можете увеличить оригинальную сборку - используя Mono.Cecil вы можете ввести [InternalsVisibleTo(...)] в сборку 3-го порядка. Обратите внимание, что могут быть юридические последствия - вы возитесь с сборкой 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")) 
    // }); 
} 
+0

Для меня не может быть изменено означает, что он исходит из nuget, и я не хочу создавать и управлять локальным nuget с изменениями. Кроме того, для некоторых людей будет иметь значение потеря сильного имени. Но это интересный момент. – binki

Смежные вопросы