簡體   English   中英

如何將泛型類型限制為必須具有接受某些參數的構造函數?

[英]How to constrain generic type to must have a construtor that takes certain parameters?

我有一個包裝通用類,打算與一組類型一起使用。 這些類型由實用程序生成,並且都派生自基類 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 必須有一個接受字符串的構造函數? 如果這是不可能的,那么處理這種情況的好方法是什么?

這是不可能的。 我希望看到“靜態接口”來處理這個問題,但不要指望他們很快......

備擇方案:

  • 指定一個委托作為 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 的實例傳遞給構造函數。)

不幸的是,您嘗試做的事情是不可能的。

關於類型約束的 MSDN 文章

這並沒有回答您的實際問題,限制了一個方法,但為了完整起見,您可以使用反射在運行時完成您的要求:

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

正如 Jon 所指出的,對此沒有內置支持 - 但順便說一句,您可以使用Expression為構造函數創建類型化委托(比反射更快)。 執行此操作的代碼可以在MiscUtil (在MiscUtil.Linq.Extensions.TypeExt )中找到。

這是基於@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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM