2009-04-02 15 views
20

Definitivamente recuerdo haber visto en alguna parte un ejemplo de hacerlo usando la reflexión o algo así. Era algo que tenía que ver con SqlParameterCollection que no puede ser creado por un usuario (si no me equivoco). Desafortunadamente no puedo encontrarlo más.¿Cómo crear una instancia de un objeto con un constructor privado en C#?

¿Alguien puede compartir este truco aquí? No es que lo considere un enfoque válido en el desarrollo, estoy muy interesado en la posibilidad de hacer esto.

Respuesta

33
// the types of the constructor parameters, in order 
// use an empty Type[] array if the constructor takes no parameters 
Type[] paramTypes = new Type[] { typeof(string), typeof(int) }; 

// the values of the constructor parameters, in order 
// use an empty object[] array if the constructor takes no parameters 
object[] paramValues = new object[] { "test", 42 }; 

TheTypeYouWantToInstantiate instance = 
    Construct<TheTypeYouWantToInstantiate>(paramTypes, paramValues); 

// ... 

public static T Construct<T>(Type[] paramTypes, object[] paramValues) 
{ 
    Type t = typeof(T); 

    ConstructorInfo ci = t.GetConstructor(
     BindingFlags.Instance | BindingFlags.NonPublic, 
     null, paramTypes, null); 

    return (T)ci.Invoke(paramValues); 
} 
+0

Una ligera mejora en este práctico método estático consiste en crear la matriz de tipos de parámetros dinámicamente – nrjohnstone

+1

@nrjohnstone Si se refiere a GetType en cada elemento de la matriz, en general no es posible debido a nulos. Además, si intenta inferir los tipos en este caso, terminará implementando una resolución de sobrecarga completa de acuerdo con el idioma de su elección, lo que no es trivial en el caso de C#. –

2

Si la clase no es una de las suyas, entonces parece que la API se escribió deliberadamente para evitar esto, lo que significa que es posible que su enfoque no sea el deseado por los escritores de la API. Eche un vistazo a los documentos y vea si hay un enfoque recomendado para usar esta clase.

Si do tienen control sobre la clase y desean implementar este patrón, entonces, por lo general, se implementa mediante un método estático en una clase. Este es un concepto clave que también conforma el patrón de Singleton.

Por ejemplo:

public PrivateCtorClass 
{ 
    private PrivateCtorClass() 
    { 
    } 

    public static PrivateCtorClass Create() 
    { 
     return new PrivateCtorClass(); 
    } 
} 

public SomeOtherClass 
{ 
    public void SomeMethod() 
    { 
     var privateCtorClass = PrivateCtorClass.Create(); 
    } 
} 

El material SqlCommandParameter es un buen ejemplo. Ellos esperan que le permite crear parámetros llamando a cosas como esta:

var command = IDbConnnection.CreateCommand(...); 
command.Parameters.Add(command.CreateParameter(...)); 

Mi ejemplo no es muy grande, ya que el código no muestra la configuración de propiedades de parámetros de comandos o la reutilización de los parámetros/comandos, pero se entiende la idea.

+0

que en realidad quería decir si el autor si la clase (no yo) hizo todo constructores privados o internos y no puedo modificar la clase, pero realmente, realmente quiero crear una instancia de la misma, ¿cómo puedo evitar esta protección y crear una instancia? – User

+0

Ahh entiendo a qué te refieres. Bueno, parece que la API se escribió deliberadamente para evitar esto, lo que significa que es posible que su enfoque no sea el que pretendían los escritores de la API. Eche un vistazo a los documentos y vea si hay un enfoque recomendado para usar esta clase. –

47

Puede utilizar una de las sobrecargas de Activator.CreateInstance de hacer esto: Activator.CreateInstance(Type type, bool nonPublic)

Uso true para el argumento nonPublic. Porque true coincide con un constructor predeterminado público o no público; y false solo coincide con un constructor predeterminado público.

Por ejemplo:

class Program 
    { 
     public static void Main(string[] args) 
     { 
      Type type=typeof(Foo); 
      Foo f=(Foo)Activator.CreateInstance(type,true); 
     }  
    } 

    class Foo 
    { 
     private Foo() 
     { 
     } 
    } 
+1

En Silverlight, esto coincide con 'CreateInstance (Type type, params object [] args);' que se cansa de llamar ** public ** ctor con ** bool ** argument. http://msdn.microsoft.com/en-us/library/system.activator.createinstance%28v=vs.95%29.aspx –

+2

Eso está bien si invoca constructor sin parámetros. Si desea invocar un constructor privado con params, intente esto: 'Foo f = (Foo) Activator.CreateInstance (typeof (Foo), BindingFlags.Instance | BindingFlags.NonPublic, null, new object [] {"Param1"}, null, null); ' –

0

También ayudará si su Type es private o internal:

public static object CreatePrivateClassInstance(string typeName, object[] parameters) 
    { 
     Type type = AppDomain.CurrentDomain.GetAssemblies(). 
       SelectMany(assembly => assembly.GetTypes()).FirstOrDefault(t => t.Name == typeName); 
     return type.GetConstructors()[0].Invoke(parameters); 
    } 
Cuestiones relacionadas