如何约束generics类型必须有一个带有某些参数的construtor?

我有一个包装器generics类,旨在与一组类型一起使用。 这些类型由实用程序生成,并且都是从基类ClientBase派生的。 虽然ClientBase只有一个默认构造函数,但所有生成的类型都有默认构造函数,构造函数则将字符串作为参数。 在包装器类的构造函数中,我使用带有字符串的构造函数实例化该类型的实例。 这是一个示例代码:

public class ClientBase { } public class GenericProxy where T: ClientBase, new() { T _proxy; public GenericProxy(string configName) { _proxy = new T(configName); } } 

此代码无法编译,因为不保证类型T具有接受字符串的构造函数。 有没有办法在generics类上定义约束来强制类型T必须有一个带字符串的构造函数? 如果这是不可能的,那么处理这种情况的好方法是什么?

这是不可能的。 我想看看“静态接口”来处理这个问题,但不要期待它们很快…

备择方案:

  • 指定委托作为T的工厂
  • 指定另一个接口作为T的工厂
  • 在T本身上指定一个接口进行初始化(并添加一个约束,以便T实现接口)

前两个是真正相同的。 基本上你将代理类改为这样的:

 public class GenericProxy where T: ClientBase, new() { string _configName; T _proxy; Func _factory; public GenericProxy(Func factory, string configName) { _configName = configName; _factory = factory; RefreshProxy(); } void RefreshProxy() // As an example; suppose we need to do this later too { _proxy = _factory(_configName); } } 

(我假设您以后想要创建更多实例 – 否则您也可以将T的实例传递给构造函数。)

不幸的是,你想做的事情是不可能的。

关于类型约束的MSDN文章

这不能回答你的实际问题,限制一个方法,但为了完整性,你可以在运行时使用reflection来做你所要求的事情:

 private T Get(string id) { var constructor = typeof(T).GetConstructor(new Type[] { typeof(X), typeof(Y) }); if (constructor == null) throw new InvalidOperationException("The type submitted, " + typeof(T).Name + ", does not support the expected constructor (X, Y)."); var data = GetData(id); return (T)constructor.Invoke(new object[] { data.x, data.y }); } 

正如Jon指出的那样,没有内置的支持 – 但是除了你可以使用Expression创建一个构造函数的类型委托(比reflection更快)。 执行此操作的代码可以在MiscUtil中找到(在MiscUtil.Linq.Extensions.TypeExt )。

这是一个基于@JonSkeet答案的完整工作示例:

 using System; using System.Collections.Generic; namespace GenericProxy { class Program { static void Main() { GenericProxy proxy = new GenericProxy(ClientBase.Factory, "cream"); Console.WriteLine(proxy.Proxy.ConfigName); // test to see it working } } public class ClientBase { static public ClientBase Factory(string configName) { return new ClientBase(configName); } // default constructor as required by new() constraint public ClientBase() { } // constructor that takes arguments public ClientBase(string configName) { _configName = configName; } // simple method to demonstrate working example public string ConfigName { get { return "ice " + _configName; } } private string _configName; } public class GenericProxy where T : ClientBase, new() { public GenericProxy(Func factory, string configName) { Proxy = factory(configName); } public T Proxy { get; private set; } } } 

期待看到以下输出: ice cream