Crear instancia de tipo genérico?

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()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 le doy a BaseFruit un constructor sin parámetros y luego lleno todo a través de las variables de los miembros. En mi código real (no sobre la fruta) esto es bastante poco práctico.

-Actualizar-
Entonces parece que no puede ser resuelto por restricciones de ninguna manera. De las respuestas hay tres soluciones candidatas:

  • Patrón de fábrica
  • Reflexión
  • Activador

Tiendo a pensar que el reflection es el menos limpio, pero no puedo decidir entre los otros dos.

Además, un ejemplo más simple:

 return (T)Activator.CreateInstance(typeof(T), new object[] { weight }); 

Tenga en cuenta que el uso de la nueva () restricción en T es solo para hacer que el comstackdor verifique la existencia de un constructor público sin parámetros en tiempo de comstackción, el código real utilizado para crear el tipo es la clase Activator.

Tendrá que asegurarse del 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 #).

No puedes usar ningún constructor con parámetros. Puede usar un constructor sin parámetros si tiene una restricción ” where T : new() “.

Es un dolor, pero así es la vida 🙁

Esta es una de las cosas que me gustaría abordar con “interfaces estáticas” . Entonces podría restringir T para incluir métodos estáticos, operadores y constructores, y luego llamarlos.

Sí; cambia tu lugar donde estar:

 where T:BaseFruit, new() 

Sin embargo, esto solo funciona con constructores sin parámetros . Deberá tener otros medios para configurar su propiedad (establecer la propiedad en sí o algo similar).

La solución más simple Activator.CreateInstance()

Como Jon señaló, esta es la vida para restringir a un constructor sin parámetros. Sin embargo, una solución diferente es usar un patrón de fábrica. Esto es fácilmente restringible

 interface IFruitFactory where T : BaseFruit { T Create(int weight); } public void AddFruit( IFruitFactory factory ) where T: BaseFruit { BaseFruit fruit = factory.Create(weight); /*new Apple(150);*/ fruit.Enlist(fruitManager); } 

Otra opción más es usar un enfoque funcional. Pase en un método de fábrica.

 public void AddFruit(Func factoryDel) where T : BaseFruit { BaseFruit fruit = factoryDel(weight); /* new Apple(150); */ fruit.Enlist(fruitManager); } 

Puedes hacerlo mediante el uso de la reflexión:

 public void AddFruit()where T: BaseFruit { ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); if (constructor == null) { throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); } BaseFruit fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; fruit.Enlist(fruitManager); } 

EDITAR: constructor agregado == verificación nula.

EDITAR: una variante más rápida que usa un caché:

 public void AddFruit()where T: BaseFruit { var constructor = FruitCompany.constructor; if (constructor == null) { throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); } var fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; fruit.Enlist(fruitManager); } private static class FruitCompany { public static readonly ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); } 

Recientemente me encontré con un problema muy similar. Solo quería compartir nuestra solución con todos ustedes. Quería crear una instancia de un Car desde un objeto json usando una enumeración:

 Dictionary mapper = new Dictionary(); mapper.Add(1, typeof(CarA)); mapper.Add(2, typeof(BarB)); public class Car where T : class { public T Detail { get; set; } public Car(T data) { Detail = data; } } public class CarA { public int PropA { get; set; } public CarA(){} } public class CarB { public int PropB { get; set; } public CarB(){} } var jsonObj = {"Type":"1","PropA":"10"} MyEnum t = GetTypeOfCar(jsonObj); Type objectT = mapper[t] Type genericType = typeof(Car<>); Type carTypeWithGenerics = genericType.MakeGenericType(objectT); Activator.CreateInstance(carTypeWithGenerics , new Object[] { JsonConvert.DeserializeObject(jsonObj, objectT) }); 

Todavía es posible, con un alto rendimiento, haciendo lo siguiente:

  // public List GetAllItems() where R : IBaseRO, new() { var list = new List(); using ( var wl = new ReaderLock( this ) ) { foreach ( var bo in this.items ) { T t = bo.Value.Data as T; R r = new R(); r.Initialize( t ); list.Add( r ); } } return list; } 

y

  // ///Base class for read-only objects public partial interface IBaseRO { void Initialize( IDTO dto ); void Initialize( object value ); } 

Las clases relevantes deben derivarse 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 como parámetro genérico. R, en mi caso, también es una clase de solo lectura. IMO, la disponibilidad pública de las funciones de Initialize () no tiene un efecto negativo en la inmutabilidad. El usuario de esta clase podría incluir otro objeto, pero esto no modificaría la colección subyacente.