En C #, comment instancier un type générique passé dans une méthode?

Comment puis-je instancier le type T dans ma méthode InstantiateType ci-dessous?

Je reçois l’erreur: «T» est un «paramètre de type» mais est utilisé comme une «variable». :

(Défilement vers le bas pour une réponse refroidie)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestGeneric33 { class Program { static void Main(ssortingng[] args) { Container container = new Container(); Console.WriteLine(container.InstantiateType("Jim", "Smith")); Console.WriteLine(container.InstantiateType("Joe", "Thompson")); Console.ReadLine(); } } public class Container { public T InstantiateType(ssortingng firstName, ssortingng lastName) where T : IPerson { T obj = T(); obj.FirstName(firstName); obj.LastName(lastName); return obj; } } public interface IPerson { ssortingng FirstName { get; set; } ssortingng LastName { get; set; } } public class Customer : IPerson { public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public ssortingng Company { get; set; } } public class Employee : IPerson { public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public int EmployeeNumber { get; set; } } } 

RÉFACTURÉ RÉPONSE:

Merci pour tous les commentaires, ils m’ont mis sur la bonne voie, c’est ce que je voulais faire:

 using System; namespace TestGeneric33 { class Program { static void Main(ssortingng[] args) { Container container = new Container(); Customer customer1 = container.InstantiateType("Jim", "Smith"); Employee employee1 = container.InstantiateType("Joe", "Thompson"); Console.WriteLine(PersonDisplayer.SimpleDisplay(customer1)); Console.WriteLine(PersonDisplayer.SimpleDisplay(employee1)); Console.ReadLine(); } } public class Container { public T InstantiateType(ssortingng firstName, ssortingng lastName) where T : IPerson, new() { T obj = new T(); obj.FirstName = firstName; obj.LastName = lastName; return obj; } } public interface IPerson { ssortingng FirstName { get; set; } ssortingng LastName { get; set; } } public class PersonDisplayer { private IPerson _person; public PersonDisplayer(IPerson person) { _person = person; } public ssortingng SimpleDisplay() { return Ssortingng.Format("{1}, {0}", _person.FirstName, _person.LastName); } public static ssortingng SimpleDisplay(IPerson person) { PersonDisplayer personDisplayer = new PersonDisplayer(person); return personDisplayer.SimpleDisplay(); } } public class Customer : IPerson { public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public ssortingng Company { get; set; } } public class Employee : IPerson { public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public int EmployeeNumber { get; set; } } } 

Déclarez votre méthode comme ceci:

 public ssortingng InstantiateType(ssortingng firstName, ssortingng lastName) where T : IPerson, new() 

Notez la contrainte supplémentaire à la fin. Ensuite, créez une new instance dans le corps de la méthode:

 T obj = new T(); 

Quelques façons.

Sans spécifier le type doit avoir un constructeur:

 T obj = default(T); //which will produce null for reference types 

Avec un constructeur:

 T obj = new T(); 

Mais cela nécessite la clause:

 where T : new() 

Pour étendre les réponses ci-dessus, append la contrainte where T:new() à une méthode générique nécessitera que T ait un constructeur public, sans paramètre.

Si vous voulez éviter cela – et dans un modèle d’usine, vous forcez parfois les autres à passer par votre méthode d’usine et non directement par le constructeur – alors l’alternative est d’utiliser la reflection ( Activator.CreateInstance... ) et de conserver le constructeur par défaut privé. Mais cela s’accompagne bien sûr d’une pénalité de performance.

vous voulez un nouveau T (), mais vous devrez également append , new() à la spécification where pour la méthode d’usine

Un peu vieux, mais pour les autres qui recherchent une solution, cela pourrait être intéressant: http://daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/

Deux solutions Un utilisant Activator et un utilisant Lambdas compilés.

 //Person has private ctor var person = Factory.Create(p => p.Name = "Daniel"); public static class Factory where T : class { private static readonly Func FactoryFn; static Factory() { //FactoryFn = CreateUsingActivator(); FactoryFn = CreateUsingLambdas(); } private static Func CreateUsingActivator() { var type = typeof(T); Func f = () => Activator.CreateInstance(type, true) as T; return f; } private static Func CreateUsingLambdas() { var type = typeof(T); var ctor = type.GetConstructor( BindingFlags.Instance | BindingFlags.CreateInstance | BindingFlags.NonPublic, null, new Type[] { }, null); var ctorExpression = Expression.New(ctor); return Expression.Lambda>(ctorExpression).Comstack(); } public static T Create(Action init) { var instance = FactoryFn(); init(instance); return instance; } } 

Au lieu de créer une fonction pour instancier le type

 public T InstantiateType(ssortingng firstName, ssortingng lastName) where T : IPerson, new() { T obj = new T(); obj.FirstName = firstName; obj.LastName = lastName; return obj; } 

vous auriez pu le faire comme ça

 T obj = new T { FirstName = firstName, LastName = lastname }; 

Utiliser une classe de fabrique pour construire votre object avec une expression lamba compilée: le moyen le plus rapide pour instancier des types génériques.

 public static class FactoryContructor { private static readonly Func New = Expression.Lambda>(Expression.New(typeof (T))).Comstack(); public static T Create() { return New(); } } 

Voici les étapes que j’ai suivies pour mettre en place le benchmark.

Créer ma méthode de test de référence:

 static void Benchmark(Action action, int iterationCount, ssortingng text) { GC.Collect(); var sw = new Stopwatch(); action(); // Execute once before sw.Start(); for (var i = 0; i <= iterationCount; i++) { action(); } sw.Stop(); System.Console.WriteLine(text + ", Elapsed: {0}ms", sw.ElapsedMilliseconds); } 

J'ai aussi essayé d'utiliser une méthode d'usine:

 public static T FactoryMethod() where T : new() { return new T(); } 

Pour les tests, j'ai créé la classe la plus simple:

 public class A { } 

Le script à tester:

 const int iterations = 1000000; Benchmark(() => new A(), iterations, "new A()"); Benchmark(() => FactoryMethod(), iterations, "FactoryMethod()"); Benchmark(() => FactoryClass.Create(), iterations, "FactoryClass.Create()"); Benchmark(() => Activator.CreateInstance(), iterations, "Activator.CreateInstance()"); Benchmark(() => Activator.CreateInstance(typeof (A)), iterations, "Activator.CreateInstance(typeof (A))"); 

Résultats sur 1 000 000 itérations:

nouveau A (): 11ms

FactoryMethod A (): 275ms

FactoryClass A .Create (): 56ms

Activator.CreateInstance A (): 235ms

Activator.CreateInstance (typeof (A)): 157ms

Remarques : J'ai testé à la fois avec .NET Framework 4.5 et 4.6 (résultats équivalents).

Vous pouvez également utiliser la reflection pour récupérer le constructeur de l’object et l’instancier de la manière suivante:

 var c = typeof(T).GetConstructor(); T t = (T)c.Invoke();