C # Créer un nouveau T ()

159

Vous pouvez voir ce que j'essaye (mais échoue) de faire avec le code suivant:

protected T GetObject()
{
    return new T();
}

Toute aide serait grandement appréciée.

ÉDITER:

Le contexte était le suivant. Je jouais avec une classe de contrôleur personnalisée pour tous les contrôleurs à dériver, avec des méthodes standardisées. Donc, en contexte, j'avais besoin de créer une nouvelle instance de l'objet du type contrôleur. Donc, au moment de la rédaction de cet article, c'était quelque chose comme:

public class GenericController<T> : Controller
{
    ...

    protected T GetObject()
    {
        return (T)Activator.CreateInstance(ObjectType);
    }        

    public ActionResult Create()
    {
        var obj = GetObject()

        return View(obj);
    }

Et j'ai donc décidé que la réflexion était la plus facile ici. Je conviens que, compte tenu de l'énoncé initial de la question, la réponse la plus appropriée pour marquer comme correcte était celle utilisant la contrainte new (). J'ai arrangé ça.

Hanshan
la source
27
Non, je ne vois pas ce que vous essayez et ce que vous ne faites pas. Je vois un morceau de code qui pourrait faire partie d'un programme de travail, sans contexte, sans message d'erreur et sans explication.
Ben Voigt
17
Oh, je déteste quand la mauvaise réponse est sélectionnée!
David Heffernan

Réponses:

409

Jetez un œil à la nouvelle contrainte

public class MyClass<T> where T : new()
{
    protected T GetObject()
    {
        return new T();
    }
}

Tpourrait être une classe qui n'a pas de constructeur par défaut: dans ce cas new T()serait une instruction invalide. La new()contrainte dit que Tdoit avoir un constructeur par défaut, ce qui rend new T()légal.

Vous pouvez appliquer la même contrainte à une méthode générique:

public static T GetObject<T>() where T : new()
{
    return new T();
}

Si vous devez passer des paramètres:

protected T GetObject(params object[] args)
{
    return (T)Activator.CreateInstance(typeof(T), args);
}
Alex Aza
la source
2
Merci, mon pote - je suis heureux d'avoir appris cela aujourd'hui. Compte tenu du contexte de ma méthode, j'ai opté pour la solution de réflexion. À votre santé!
Hanshan
8
@nulliusinverba - hmm ... ce serait bien si vous montriez le contexte de votre méthode dans la question.
Alex Aza
1
@nulliusinverba - vous n'avez pas indiqué dans la question que vous aviez besoin de paramètres.
Alex Aza
1
@Alex - Quand j'ai lu sa question, j'ai supposé qu'il ne voulait pas de paramètres: S Up-vote pour vous cependant :)
Phill
Est-il possible d'utiliser quelque chose comme une nouvelle contrainte (de paramètres)?
Louis Rhys
29

Une autre façon est d'utiliser la réflexion:

protected T GetObject<T>(Type[] signature, object[] args)
{
    return (T)typeof(T).GetConstructor(signature).Invoke(args);
}
Sean Thoman
la source
Merci, mon pote - J'ai choisi cette solution compte tenu du contexte de la méthode.
Hanshan
22
Tout comme un FYI, cela peut également être écrit comme Activator.CreateInstance (typeof (T), signature, args); voir msdn.microsoft.com/en-us/library/4b0ww1we.aspx pour plus de détails.
Chris Baxter
@Calgary Coder: Quelle est l'utilité d'une signature de type [], vous pouvez simplement appeler CreateInstance avec des paramètres directement, sans spécifier explicitement la signature. Dans les deux cas, vous obtiendrez MissingMethodException si un constructeur correspondant n'existe pas.
Boris B.
4
Même si c'est la réponse qui vous convient le mieux, ce n'est évidemment pas la meilleure pour la communauté. Les personnes qui recherchent cette question recherchent la réponse par le bas, vraiment.
Trap
Et quel est exactement ce contexte? Veuillez l'ajouter à la question d'origine.
James
18

Juste pour compléter, la meilleure solution ici est souvent d'exiger un argument de fonction d'usine:

T GetObject<T>(Func<T> factory)
{  return factory(); }

et appelez-le quelque chose comme ceci:

string s = GetObject(() => "result");

Vous pouvez l'utiliser pour exiger ou utiliser les paramètres disponibles, si nécessaire.

Joël Coehoorn
la source
16

La nouvelle contrainte est correcte, mais si vous avez besoin que T soit également un type valeur, utilisez ceci:

protected T GetObject() {
    if (typeof(T).IsValueType || typeof(T) == typeof(string)) {
        return default(T);
    } else {
       return (T)Activator.CreateInstance(typeof(T));
    }
}
Lukas Cenovsky
la source
7

Puisqu'il est étiqueté C # 4. Avec le framework open source ImpromptuIntereface, il utilisera le dlr pour appeler le constructeur, il est significativement plus rapide que Activator lorsque votre constructeur a des arguments, et négligeable quand il ne le fait pas. Cependant, le principal avantage est qu'il gérera correctement les constructeurs avec des paramètres optionnels C # 4.0, ce qu'Activator ne fera pas.

protected T GetObject(params object[] args)
{
    return (T)Impromptu.InvokeConstructor(typeof(T), args);
}
jbtule
la source
4

Pour obtenir cela, j'ai essayé le code suivant:

  protected T GetObject<T>()
    {
        T obj = default(T);
        obj =Activator.CreateInstance<T>();
        return obj ;
    }
UJS
la source