Sunday, October 29, 2006

Como instanciar una clase que tenga un constructor privado

Este articulo aplica a C# (probado en VS2005)

Hace poco hablaba sobre como podemos ejecutar metodos privados (de otra clase) usando refleccion; parece que no mucha gente esta enterada de que se puede hacer esto y siempre genera todo tipo de comentarios. Pues aqui les tengo otra para ustedes, tambien podemos crear una instancia de una clase, aun si los constructores de esta son privados.

Aqui estoy incluyendo un ejemplo para un constructor privado, un protegido y un (normal) publico, todos ellos usando refleccion.

Vamos a crear una clase para poder jugar:

public class TestClass {
private TestClass() {
Console.WriteLine("private TestClass constructor");
}
protected TestClass(int i) {
Console.WriteLine(string.Format("protected TestClass constructor (int:{0})", i));
}
public TestClass(string s) {
Console.WriteLine(string.Format("public TestClass constructor (string:{0})", s));
}
}

Ahora vamos a ver que facil es crear instancias de esta clase usando cualquiera de sus constructores, sin importar su visibilidad:


- Para este codigo necesitas incluir System.Reflection en el uses.


El primero es un constructor privado simple que no tiene ningun parametro, no se requiere mucho para este

//*** Private constructor
ConstructorInfo ci1 = typeof(TestClass).GetConstructor(
//*** Non public and instance (non static) constructor
BindingFlags.NonPublic | BindingFlags.Instance,
//*** No binder, no parameters, so no parameter modifiers
null, new Type[0], new ParameterModifier[0]);
//*** Call our constructor
TestClass tc1 = (TestClass)ci1.Invoke(new object[0]);

El segundo es protected (aun cuenta como "no publico"), aqui inclui un parametro asi que tenemos que pasarle el tipo del parametro

//*** Protected constructor
ConstructorInfo ci2 = typeof(TestClass).GetConstructor(
//*** non public and instance (non static) constructor
BindingFlags.NonPublic | BindingFlags.Instance,
//*** no binder, 1 parameter of type int, no modifiers for the param
null, new Type[1] { typeof(int) }, new ParameterModifier[0]);
//*** Call our constructor
TestClass tc2 = (TestClass)ci2.Invoke(new object[1] { 10 });

El tercero es publico, asi que podriamos crear la instancia sin tener que usar el metodo de refleccion, pero le deje aqui solo como ejemplo

//*** Public constructor
ConstructorInfo ci3 = typeof(TestClass).GetConstructor(
//*** public and instance method
BindingFlags.Public | BindingFlags.Instance,
//*** no binder, 1 parameter of type string, no parameter modifiers
null, new Type[1] { typeof(string) }, new ParameterModifier[0]);
//*** Call our constructor
TestClass tc3 = (TestClass)ci3.Invoke(new object[1] { "test" });

Eso es todo, asi de sencillo; esta tecnica realmente es muy raro que la tengamos que utilizar, mas que todo me interesa que sepan que es posible y cuan facil es lograrlo


Uno podria incluso combinar los flags: BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance y podriamos crear la instancia de la clase sin importar si esta fuera private, protected o public (por ejemplo en el caso de que cargaramos un assembly dinamicamente y solo nos interesara que la clase estuviera ahi)


Pueden encontrar el codigo completo aqui,


por cierto, en estos momentos estoy en Guadalajara, estare aqui hasta el martes y luego voy a Tulancingo Hidalgo y de ahi a Aguascalientes, si alguien esta interesado en echar una platicada o ir a cenar aqui estamos a la orden


salu2 

No comments: