2009-05-28 5 views
72

Tener un montaje que no puedo modificar (suministrado por el proveedor), que tiene un método que devuelve un objeto tipo pero en realidad es de un tipo interno.C# - Cómo acceder a clases interna del conjunto externa

¿Cómo puedo acceder a los campos y/o métodos del objeto desde mi ensamblaje?

Tenga en cuenta que no puedo modificar el ensamblaje suministrado por el proveedor.

En esencia, esto es lo que tengo:

De proveedor:

internal class InternalClass 
    public string test; 
end class 

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

Desde mi ensamblaje utilizando el conjunto de proveedores.

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

Respuesta

70

Sin acceso al tipo (y sin "InternalsVisibleTo", etc.) tendría que usar la reflexión. Pero una mejor pregunta sería: ¿debería tener acceso a esta información? No es parte del contrato de tipo público ... me parece que está destinado a ser tratado como un objeto opaco (para sus propósitos, no para los suyos).

Lo ha descrito como un campo de instancia pública; para conseguir esto a través de la reflexión:

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

Si en realidad es una propiedad (no un campo):

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

Si es no pública, que tendrá que utilizar la sobrecarga de BindingFlagsGetField/GetProperty.

A un lado importante: tenga cuidado con la reflexión de esta manera; la implementación podría cambiar en la próxima versión (rompiendo su código), o podría ser ofuscada (rompiendo su código), o podría no tener suficiente "confianza" (rompiendo su código). ¿Ves el patrón?

+1

Wooo .. 2 minutos! ¡Eso estuvo cerca! Bien dijo Marc (como siempre). : D – Galilyou

+0

¡Genial! Eso funciona. Pensé que no podía acceder a los internos de esta manera ... Muchas gracias –

+0

Marc Me pregunto ... es posible acceder a campos/propiedades privadas, pero ¿hay alguna forma de convertir el objeto devuelto por GetValue utilizando el tipo correcto? – codingadventures

-3

Bueno, no se puede. Las clases internas no pueden ser visibles fuera de su ensamblado, por lo que no hay una forma explícita de acceder directamente a él, por supuesto, -AFAIK. La única forma es utilizar el enlace en tiempo de ejecución tarde a través de la reflexión, luego puede invocar indirectamente métodos y propiedades de la clase interna.

+0

La solución de zonkflut me ha funcionado en el pasado – phlip

+0

aunque depende de la edición del ensamblado llamado – phlip

+4

Puede llamar a cualquier cosa con reflejo –

3

Reflection.

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

Utilice la potencia sabiamente. No te olvides de la comprobación de errores. :)

171

Veo solo una caja que permitiría la exposición de sus miembros internos a otro conjunto y es para realizar pruebas.

diciendo que hay una manera de permitir el acceso asambleas "amigo" a los interiores:

En el archivo AssemblyInfo.cs del proyecto se agrega una línea para cada conjunto.

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

esta información está disponible here.

Espero que esto ayude.

+1

Justo hoy estaba esperando un screencst y escuché sobre exponer los miembros internos de un ensamblaje dado para otro (la prueba montaje) tiene acceso a él, pero no tenía idea de cómo hacerlo. ¡Gracias! –

+3

Vote porque esto fue información útil para mí ... aunque esta no puede ser la respuesta a la pregunta :-D – yeyeyerman

+4

la pregunta dice específicamente que el ensamblaje asociado no se pudo modificar. Rechazar. – danielpops

3

Me gustaría argumentar un punto, que no se puede aumentar el ensamblaje original, utilizando Mono.Cecil puede inyectar [InternalsVisibleTo(...)] al ensamblaje 3pty. Tenga en cuenta que puede haber implicaciones legales: está jugando con el ensamblaje 3pty y las implicaciones técnicas. Si el ensamblaje tiene un nombre fuerte, necesita quitarlo o volver a firmarlo con una clave diferente.

Install-Package Mono.Cecil 

Y el código como:

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

Para mí, no se puede modificar significa que proviene de un nuget y no quiero tener que crear y administrar un nuget local con las modificaciones. Además, para algunas personas, la pérdida de un nombre fuerte será importante. Pero este es un punto interesante. – binki

Cuestiones relacionadas