2009-04-02 12 views
20

Ricordo definitivamente di aver visto da qualche parte un esempio di come farlo usando il riflesso o qualcosa del genere. Era qualcosa che aveva a che fare con SqlParameterCollection che non è creabile da un utente (se non sbaglio). Purtroppo non lo trovo più.Come istanziare un oggetto con un costruttore privato in C#?

Qualcuno può condividere questo trucco qui? Non che io consideri un valido approccio allo sviluppo, sono solo molto interessato alla possibilità di farlo.

risposta

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

Un leggero miglioramento di questo pratico metodo statico è la creazione della matrice dei tipi di parametri dinamicamente – nrjohnstone

+1

@nrjohnstone Se si intende GetType su ogni elemento dell'array, non è possibile in generale a causa di valori null. Inoltre, se si tenta di dedurre i tipi in questo caso si finirà per implementare una risoluzione di sovraccarico completa in base alla lingua scelta, che non è banale in caso di C#. –

2

Se la classe non è una delle tue, allora sembra che l'API sia stata scritta deliberatamente per impedirlo, il che significa che è possibile che il tuo approccio non sia quello che gli autori delle API intendevano. Dai un'occhiata ai documenti e verifica se esiste un approccio raccomandato per l'utilizzo di questa classe.

Se si do ha il controllo sulla classe e si desidera implementare questo modello, in genere viene implementato tramite un metodo statico su una classe. Questo è un concetto chiave che costituisce anche il modello Singleton.

Ad esempio:

public PrivateCtorClass 
{ 
    private PrivateCtorClass() 
    { 
    } 

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

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

La roba SqlCommandParameter è un buon esempio. Si aspettano di creare parametri chiamando le cose in questo modo:

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

Il mio esempio non è grande codice, perché non dimostra l'impostazione delle proprietà dei parametri di comando o riutilizzo dei parametri/comandi, ma si ottiene l'idea.

+0

Io in realtà significava se l'autore se la classe (non io) ha fatto tutto costruttori privati ​​o interni e non posso modificare la classe, ma in realtà, voglio davvero creare un'istanza di essa, quindi come posso aggirare questa protezione e creare un'istanza? – User

+0

Ah, capisco cosa intendi. Bene, sembra che l'API sia stata scritta deliberatamente per impedirlo, il che significa che è possibile che il tuo approccio non sia quello che gli autori delle API intendevano. Dai un'occhiata ai documenti e verifica se esiste un approccio raccomandato per l'utilizzo di questa classe. –

47

È possibile utilizzare uno dei sovraccarichi di Activator.CreateInstance per farlo: Activator.CreateInstance(Type type, bool nonPublic)

Usa true per l'argomento nonPublic. Perché true corrisponde a un costruttore predefinito pubblico o non pubblico; e false corrisponde solo a un costruttore pubblico predefinito.

Ad esempio:

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

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

In silverlight questo corrisponde a 'CreateInstance (Type type, params object [] args);' che stanca di chiamare ** public ** ctor con ** bool ** argomento. http://msdn.microsoft.com/en-us/library/system.activator.createinstance%28v=vs.95%29.aspx –

+2

Questo va bene se si richiama il costruttore senza parametri. Se vuoi invocare un costruttore privato con parametri, prova questo: 'Foo f = (Foo) Activator.CreateInstance (typeof (Foo), BindingFlags.Instance | BindingFlags.NonPublic, null, new object [] {"Param1"}, null, null); ' –

0

Aiuterà anche se il Type è 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); 
    } 
Problemi correlati