如何约束泛型类型必须具有接受特定参数的构造函数?

18

我有一个通用的封装类,旨在与一组类型一起使用。这些类型是由一个实用程序生成的,并且全部都派生自基类ClientBase。虽然ClientBase只有一个默认构造函数,但所有生成的类型都有默认构造函数以及带有字符串参数的构造函数。在包装类的构造函数中,我使用带有字符串参数的构造函数实例化类型的实例。以下是示例代码:

public class ClientBase
{ }

public class GenericProxy<T>
    where T: ClientBase, new()
{
    T _proxy;

    public GenericProxy(string configName)
    {
        _proxy = new T(configName);    
    }
}

这段代码无法编译,因为类型T并不能保证有一个接受字符串参数的构造函数。有没有一种方法可以对泛型类进行约束,以强制确保类型T必须有一个接受字符串参数的构造函数?如果不可能,那么处理这种情况的好的替代方案是什么?

5个回答

24

这是不可能的。我希望能看到静态接口来处理这个问题,但不要指望它们很快就会出现...

替代方案:

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

前两种方案实际上是等效的。基本上,您需要将代理类更改为以下内容:

public class GenericProxy<T>
    where T: ClientBase, new()
{
    string _configName;
    T _proxy;
    Func<string, T> _factory;

    public GenericProxy(Func<string, T> 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的实例传递给构造函数。


4

2

这并没有回答您实际问题,即限制一个方法的内容,但为了完整起见,在运行时可以使用反射来实现您所请求的操作:

private T  Get<T>(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 });
    }

1
正如Jon所指出的,没有内置支持此功能,但是你可以使用Expression创建一个类型化的构造函数委托(比反射更快)。实现此功能的代码可以在MiscUtil(位于MiscUtil.Linq.Extensions.TypeExt中)找到。

0

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

using System;
using System.Collections.Generic;

namespace GenericProxy
{
    class Program
    {
        static void Main()
        {
            GenericProxy<ClientBase> proxy = new GenericProxy<ClientBase>(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<T>
        where T : ClientBase, new()
    {
        public GenericProxy(Func<string, T> factory, string configName)
        {
            Proxy = factory(configName);
        }

        public T Proxy { get; private set; }
    }
}

期望看到以下输出:冰淇淋


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接