【问题标题】:How to constrain generic type to must have a construtor that takes certain parameters?如何将泛型类型约束为必须具有带有某些参数的构造函数?
【发布时间】:2009-05-12 16:38:06
【问题描述】:

我有一个包装泛型类,它打算与一组类型一起使用。这些类型由实用程序生成,并且都派生自基类 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 必须有一个带字符串的构造函数?如果这是不可能的,有什么好的选择来处理这种情况?

【问题讨论】:

    标签: c# generics constraints


    【解决方案1】:

    这是不可能的。我希望看到"static interfaces" 来处理这个问题,但不要指望他们很快...

    替代方案:

    • 指定一个代表作为 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 的实例传递给构造函数。)

    【讨论】:

      【解决方案2】:

      不幸的是,您尝试做的事情是不可能的。

      MSDN article on Type Constraints

      【讨论】:

        【解决方案3】:

        这并不能回答您的实际问题,限制方法,但为了完整起见,这里是您如何在运行时使用反射来完成您所要求的事情:

        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 });
            }
        

        【讨论】:

          【解决方案4】:

          正如 Jon 所说,没有对此的内置支持 - 但顺便说一下,您可以使用 Expression 创建一个类型化的委托给构造函数(比反射更快)。可以在MiscUtilMiscUtil.Linq.Extensions.TypeExt)中找到执行此操作的代码。

          【讨论】:

            【解决方案5】:

            这是基于@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; }
                }
            }
            

            预计会看到以下输出:ice cream

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-09-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-09-16
              • 2023-04-03
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多