Se BaseFruit
tem 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<T>()where T: BaseFruit{
BaseFruit fruit = new T(weight); /*new Apple(150);*/
fruit.Enlist(fruitManager);
}
Um exemplo é adicionado atrás dos comentários. Parece que só posso fazer isso se der BaseFruit
um construtor sem parâmetros e preencher tudo através de variáveis-membro. No meu código real (não sobre frutas), isso é bastante impraticável.
-Update-
Então parece que não pode ser resolvido por restrições de qualquer forma então. Das respostas, há três soluções candidatas:
- Padrão de fábrica
- Reflexão
- Activator
Costumo pensar que a reflexão é a menos limpa, mas não consigo decidir entre as outras duas.
Respostas:
Além disso, um exemplo mais simples:
Observe que o uso da nova restrição () em T é apenas para fazer com que o compilador verifique se há um construtor público sem parâmetros em tempo de compilação, o código real usado para criar o tipo é a classe Activator.
Você precisará garantir-se em relação ao 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 #).
fonte
new object[] { weight }
.CreateInstance
é declarado com paramspublic static object CreateInstance(Type type, params object[] args)
, para que você possa fazerreturn (T) Activator.CreateInstance(typeof(T), weight);
. Se houver vários parâmetros, passe-os como argumentos separados. Somente se você já possui um enumerável de parâmetros construídos, deve se preocupar em convertê-loobject[]
e transmiti-lo paraCreateInstance
.Func
que cria a nova instância. Suponha que oApple
uso do construtor sejanew Apple(wgt)
. Em seguida, adicione àApple
classe esta definição:static Func<float, Fruit> CreateOne { get; } = (wgt) => new Apple(wgt);
No Factory definapublic static Fruit CreateFruitGiven(float weight, Func<float, Fruit> createOne) { return createOne(weight); }
Usage:Factory.CreateFruit(57.3f, Apple.CreateOne);
- que cria e retorna umApple
, comweight=57.3f
.Você não pode usar nenhum construtor parametrizado. Você pode usar um construtor sem parâmetros se tiver uma "
where T : new()
" restrição.É uma dor, mas a vida é assim :(
Essa é uma das coisas que eu gostaria de abordar com "interfaces estáticas" . Você seria capaz de restringir T para incluir métodos estáticos, operadores e construtores e depois chamá-los.
fonte
Sim; Mude o seu lugar para estar:
No entanto, isso funciona apenas com construtores sem parâmetros . Você precisará ter outros meios para definir sua propriedade (definir a propriedade em si ou algo semelhante).
fonte
Solução mais simples
Activator.CreateInstance<T>()
fonte
Como Jon apontou, essa é a vida para restringir um construtor sem parâmetros. No entanto, uma solução diferente é usar um padrão de fábrica. Isso é facilmente restritivo
Ainda outra opção é usar uma abordagem funcional. Passe em um método de fábrica.
fonte
Você pode fazer isso usando a reflexão:
EDIT: Adicionado construtor == verificação nula.
EDIT: Uma variante mais rápida usando um cache:
fonte
Como complemento à sugestão do usuário14191935:
Para instanciar uma classe genérica usando um construtor com um ou mais parâmetros, agora você pode usar a classe Activator.
A lista de objetos são os parâmetros que você deseja fornecer. De acordo com a Microsoft :
Há também uma versão genérica de CreateInstance (
CreateInstance<T>()
), mas essa também não permite que você forneça parâmetros de construtor.fonte
Eu criei este método:
Eu uso isso desta maneira:
Código:
fonte
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 a
Car<CarA>
partir de um objeto json usando o qual tinha um enum:fonte
Ainda é possível, com alto desempenho, fazendo o seguinte:
e
As classes relevantes precisam derivar dessa interface e inicializar de acordo. Observe que, no meu caso, esse código faz parte de uma classe circundante, que já tem <T> como parâmetro genérico. R, no meu caso, também é uma classe somente leitura. Na IMO, a disponibilidade pública das funções Initialize () não tem efeito negativo na imutabilidade. O usuário desta classe poderia colocar outro objeto, mas isso não modificaria a coleção subjacente.
fonte