В C#, как создать экземпляр переданного универсального типа внутри метода?
как я могу создать экземпляр типа T внутри моей InstantiateType<T>
метод ниже?
Я получаю сообщение об ошибке: 'T' является 'параметром типа', но используется как 'переменная'.:
(ПРОКРУТИТЕ ВНИЗ ДЛЯ РЕФАКТОРИНГА ОТВЕТА)
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; }
}
}
РЕФАКТОРИНГ ОТВЕТ:
Спасибо за все комментарии, они меня на правильном пути, это то, что я хотел сделать:
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; }
}
}
8 ответов:
объявите свой метод следующим образом:
public string InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
обратите внимание на дополнительное ограничение в конце. Затем создайте
new
экземпляр в теле метода:T obj = new T();
несколько способов.
без указания типа должен быть конструктор:
T obj = default(T); //which will produce null for reference types
С конструктором:
T obj = new T();
но для этого требуется предложение:
where T : new()
расширить на ответы выше, добавив
where T:new()
ограничение на общий метод потребует, чтобы T имел открытый конструктор без параметров.Если вы хотите избежать этого - и в Заводском шаблоне вы иногда заставляете других проходить через ваш заводской метод, а не непосредственно через конструктор - тогда альтернативой является использование отражения (
Activator.CreateInstance...
) и сохранить конструктор по умолчанию частная. Но это связано с штрафом за производительность, конечно.
вы хотите новая T (), но вам также нужно добавить
, new()
доwhere
спецификация для заводского метода
немного старый, но для других, ищущих решение, возможно, это может представлять интерес: http://daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/
два решения. Один использует активатор, а другой-скомпилированные лямбды.
//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; } }
вместо создания функции для создания экземпляра типа
public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new() { T obj = new T(); obj.FirstName = firstName; obj.LastName = lastName; return obj; }
вы могли бы сделать это вот так
T obj = new T { FirstName = firstName, LastName = lastname };
вы также можете использовать отражение, чтобы принести конструктор объекта и создать таким образом:
var c = typeof(T).GetConstructor(); T t = (T)c.Invoke();
использование заводского класса для построения объекта с компилированным выражением lamba: самый быстрый способ, который я нашел для создания экземпляра универсального типа.
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(); } }
вот шаги, которые я следовал, чтобы установить ориентир.
создать мой тестовый метод:
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); }
Я также пробовал использовать заводской метод:
public static T FactoryMethod<T>() where T : new() { return new T(); }
для тестов я создал самый простой класс:
public class A { }
скрипт для проверки:
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))");
результаты более 1 000 000 итераций:
новый A (): 11ms
FactoryMethod A (): 275ms
FactoryClass A .Create (): 56ms
активатор.CreateInstance A (): 235ms
активатор.CreateInstance(typeof (A)): 157ms
Примечания: я тестировал с помощью обоих.NET Framework 4.5 и 4.6 (эквивалентные результаты).