2010-01-07 13 views
23

Estoy tratando de usar la reflexión para crear una instancia de una clase. Pero está sellado internamente y tiene un constructor privado. Me pregunto cómo puedo iniciarlo y, como parte del marco, solo puedo usar la reflexión para eliminarlo.Instanciando clase interna con constructor privado

internal sealed class ABC 
{ 
    private ABC(string password){} 
    public static ABC Create(string password){}; 
} 

Agregado: System.ServiceModel.Channels.SelfSignedCertificate es la clase interna que estoy tratando de utilizar

+1

¿Desea llamar al constructor privado o llamar al método estático Create? –

+0

Es una clase interna en el marco con la estructura anterior. Me gustaría crear una instancia de la clase usando constructor/método estático y luego usar MethodInfo para usar los métodos en la clase. objeto mc = assembly.CreateInstance (---- <- incapaz de usarlo ya que tiene constructor privado MethodInfo mi = type1.GetMethod ("MyMethod", bf); – sunny

Respuesta

17

En primer lugar, el hecho de que es interna significa que no se supone que estar haciendo lo que quiere hacer.

Debería tratar de encontrar una ruta alternativa a lo que desea lograr.

Lamentablemente, no nos dice lo que quiere lograr, solo el siguiente paso que cree que desea hacer, entonces eso es lo que puedo ayudarlo.

Este código funcionará, y accede al método estático público:

Type t = typeof(SomeOtherTypeInSameAssembly) 
    .Assembly.GetType("ClassLibrary1.ABC"); 
MethodInfo method = t.GetMethod("Create", 
    BindingFlags.Public | BindingFlags.Static, null, 
    new Type[] { typeof(String) }, 
    new ParameterModifier[0]); 
Object o = method.Invoke(null, new Object[] { "test" }); 

Tenga en cuenta que este se basa en tener acceso a otro tipo en la misma asamblea, que es pública. Si no tiene eso, necesita obtener el objeto Assembly que contiene el tipo.

+7

'Primero que nada, el hecho de que sea interno significa que se supone que no debes hacer lo que quieres hacer. ¿Qué tal hacer una prueba unitaria pero mantener el aislamiento de la clase? – ktutnik

+14

El hecho de que alguien necesite acceder a clases internas y esté al tanto de clases internas específicas demuestra que probablemente sean lo suficientemente inteligentes como para abrir Reflector y decidir por sí mismos si hacerlo sería útil o una buena idea. El código es código, y un modificador "interno" no es más que un tipo de DRM a sortear, IMO. – Triynko

+4

@ktutnik: con la prueba unitaria esto generalmente se logra con [InternalsVisibleToAttribute] (http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx) – FooBarTheLittle

0

No se puede crear instancias de clases con constructores privados a menos que sea una clase anidada.

http://msdn.microsoft.com/en-us/library/kcfb85a6%28VS.71%29.aspx

+0

No es cierto, con la reflexión todo es posible si tiene suficientes derechos. –

+0

¿Pero tiene suficientes derechos si la clase está sellada internamente? – Matt

+0

Puede crear una instancia desde dentro de la clase utilizando un método estático. –

25

EDIT: No me había dado cuenta de que usted ha mencionado que el tipo que está tratando de inicializar es parte del marco .NET. Pensé que era uno de tus propios tipos, al que se hacía referencia desde otro lugar.

Le recomiendo firmemente que no intente esto. Microsoft tiene la libertad de cambiar o eliminar las clases internas entre las versiones del framework: su código será increíblemente frágil si confía en los detalles de implementación como este.

Cambie su diseño para evitar tener que hacer esto.


Respuesta original:

Sí, usted tendría que utilizar la reflexión - como esto:

using System; 
using System.Reflection; 

internal sealed class ABC 
{ 
    private ABC(string password) 
    { 
     Console.WriteLine("Constructor called"); 
    } 
} 

public class Test 
{ 
    static void Main() 
    { 
     ConstructorInfo ctor = typeof(ABC).GetConstructors 
      (BindingFlags.Instance | BindingFlags.NonPublic)[0]; 

     ABC abc = (ABC) ctor.Invoke(new object[] { "test" }); 
    } 
} 

Tenga en cuenta que viola los modificadores de acceso de esta manera requiere el permiso ReflectionPermissionFlag.MemberAccess. Si sabe que habrá un método estático llamado Create, que sería mejor invocando que través de la reflexión:

using System; 
using System.Reflection; 

internal sealed class ABC 
{ 
    private ABC(string password) 
    { 
     Console.WriteLine("Constructor called"); 
    } 

    public static ABC Create(string password) 
    { 
     return new ABC(password); 
    } 
} 

public class Test 
{ 
    static void Main() 
    { 
     MethodInfo method = typeof(ABC).GetMethod("Create", 
      BindingFlags.Static | BindingFlags.Public); 

     ABC abc = (ABC) method.Invoke(null, new object[]{"test"}); 
    } 
} 
+1

Si el ABC el tipo es interno en otro ensamblado, necesitará obtener el tipo por su FullName. –

+0

Gracias a todos por sus comentarios. Encontraré otra forma de hacerlo. Es un punto válido que MS puede cambiarlo! gracias de nuevo – sunny

2

Si se trata de internal a una asamblea que no es el suyo y el constructor se marca como private, el autor de la asamblea está haciendo un gran esfuerzo para evitar que crees el objeto.

Lo que sigue es realmente malo ya que se basa en clases y métodos no públicos que pueden cambiar sin previo aviso.

Added: System.ServiceModel.Channels.SelfSignedCertificate is the internal class I am trying to use

No haga esto. El marco podría cambiar debajo de usted en un abrir y cerrar de ojos arruinando su código y todo su trabajo duro.

Dicho esto, usted puede hacer esto:

MethodInfo method = Type.GetType(assemblyQualifiedName).GetMethod("Create"); 
ABC o = (ABC)method.Invoke(null, new[] { "test" }); 

Esto invoca el método staticCreate. Otra opción es

MethodInfo method = Type.GetType(assemblyQualifiedName).GetConstructor(
         BindingFlags.NonPublic | BindingFlags.Instance, 
         null, 
         new[] { typeof(string) }, 
         null 
        ); 
ABC o = (ABC)method.Invoke(new[] { "test" }); 

que utiliza la reflexión para cargar el constructor que acepta un string como parámetro.

Una manera fácil de obtener el nombre de ensamblado cualificado es

string name = typeof(somePublicType).Assembly.GetType("Namespace.ABC"); 

donde somePublicType es un tipo que es público y en el mismo conjunto ABC.

+0

No tengo acceso a ABC por lo no se puede usar MethodInfo method = typeof (ABC). Utilicé el reflector y vi esta clase en el marco. Necesito usarlo. – sunny

+0

Si el diseñador de la arquitectura quería que la clase fuera utilizada por los consumidores del framework, habría otra forma de obtener una instancia del objeto sin recurrir a este tipo de hackers. –

+0

@unknown (google): debe usar el nombre calificado para ensamblar. Ver mi edición – jason

0

utilizar la clase Activador instanciarlo

Activator.CreateInstance(myType, true); 
+0

Error: No se pudo cargar el archivo o ensamblado 'System.ServiceModel.Channels' o una de sus dependencias. El sistema no puede encontrar el archivo especificado. – sunny

Cuestiones relacionadas