Si BaseFruit
tiene un constructor que acepta un int weight
, ¿puedo crear una instancia de una fruta en un método genérico como este?
public void AddFruit<T>()where T: BaseFruit{
BaseFruit fruit = new T(weight); /*new Apple(150);*/
fruit.Enlist(fruitManager);
}
Se agrega un ejemplo detrás de los comentarios. Parece que solo puedo hacer esto si doy BaseFruit
un constructor sin parámetros y luego lo relleno todo a través de las variables miembro. En mi código real (no sobre fruta) esto es poco práctico.
-Update-
Entonces parece que no puede resolverse por restricciones de ninguna manera entonces. De las respuestas hay tres soluciones candidatas:
- Patrón de fábrica
- Reflexión
- Activador
Tiendo a pensar que la reflexión es la menos limpia, pero no puedo decidir entre las otras dos.
Respuestas:
Además, un ejemplo más simple:
Tenga en cuenta que el uso de la restricción new () en T es solo para hacer que el compilador verifique un constructor público sin parámetros en tiempo de compilación, el código real utilizado para crear el tipo es la clase Activator.
Deberá asegurarse con respecto al constructor específico existente, y este tipo de requisito puede ser un olor a código (o más bien algo que debería tratar de evitar en la versión actual en c #).
fuente
new object[] { weight }
.CreateInstance
se declara con params,public static object CreateInstance(Type type, params object[] args)
por lo que puedes hacerloreturn (T) Activator.CreateInstance(typeof(T), weight);
. Si hay varios parámetros, páselos como argumentos separados. Solo si ya tiene una construcción de parámetros enumerables, debería molestarse en convertirlaobject[]
y pasarla aCreateInstance
.Func
que crea la nueva instancia. Supongamos que elApple
uso del constructor esnew Apple(wgt)
. Luego agregue a laApple
clase esta definición:static Func<float, Fruit> CreateOne { get; } = (wgt) => new Apple(wgt);
En la Fábrica, definapublic static Fruit CreateFruitGiven(float weight, Func<float, Fruit> createOne) { return createOne(weight); }
Uso:Factory.CreateFruit(57.3f, Apple.CreateOne);
- que crea y devuelve unApple
, conweight=57.3f
.No puede usar ningún constructor parametrizado. Puede usar un constructor sin parámetros si tiene una "
where T : new()
" restricción.Es un dolor, pero así es la vida :(
Esta es una de las cosas que me gustaría abordar con "interfaces estáticas" . Luego podrá restringir a T para que incluya métodos estáticos, operadores y constructores, y luego llamarlos.
fuente
Si; cambia tu lugar donde estar:
Sin embargo, esto solo funciona con constructores sin parámetros . Tendrá que tener otros medios para configurar su propiedad (configurar la propiedad en sí o algo similar).
fuente
Solución más simple
Activator.CreateInstance<T>()
fuente
Como Jon señaló, esta es la vida para restringir un constructor sin parámetros. Sin embargo, una solución diferente es usar un patrón de fábrica. Esto es fácilmente restringible
Otra opción más es utilizar un enfoque funcional. Pase en un método de fábrica.
fuente
Puedes hacerlo usando la reflexión:
EDITAR: Constructor agregado == verificación nula.
EDITAR: una variante más rápida usando un caché:
fuente
Como una adición a la sugerencia de user1471935:
Para crear una instancia de una clase genérica utilizando un constructor con uno o más parámetros, ahora puede usar la clase Activador.
La lista de objetos son los parámetros que desea suministrar. De acuerdo con Microsoft :
También hay una versión genérica de CreateInstance (
CreateInstance<T>()
) pero esa tampoco le permite proporcionar parámetros de constructor.fuente
Creé este método:
Lo uso de esta manera:
Código:
fuente
Recientemente me encontré con un problema muy similar. Solo quería compartir nuestra solución con todos ustedes. Quería crear una instancia de a
Car<CarA>
partir de un objeto json usando que tenía una enumeración:fuente
Todavía es posible, con un alto rendimiento, haciendo lo siguiente:
y
Las clases relevantes tienen que derivar de esta interfaz e inicializarse en consecuencia. Tenga en cuenta que, en mi caso, este código es parte de una clase circundante, que ya tiene <T> como parámetro genérico. R, en mi caso, también es una clase de solo lectura. En mi opinión, la disponibilidad pública de las funciones Initialize () no tiene ningún efecto negativo sobre la inmutabilidad. El usuario de esta clase podría poner otro objeto, pero esto no modificaría la colección subyacente.
fuente