2009-04-02 11 views
20

Ich erinnere mich definitiv daran, irgendwo ein Beispiel dafür zu sehen, wie ich Reflektionen oder etwas anderes benutze. Es hatte etwas mit SqlParameterCollection zu tun, das von einem Benutzer nicht erstellt werden kann (wenn ich mich nicht irre). Leider kann ich es nicht mehr finden.Wie instanziiert ein Objekt mit einem privaten Konstruktor in C#?

Kann jemand bitte diesen Trick hier teilen? Nicht, dass ich es als einen gültigen Ansatz in der Entwicklung betrachte, ich bin nur sehr interessiert an der Möglichkeit, dies zu tun.

Antwort

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

Eine leichte Verbesserung dieser handlichen statischen Methode besteht im Aufbau des Parametertyps Array dynamisch – nrjohnstone

+1

@nrjohnstone Wenn Sie GetType für jedes Array-Element meinen, ist es im Allgemeinen aufgrund von Nullen nicht möglich. Wenn Sie versuchen, die Typen in diesem Fall abzuleiten, implementieren Sie eine vollständige Überladungsauflösung entsprechend der Sprache Ihrer Wahl, die im Falle von C# nicht trivial ist. –

2

Wenn die Klasse nicht eine von Ihnen ist, dann klingt es so, als ob die API absichtlich geschrieben wurde, um dies zu verhindern, was bedeutet, dass es möglich ist, dass Ihr Ansatz nicht das ist, was die API-Autoren beabsichtigten. Sehen Sie sich die Dokumentation an und prüfen Sie, ob es einen empfohlenen Ansatz für die Verwendung dieser Klasse gibt.

Wenn Sie tun Kontrolle über die Klasse haben und dieses Muster implementieren möchten, wird es normalerweise über eine statische Methode für eine Klasse implementiert. Dies ist ein Schlüsselkonzept, das auch das Singleton-Muster ausmacht.

Zum Beispiel:

public PrivateCtorClass 
{ 
    private PrivateCtorClass() 
    { 
    } 

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

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

Das SqlCommandParameter Zeug ist ein gutes Beispiel. Sie erwarten, dass Sie Parameter erstellen, indem Sie Dinge wie dieser Aufruf:

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

Mein Beispiel ist nicht groß Code, weil es keine Befehlsparametereigenschaften oder Wiederverwendung von Parametern/Kommandos Einstellung nicht zeigen, aber Sie bekommen die Idee.

+0

Ich meinte eigentlich, wenn der Autor, wenn die Klasse (nicht ich) machte alle Konstruktoren privat oder intern und ich kann die Klasse nicht ändern, aber wirklich, wirklich eine Instanz davon erstellen wollen, wie kann ich dann diesen Schutz umgehen und eine Instanz erstellen? – User

+0

Ahh Ich sehe was du meinst. Nun, es scheint, dass die API absichtlich geschrieben wurde, um dies zu verhindern, was bedeutet, dass es möglich ist, dass Ihr Ansatz nicht das ist, was die API-Autoren beabsichtigten. Sehen Sie sich die Dokumentation an und prüfen Sie, ob es einen empfohlenen Ansatz für die Verwendung dieser Klasse gibt. –

47

Sie können eine der Überlastung von Activator.CreateInstance verwenden dies zu tun: Activator.CreateInstance(Type type, bool nonPublic)

Verwenden true für das nonPublic Argument. Weil true einem öffentlichen oder nicht öffentlichen Standardkonstruktor entspricht; und false stimmt nur mit einem öffentlichen Standardkonstruktor überein.

Zum Beispiel:

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 entspricht dies CreateInstance (Typ type, params object [] args); 'das ruft ** public ** ctor mit ** bool ** argument an. http://msdn.microsoft.com/en-us/library/system.activator.creatinstance%28v=vs.95%29.aspx –

+2

Das ist in Ordnung, wenn Sie parameterlosen Konstruktor aufrufen. Wenn Sie einen privaten Konstruktor mit Parametern aufrufen möchten, versuchen Sie Folgendes: 'Foo f = (Foo) Activator.CreateInstance (typeof (Foo), BindingFlags.Instance | BindingFlags.NonPublic, null, neues Objekt [] {"Param1"}, null, null); ' –

0

Es wird auch helfen, wenn Ihr Type ist private oder 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); 
    } 
Verwandte Themen