C#:具有构造函数的generics类型?

我有以下C#测试代码:

class MyItem { MyItem( int a ) {} } class MyContainer where T : MyItem, new() { public void CreateItem() { T oItem = new T( 10 ); } } 

Visual Studio无法编译它,错误是在使用’new’的行:

 'T': cannot provide arguments when creating an instance of a variable type 

在C#中是否可以使用非参数构造函数创建generics类型的对象? 在C ++模板中做这样的事情没有问题,所以我很好奇为什么我不能在C#中做同样的事情。 也许还需要一些额外的’where’或语法不同?

它可以通过reflection来完成:

 public void CreateItem() { int constructorparm1 = 10; T oItem = Activator.CreateInstance(typeof(T), constructorparm1) as T; } 

但是没有通用约束来确保T实现所需的构造函数,所以除非你小心地在每个实现接口的类型中声明构造函数,否则我不建议这样做。

对于这个问题,C#和VB.Net不支持将generics约束为具有特定参数的构造函数的概念。 它只支持约束有一个空构造函数。

一种解决方法是让调用者在工厂lambda中传递以创建值。 例如

 public void CreateItem(Func del) { T oItem = del(10); } 

致电网站

 CreateItem(x => new SomeClass(x)); 

没有这样的通用约束,因此不可能直接(这是CLR限制)。 如果需要,必须提供工厂类(具有无参数构造函数),并将其作为第二个generics类型参数传递。

IMO,这里最好的方法是初始化方法,即

 interface ISomeInterface { void Init(int i); } class Foo : ISomeInterface { void ISomeInterface.Init(int i) { /* ... */ } } static class Program { static T Create(int i) where T : class, ISomeInterface, new() { T t = new T(); t.Init(i); return t; } static void Main() { Foo foo = Create(123); } } 

但是,您可以使用Expression (但没有编译时支持):

 using System; using System.Linq.Expressions; class Foo { public Foo(int i) { /* ... */ } } static class Program { static T Create(int i) { return CtorCache.Create(i); } static class CtorCache { static Func ctor; public static T Create(int i) { if (ctor == null) ctor = CreateCtor(); return ctor(i); } static Func CreateCtor() { var param = Expression.Parameter(typeof(int), "i"); var ci = typeof(T).GetConstructor(new[] {typeof(int)}); if(ci == null) throw new InvalidOperationException("No such ctor"); var body = Expression.New(ci, param); return Expression.Lambda>(body, param).Compile(); } } static void Main() { Foo foo = Create(123); } } 

请注意,这会缓存并重用委托以提高性能。

我使用的一种模式是让受约束的类实现一个接口,该接口使用适当的签名定义Init方法:

 interface IMyItem { void Init(int a); } class MyItem : IMyItem { MyItem() {} void Init(int a) { } } class MyContainer< T > where T : MyItem, IMyItem, new() { public void CreateItem() { T oItem = new T(); oItem.Init( 10 ); } }