Comment puis-je instancier le type T dans mon InstantiateType<T>
méthode ci-dessous?
J'obtiens l'erreur: 'T' est un 'paramètre de type' mais est utilisé comme une 'variable'.:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestGeneric33
{
class Program
{
static void Main(string[] args)
{
Container container = new Container();
Console.WriteLine(container.InstantiateType<Customer>("Jim", "Smith"));
Console.WriteLine(container.InstantiateType<Employee>("Joe", "Thompson"));
Console.ReadLine();
}
}
public class Container
{
public T InstantiateType<T>(string firstName, string lastName) where T : IPerson
{
T obj = T();
obj.FirstName(firstName);
obj.LastName(lastName);
return obj;
}
}
public interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
}
public class Customer : IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
}
public class Employee : IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int EmployeeNumber { get; set; }
}
}
Merci pour tous les commentaires, ils m'ont mis sur la bonne voie, voici ce que je voulais faire:
using System;
namespace TestGeneric33
{
class Program
{
static void Main(string[] args)
{
Container container = new Container();
Customer customer1 = container.InstantiateType<Customer>("Jim", "Smith");
Employee employee1 = container.InstantiateType<Employee>("Joe", "Thompson");
Console.WriteLine(PersonDisplayer.SimpleDisplay(customer1));
Console.WriteLine(PersonDisplayer.SimpleDisplay(employee1));
Console.ReadLine();
}
}
public class Container
{
public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
{
T obj = new T();
obj.FirstName = firstName;
obj.LastName = lastName;
return obj;
}
}
public interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
}
public class PersonDisplayer
{
private IPerson _person;
public PersonDisplayer(IPerson person)
{
_person = person;
}
public string SimpleDisplay()
{
return String.Format("{1}, {0}", _person.FirstName, _person.LastName);
}
public static string SimpleDisplay(IPerson person)
{
PersonDisplayer personDisplayer = new PersonDisplayer(person);
return personDisplayer.SimpleDisplay();
}
}
public class Customer : IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
}
public class Employee : IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int EmployeeNumber { get; set; }
}
}
Déclarez votre méthode comme ceci:
public string InstantiateType<T>(string firstName, string lastName)
where T : IPerson, new()
Remarquez la contrainte supplémentaire à la fin. Créez ensuite une instance new
dans le corps de la méthode:
T obj = new T();
Deux 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, l'ajout de 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 réflexion (Activator.CreateInstance...
) et garder le constructeur par défaut privé. Mais cela s'accompagne d'une pénalité de performance, bien sûr.
vous voulez nouvea T (), mais vous devrez également ajouter , new()
à la spécification where
pour la méthode d'usine
Un peu vieux mais pour ceux qui recherchent une solution, cela pourrait peut-être être intéressant: http://daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for- constructeurs-privés /
Deux solutions. Un utilisant Activator et un utilisant Lambdas compilés.
//Person has private ctor
var person = Factory<Person>.Create(p => p.Name = "Daniel");
public static class Factory<T> where T : class
{
private static readonly Func<T> FactoryFn;
static Factory()
{
//FactoryFn = CreateUsingActivator();
FactoryFn = CreateUsingLambdas();
}
private static Func<T> CreateUsingActivator()
{
var type = typeof(T);
Func<T> f = () => Activator.CreateInstance(type, true) as T;
return f;
}
private static Func<T> 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<Func<T>>(ctorExpression).Compile();
}
public static T Create(Action<T> init)
{
var instance = FactoryFn();
init(instance);
return instance;
}
}
Au lieu de créer une fonction pour instancier le type
public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
{
T obj = new T();
obj.FirstName = firstName;
obj.LastName = lastName;
return obj;
}
tu aurais pu le faire comme ça
T obj = new T { FirstName = firstName, LastName = lastname };
Vous pouvez également utiliser la réflexion pour récupérer le constructeur de l'objet et instancier de cette façon:
var c = typeof(T).GetConstructor();
T t = (T)c.Invoke();
Utiliser une classe d'usine pour construire votre objet avec une expression lamba compilée: Le moyen le plus rapide que j'ai trouvé pour instancier un type générique.
public static class FactoryContructor<T>
{
private static readonly Func<T> New =
Expression.Lambda<Func<T>>(Expression.New(typeof (T))).Compile();
public static T Create()
{
return New();
}
}
Voici les étapes que j'ai suivies pour établir la référence.
Créer ma méthode de test de référence:
static void Benchmark(Action action, int iterationCount, string 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 également essayé d'utiliser une méthode d'usine:
public static T FactoryMethod<T>() 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<A>(), iterations, "FactoryMethod<A>()");
Benchmark(() => FactoryClass<A>.Create(), iterations, "FactoryClass<A>.Create()");
Benchmark(() => Activator.CreateInstance<A>(), iterations, "Activator.CreateInstance<A>()");
Benchmark(() => Activator.CreateInstance(typeof (A)), iterations, "Activator.CreateInstance(typeof (A))");
Résultats sur 1 000 000 d'itérations:
nouveau A (): 11ms
Méthode d'usine A (): 275 ms
FactoryClass A .Create (): 56 ms
Activator.CreateInstance A (): 235ms
Activator.CreateInstance (typeof (A)): 157 ms
Remarques: J'ai testé en utilisant les deux . NET Framework 4.5 et 4.6 (résultats équivalents).