uso type tipo requiere parametros multiple metodos method genericos generico generic dato argumentos c# generics constraints

c# - type - ¿Cómo restringir el tipo genérico para tener un constructor que tome ciertos parámetros?



tipo de dato generico c++ (5)

Tengo una clase genérica de envoltorio que pretendía ser utilizada con un conjunto de tipos. Esos tipos son generados por una utilidad y todos se derivan de una clase base ClientBase. Si bien ClientBase solo tiene un constructor predeterminado, todos los tipos generados tienen un constructor predeterminado y un constructor toma una cadena como parámetro. En el constructor de la clase contenedora, instalo una instancia del tipo con el constructor que toma una cadena. Aquí hay un código de ejemplo:

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

Este código no se compila porque no se garantiza que el tipo T tenga un constructor que tome una cadena. ¿Hay una manera de definir una restricción en la clase genérica para imponer que el tipo T debe tener un constructor que tome una cadena? Si esto no es posible, ¿cuáles son las buenas alternativas para manejar este tipo de situación?


Aquí hay un ejemplo completo de trabajo basado en la respuesta de @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; } } }

Espera ver el siguiente resultado: ice cream


Como señala Jon, no hay soporte incorporado para esto, pero como parte aparte, puede crear un delegado escrito para el constructor (más rápido que la reflexión) usando Expression . El código para hacer esto se puede encontrar en MiscUtil (en MiscUtil.Linq.Extensions.TypeExt ).



Esto no responde a su pregunta real, restringiendo un método, pero para completar, aquí le explicamos cómo puede hacer lo que está pidiendo en tiempo de ejecución, utilizando la reflexión:

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


No es posible. Me gustaría ver "interfaces estáticas" para manejar esto, pero no las espere pronto ...

Alternativas:

  • Especifique un delegado para que actúe como una fábrica para T
  • Especifica otra interfaz para actuar como una fábrica para T
  • Especifique una interfaz en T para la inicialización (y agregue una restricción para que T implemente la interfaz)

Los dos primeros son realmente equivalentes. Básicamente, cambiarías tu clase de proxy a algo como esto:

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

(Supongo que querrá crear más instancias más adelante; de ​​lo contrario, podría pasar una instancia de T al constructor).