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

前端 未结 5 890
没有蜡笔的小新
没有蜡笔的小新 2020-12-03 05:07

I have a wrapper generic class that intended to be used with a set of types. Those types are generated by a utility and are all derived from a base class ClientBase. While C

相关标签:
5条回答
  • 2020-12-03 05:45

    This does not answer your actual question, constraining a method, but for completeness here's how you can do what you're asking at run time, using reflection:

    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 });
        }
    
    0 讨论(0)
  • 2020-12-03 05:48

    Unfortunately what you're trying to do isn't possible.

    MSDN article on Type Constraints

    0 讨论(0)
  • 2020-12-03 05:53

    Here is a full working example based on @JonSkeet answer:

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

    Expect to see the following output: ice cream

    0 讨论(0)
  • 2020-12-03 05:56

    As Jon notes, there is no inbuilt support for this - but as an aside you can create a typed delegate to the constructor (faster than reflection) using Expression. The code to do this can be found in MiscUtil (in MiscUtil.Linq.Extensions.TypeExt).

    0 讨论(0)
  • 2020-12-03 06:01

    It's not possible. I'd like to see "static interfaces" to handle this, but don't expect them any time soon...

    Alternatives:

    • Specify a delegate to act as a factory for T
    • Specify another interface to act as a factory for T
    • Specify an interface on T itself for initialization (and add a constraint so that T implements the interface)

    The first two are really equivalent. Basically you'd change your proxy class to something like this:

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

    (I assume you're going to want to create more instances later - otherwise you might as well pass an instance of T into the constructor.)

    0 讨论(0)
提交回复
热议问题