在 C# 中,如何在方法中实例化传递的泛型类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/658951/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In C#, how to instantiate a passed generic type inside a method?
提问by Edward Tanguay
How can I instantiate the type T inside my InstantiateType<T>
method below?
如何在InstantiateType<T>
下面的方法中实例化类型 T ?
I'm getting the error: 'T' is a 'type parameter' but is used like a 'variable'.:
我收到错误消息:“T”是“类型参数”,但用作“变量”。:
(SCROLL DOWN FOR REFACTORED ANSWER)
(向下滚动查看重构答案)
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; }
}
}
REFACTORED ANSWER:
重构答案:
Thanks for all the comments, they got me on the right track, this is what I wanted to do:
感谢所有的评论,他们让我走上了正轨,这就是我想做的:
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; }
}
}
采纳答案by Joel Coehoorn
Declare your method like this:
像这样声明你的方法:
public string InstantiateType<T>(string firstName, string lastName)
where T : IPerson, new()
Notice the additional constraint at the end. Then create a new
instance in the method body:
注意最后的附加约束。然后new
在方法体中创建一个实例:
T obj = new T();
回答by Ruben Bartelink
you want newT(), but you'll also need to add , new()
to the where
spec for the factory method
你想要新的T(),但你还需要添加, new()
到where
工厂方法的规范中
回答by annakata
Couple of ways.
几种方式。
Without specifying the type must have a constructor:
不指定类型必须有一个构造函数:
T obj = default(T); //which will produce null for reference types
With a constructor:
使用构造函数:
T obj = new T();
But this requires the clause:
但这需要条款:
where T : new()
回答by Dan C.
To extend on the answers above, adding where T:new()
constraint to a generic method will require T to have a public, parameterless constructor.
为了扩展上面的答案,向where T:new()
泛型方法添加约束将要求 T 具有公共的无参数构造函数。
If you want to avoid that - and in a factory pattern you sometimes force the others to go through your factory method and not directly through the constructor - then the alternative is to use reflection (Activator.CreateInstance...
) and keep the default constructor private. But this comes with a performance penalty, of course.
如果你想避免这种情况——在工厂模式中,你有时会强迫其他人通过你的工厂方法而不是直接通过构造函数——那么另一种方法是使用反射 ( Activator.CreateInstance...
) 并保持默认构造函数私有。但这当然会带来性能损失。
回答by Daniel
A bit old but for others looking for a solution, perhaps this could be of interest: http://daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/
有点旧,但对于其他寻求解决方案的人来说,也许这可能会引起兴趣:http: //daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/
Two solutions. One using Activator and one using Compiled Lambdas.
两种解决方案。一种使用 Activator,一种使用 Compiled Lambdas。
//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;
}
}
回答by TMul
Instead of creating a function to Instantiate the 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;
}
you could have done it like this
你可以这样做
T obj = new T { FirstName = firstName, LastName = lastname };
回答by pimbrouwers
You can also use reflection to fetch the object's constructor and instantiate that way:
您还可以使用反射来获取对象的构造函数并以这种方式实例化:
var c = typeof(T).GetConstructor();
T t = (T)c.Invoke();
回答by Thomas
Using a factory class to build your object with compiled lamba expression: The fastest way I've found to instantiate generic type.
使用工厂类通过编译的 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();
}
}
Here is the steps I followed to set up the benchmark.
这是我设置基准所遵循的步骤。
Create my benchmark test method:
创建我的基准测试方法:
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);
}
I've also tried using a factory method:
我也试过使用工厂方法:
public static T FactoryMethod<T>() where T : new()
{
return new T();
}
For the tests I've created the simplest class :
对于测试,我创建了最简单的类:
public class A { }
The script to test:
要测试的脚本:
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))");
Results over 1 000 000 iterations:
new A(): 11ms
FactoryMethod A(): 275ms
FactoryClass A .Create(): 56ms
Activator.CreateInstance A (): 235ms
Activator.CreateInstance(typeof (A)): 157ms
超过 1 000 000 次迭代的结果:
新的 A(): 11ms
工厂方法 A():275 毫秒
FactoryClass A .Create(): 56ms
Activator.CreateInstance A():235ms
Activator.CreateInstance(typeof (A)): 157ms
Remarks: I've tested using both .NET Framework 4.5 and 4.6(equivalent results).
备注:我已经使用.NET Framework 4.5 和 4.6(等效结果)进行了测试。