Criar instância do tipo genérico?

Se o BaseFruit tiver um construtor que aceita um int weight , posso instanciar um pedaço de fruta em um método genérico como este?

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

Um exemplo é adicionado atrás dos comentários. Parece que eu só posso fazer isso se eu der ao BaseFruit um construtor sem parâmetros e, em seguida, preencher tudo através de variables ​​de membro. No meu código real (não sobre frutas) isso é bastante impraticável.

-Atualizar-
Portanto, parece que não pode ser resolvido por restrições de qualquer forma. Das respostas existem três soluções candidatas:

  • Padrão de fábrica
  • Reflexão
  • Ativador

Eu tendo a pensar que a reflection é a menos limpa, mas não consigo decidir entre os outros dois.

Além disso, um exemplo mais simples:

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

Observe que usar a restrição new () em T é apenas fazer o compilador verificar um construtor público sem parâmetros em tempo de compilation, o código real usado para criar o tipo é a class Activator.

Você precisará assegurar-se sobre o construtor específico existente, e esse tipo de requisito pode ser um cheiro de código (ou melhor, algo que você deve tentar evitar na versão atual em c #).

Você não pode usar nenhum construtor parametrizado. Você pode usar um construtor sem parâmetros se tiver uma restrição ” where T : new() “.

É uma dor, mas tal é a vida 🙁

Essa é uma das coisas que gostaria de abordar com “interfaces estáticas” . Em seguida, você poderia restringir T para include methods estáticos, operadores e construtores e, em seguida, chamá-los.

Sim; mude seu onde estar:

 where T:BaseFruit, new() 

No entanto, isso só funciona com construtores sem parâmetros . Você terá que ter algum outro meio de configurar sua propriedade (definindo a própria propriedade ou algo similar).

Solução mais simples Activator.CreateInstance()

Como Jon apontou, esta é a vida para restringir um construtor sem parâmetros. No entanto, uma solução diferente é usar um padrão de fábrica. Isso é facilmente constrangido

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

Outra opção é usar uma abordagem funcional. Passe em um método de fábrica.

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

Você pode fazer usando reflection:

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

EDIT: Construtor adicionado == verificação nula.

EDIT: uma variante mais rápida usando um cache:

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

Recentemente me deparei com um problema muito semelhante. Só queria compartilhar nossa solução com todos vocês. Eu queria criar uma instância de um Car de um object json usando o qual tinha um enum:

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

Ainda é possível, com alto desempenho, fazendo o seguinte:

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

e

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

As classs relevantes então precisam derivar dessa interface e inicializar de acordo. Por favor, note que no meu caso, este código é parte de uma class circundante, que já tem como parâmetro genérico. R, no meu caso, também é uma class somente leitura. IMO, a disponibilidade pública de funções Initialize () não tem efeito negativo sobre a imutabilidade. O usuário dessa class poderia colocar outro object, mas isso não modificaria a coleção subjacente.