Der effizienteste Weg, um den Standardkonstruktor eines Typs zu erhalten

72

Was ist der effizienteste Weg, um den Standardkonstruktor (dh den Instanzkonstruktor ohne Parameter) eines System.Type abzurufen?

Ich habe etwas in Anlehnung an den folgenden Code gedacht, aber es scheint, dass es einen einfacheren und effizienteren Weg geben sollte, dies zu tun.

Type type = typeof(FooBar)
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
type.GetConstructors(flags)
    .Where(constructor => constructor.GetParameters().Length == 0)
    .First();
Wes Haggard
quelle

Antworten:

133
type.GetConstructor(Type.EmptyTypes)
Curt Hagenlocher
quelle
6
Die statischen Mitglieder, die man sich nie ansieht ... das ist großartig.
Darren Kopp
1
Private Mitglieder werden ebenfalls nicht angesehen. Angenommen, Sie benötigen nur public, dann scheint dies am einfachsten zu sein. Ist es jedoch das schnellste? Ich arbeite gerade an einem Test, um das herauszufinden.
Wes Haggard
7
Nach einigen Messungen dieses Ansatzes im Vergleich zu meinem Ansatz mit MeasureIt ( msdn.microsoft.com/en-us/magazine/cc500596.aspx ) ist dieser Ansatz in allen außer den einfachsten Fällen schneller und selbst dann kaum langsamer. Das ist also sowohl das einfachste als auch das schnellste. Vielen Dank!
Wes Haggard
30

Wenn Sie das ConstructorInfo-Objekt tatsächlich benötigen , lesen Sie die Antwort von Curt Hagenlocher .

Auf der anderen Seite, wenn Sie wirklich nur versuchen, ein Objekt zur Laufzeit aus a zu erstellen System.Type, sehen Sie System.Activator.CreateInstance- es ist nicht nur zukunftssicher (Activator verarbeitet mehr Details als ConstructorInfo.Invoke), es ist auch viel weniger hässlich.

Alex Lyman
quelle
3
Potenziell gefährlicher Rat, da bestimmte Objekte keine Standardkonstruktoren haben (String ist einer). Wenn Sie dies also wohl oder übel aufrufen, kann dies zu einer MissingMethod-Ausnahme führen. Ich muss tatsächlich nach einem Standardkonstruktor suchen, bevor ich diese Methode aufrufe.
Listdave
Wie Cunningdave schreibt, möchten Sie höchstwahrscheinlich zumindest einen Teil des Ausnahmesatzes abfangen und ordnungsgemäß behandeln, der durch diese Methode ausgelöst werden kann, wodurch der Aufruf wieder viel weniger schön wird. Die Lösung mit ConstructorInfo hingegen sieht zunächst ziemlich hässlich aus, ermöglicht es Ihnen jedoch, all diese Ausnahmefälle zu behandeln, ohne zuerst Ausnahmen zu werfen und zu fangen, was eine kostspielige Sache sein kann.
Buygrush
2

Wenn Sie den generischen Typparameter haben, ist Jeff Bridgmans Antwort die beste. Wenn Sie nur ein Type-Objekt haben, das den Typ darstellt, den Sie erstellen möchten, können Sie es Activator.CreateInstance(Type)wie von Alex Lyman vorgeschlagen verwenden, aber mir wurde gesagt, dass es langsam ist (ich habe es jedoch nicht persönlich profiliert).

Wenn Sie diese Objekte jedoch sehr häufig konstruieren, gibt es einen eloquenteren Ansatz, bei dem dynamisch kompilierte Linq-Ausdrücke verwendet werden:

using System;
using System.Linq.Expressions;

public static class TypeHelper
{
    public static Func<object> CreateDefaultConstructor(Type type)
    {
        NewExpression newExp = Expression.New(type);

        // Create a new lambda expression with the NewExpression as the body.
        var lambda = Expression.Lambda<Func<object>>(newExp);

        // Compile our new lambda expression.
        return lambda.Compile();
    }
}

Rufen Sie einfach den an Sie zurückgegebenen Delegierten an. Sie sollten diesen Delegaten zwischenspeichern, da das ständige Neukompilieren von Linq-Ausdrücken teuer sein kann. Wenn Sie den Delegaten jedoch zwischenspeichern und jedes Mal wiederverwenden, kann dies sehr schnell gehen! Ich persönlich verwende ein statisches Suchwörterbuch, das nach Typ indiziert ist. Diese Funktion ist nützlich, wenn Sie mit serialisierten Objekten arbeiten, bei denen Sie möglicherweise nur die Typinformationen kennen.

HINWEIS: Dies kann fehlschlagen, wenn der Typ nicht konstruierbar ist oder keinen Standardkonstruktor hat!

DaFlame
quelle
0

Wenn Sie nur den Standardkonstruktor zum Instanziieren der Klasse erhalten möchten und den Typ als generischen Typparameter für eine Funktion abrufen möchten, haben Sie folgende Möglichkeiten:

T NewItUp<T>() where T : new()
{
   return new T();
}
Jeff B.
quelle
-2

Sie möchten FormatterServices.GetUninitializedObject (Type) ausprobieren. Dieses ist besser als Activator.CreateInstance

Diese Methode ruft den Objektkonstruktor jedoch nicht auf. Wenn Sie dort Anfangswerte festlegen, funktioniert dies nicht. Überprüfen Sie die MSDN für dieses Objekt. Http://msdn.microsoft.com/en-us/library/system.runtime .serialization.formatterservices.getuninitializedobject.aspx

Hier gibt es einen anderen Weg: http://www.ozcandegirmenci.com/post/2008/02/Create-object-instances-Faster-than-Reflection.aspx

Dieser schlägt jedoch fehl, wenn das Objekt über parametrisierte Konstruktoren verfügt

Hoffe das hilft

Mohamed Faramawi
quelle