web-dev-qa-db-fra.com

Création d'une instance de type sans constructeur par défaut en C # à l'aide de la réflexion

Prenons l'exemple de la classe suivante:

class Sometype
{
    int someValue;

    public Sometype(int someValue)
    {
        this.someValue = someValue;
    }
}

Je veux ensuite créer une instance de ce type en utilisant la réflexion:

Type t = typeof(Sometype);
object o = Activator.CreateInstance(t);

Normalement, cela fonctionnera, cependant parce que SomeType n'a pas défini de constructeur sans paramètre, l'appel à Activator.CreateInstance lèvera une exception de type MissingMethodException avec le message "Aucun constructeur sans paramètre défini pour cet objet." Existe-t-il une autre façon de créer une instance de ce type? Ce serait un peu génial d'ajouter des constructeurs sans paramètres à toutes mes classes.

90
Aistina

J'ai initialement posté cette réponse ici , mais voici une réimpression car ce n'est pas exactement la même question mais a la même réponse:

FormatterServices.GetUninitializedObject() créera une instance sans appeler de constructeur. J'ai trouvé cette classe en utilisant Reflector et en fouillant dans certaines des classes de sérialisation de base .Net.

Je l'ai testé en utilisant l'exemple de code ci-dessous et il semble que cela fonctionne très bien:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;

namespace NoConstructorThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass)); //does not call ctor
            myClass.One = 1;
            Console.WriteLine(myClass.One); //write "1"
            Console.ReadKey();
        }
    }

    public class MyClass
    {
        public MyClass()
        {
            Console.WriteLine("MyClass ctor called.");
        }

        public int One
        {
            get;
            set;
        }
    }
}
130
Jason Jackson

Utilisez cette surcharge de la méthode CreateInstance:

public static Object CreateInstance(
    Type type,
    params Object[] args
)

Crée une instance du type spécifié en utilisant le constructeur qui correspond le mieux aux paramètres spécifiés.

Voir: http://msdn.Microsoft.com/en-us/library/wcxyzt4d.aspx

71
Nick

Quand je référencé performance de (T)FormatterServices.GetUninitializedObject(typeof(T)) c'était plus lent. En même temps, les expressions compilées vous apporteraient de grandes améliorations de vitesse bien qu'elles ne fonctionnent que pour les types avec constructeur par défaut. J'ai adopté une approche hybride:

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

Cela signifie que l'expression de création est effectivement mise en cache et encourt une pénalité uniquement la première fois que le type est chargé. Gérera également les types de valeur de manière efficace.

Appeler:

MyType me = New<MyType>.Instance();

Notez que (T)FormatterServices.GetUninitializedObject(t) échouera pour la chaîne. Par conséquent, une gestion spéciale de la chaîne est en place pour renvoyer une chaîne vide.

19
nawfal

Bonnes réponses mais inutilisables sur le framework compact dot net. Voici une solution qui fonctionnera sur CF.Net ...

class Test
{
    int _myInt;

    public Test(int myInt)
    {
        _myInt = myInt;
    }

    public override string ToString()
    {
        return "My int = " + _myInt.ToString();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var ctor = typeof(Test).GetConstructor(new Type[] { typeof(int) });
        var obj = ctor.Invoke(new object[] { 10 });
        Console.WriteLine(obj);
    }
}
4
Autodidact