Passando argumentos para C # genérico new () do tipo modelado

Eu estou tentando criar um novo object do tipo T através de seu construtor ao adicionar à lista.

Estou recebendo um erro de compilation: A mensagem de erro é:

‘T’: não pode fornecer argumentos ao criar uma instância de uma variável

Mas minhas classs têm um argumento de construtor! Como posso fazer isso funcionar?

public static string GetAllItems(...) where T : new() { ... List tabListItems = new List(); foreach (ListItem listItem in listCollection) { tabListItems.Add(new T(listItem)); // error here. } ... } 

Para criar uma instância de um tipo genérico em uma function, você deve restringi-la com o sinalizador “new”.

 public static string GetAllItems(...) where T : new() 

No entanto, isso só funcionará quando você quiser chamar o construtor que não possui parâmetros. Não é o caso aqui. Em vez disso, você terá que fornecer outro parâmetro que permita a criação de objects com base em parâmetros. O mais fácil é uma function.

 public static string GetAllItems(..., Func del) { ... List tabListItems = new List(); foreach (ListItem listItem in listCollection) { tabListItems.Add(del(listItem)); } ... } 

Você pode então chamá-lo assim

 GetAllItems(..., l => new Foo(l)); 

no .net 3.5 e depois você poderia usar a class de ativador:

 (T)Activator.CreateInstance(typeof(T), args) 

Como ninguém se incomodou em postar a resposta ‘Reflexão’ (que eu pessoalmente acho que é a melhor resposta), aqui vai:

 public static string GetAllItems(...) where T : new() { ... List tabListItems = new List(); foreach (ListItem listItem in listCollection) { Type classType = typeof(T); ConstructorInfo classConstructor = classType.GetConstructor(new Type[] { listItem.GetType() }); T classInstance = (T)classConstructor.Invoke(new object[] { listItem }); tabListItems.Add(classInstance); } ... } 

Edit: Esta resposta está obsoleta devido ao Activator.CreateInstance do .NET 3.5, no entanto, ainda é útil em versões mais antigas do .NET.

Inicializador de object

Se o seu construtor com o parâmetro não está fazendo nada além de definir uma propriedade, você pode fazer isso em C # 3 ou melhor usando um inicializador de object em vez de chamar um construtor (o que é impossível, como foi mencionado):

 public static string GetAllItems(...) where T : new() { ... List tabListItems = new List(); foreach (ListItem listItem in listCollection) { tabListItems.Add(new T() { YourPropertyName = listItem } ); // Now using object initializer } ... } 

Usando isso, você pode sempre colocar qualquer lógica de construtor no construtor padrão (vazio) também.

Activator.CreateInstance ()

Alternativamente, você poderia chamar Activator.CreateInstance () assim:

 public static string GetAllItems(...) where T : new() { ... List tabListItems = new List(); foreach (ListItem listItem in listCollection) { object[] args = new object[] { listItem }; tabListItems.Add((T)Activator.CreateInstance(typeof(T), args)); // Now using Activator.CreateInstance } ... } 

Observe que Activator.CreateInstance pode ter alguma sobrecarga de desempenho que você pode evitar se a velocidade de execução for uma prioridade máxima e outra opção puder ser mantida por você.

Isso não funcionará na sua situação. Você só pode especificar a restrição que tem um construtor vazio:

 public static string GetAllItems(...) where T: new() 

O que você poderia fazer é usar a injeção de propriedade definindo essa interface:

 public interface ITakesAListItem { ListItem Item { set; } } 

Então você poderia alterar o seu método para ser isso:

 public static string GetAllItems(...) where T : ITakesAListItem, new() { ... List tabListItems = new List(); foreach (ListItem listItem in listCollection) { tabListItems.Add(new T() { Item = listItem }); } ... } 

A outra alternativa é o método Func descrito por JaredPar.

Pergunta muito antiga, mas nova resposta 😉

A versão do ExpressionTree : (acho que a solução mais rápida e mais limpa)

Como Welly Tambunan disse, “poderíamos também usar tree de expressão para construir o object”

Isso gerará um ‘construtor’ (function) para o tipo / parâmetros fornecidos. Ele retorna um delegado e aceita os tipos de parâmetro como uma matriz de objects.

Aqui está:

 // this delegate is just, so you don't have to pass an object array. _(params)_ public delegate object ConstructorDelegate(params object[] args); public static ConstructorDelegate CreateConstructor(Type type, params Type[] parameters) { // Get the constructor info for these parameters var constructorInfo = type.GetConstructor(parameters); // define a object[] parameter var paramExpr = Expression.Parameter(typeof(Object[])); // To feed the constructor with the right parameters, we need to generate an array // of parameters that will be read from the initialize object array argument. var constructorParameters = parameters.Select((paramType, index) => // convert the object[index] to the right constructor parameter type. Expression.Convert( // read a value from the object[index] Expression.ArrayAccess( paramExpr, Expression.Constant(index)), paramType)).ToArray(); // just call the constructor. var body = Expression.New(constructorInfo, constructorParameters); var constructor = Expression.Lambda(body, paramExpr); return constructor.Compile(); } 

Exemplo MyClass:

 public class MyClass { public int TestInt { get; private set; } public string TestString { get; private set; } public MyClass(int testInt, string testString) { TestInt = testInt; TestString = testString; } } 

Uso:

 // you should cache this 'constructor' var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string)); // Call the `myConstructor` fucntion to create a new instance. var myObject = myConstructor(10, "test message"); 

insira a descrição da imagem aqui


Outro exemplo: passando os tipos como um array

 var type = typeof(MyClass); var args = new Type[] { typeof(int), typeof(string) }; // you should cache this 'constructor' var myConstructor = CreateConstructor(type, args); // Call the `myConstructor` fucntion to create a new instance. var myObject = myConstructor(10, "test message"); 

DebugView da expressão

 .Lambda #Lambda1(System.Object[] $var1) { .New TestExpressionConstructor.MainWindow+MyClass( (System.Int32)$var1[0], (System.String)$var1[1]) } 

Isso é equivalente ao código gerado:

 public object myConstructor(object[] var1) { return new MyClass( (System.Int32)var1[0], (System.String)var1[1]); } 

Pequena desvantagem

Todos os parâmetros dos tipos de valores são encheckboxdos quando são passados ​​como uma matriz de objects.


Teste de desempenho simples:

 private void TestActivator() { Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < 1024 * 1024 * 10; i++) { var myObject = Activator.CreateInstance(typeof(MyClass), 10, "test message"); } sw.Stop(); Trace.WriteLine("Activator: " + sw.Elapsed); } private void TestReflection() { var constructorInfo = typeof(MyClass).GetConstructor(new[] { typeof(int), typeof(string) }); Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < 1024 * 1024 * 10; i++) { var myObject = constructorInfo.Invoke(new object[] { 10, "test message" }); } sw.Stop(); Trace.WriteLine("Reflection: " + sw.Elapsed); } private void TestExpression() { var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string)); Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < 1024 * 1024 * 10; i++) { var myObject = myConstructor(10, "test message"); } sw.Stop(); Trace.WriteLine("Expression: " + sw.Elapsed); } TestActivator(); TestReflection(); TestExpression(); 

Resultados:

 Activator: 00:00:13.8210732 Reflection: 00:00:05.2986945 Expression: 00:00:00.6681696 

O uso de Expressions é +/- 8 vezes mais rápido do que invocar o ConstructorInfo e +/- 20 vezes mais rápido do que usando o Activator

Você precisa adicionar onde T: new () para deixar o compilador saber que T tem garantia de fornecer um construtor padrão.

 public static string GetAllItems(...) where T: new() 

Se você simplesmente deseja inicializar um campo ou propriedade de membro com o parâmetro constructor, em C #> = 3 você pode fazer isso muito mais facilmente:

 public static string GetAllItems(...) where T : InterfaceOrBaseClass, new() { ... List tabListItems = new List(); foreach (ListItem listItem in listCollection) { tabListItems.Add(new T{ BaseMemberItem = listItem }); // No error, BaseMemberItem owns to InterfaceOrBaseClass. } ... } 

Essa é a mesma coisa que Garry Shutler disse, mas eu gostaria de colocar uma nota adicional.

É claro que você pode usar um truque de propriedade para fazer mais coisas do que apenas definir um valor de campo. Uma propriedade “set ()” pode triggersr qualquer processamento necessário para configurar seus campos relacionados e qualquer outra necessidade para o próprio object, incluindo uma verificação para ver se uma boot completa deve ocorrer antes do object ser usado, simulando uma construção completa ( sim, é uma solução feia, mas supera a nova limitação () do M $.

Não posso garantir se é um furo planejado ou um efeito colateral acidental, mas funciona.

É muito engraçado como M $ pessoas adicionam novos resources à linguagem e parece não fazer uma análise completa dos efeitos colaterais. Todo o genérico é uma boa evidência disso …

Descobri que eu estava recebendo um erro “não pode fornecer argumentos ao criar uma instância do tipo de parâmetro T”, então eu precisava fazer isso:

 var x = Activator.CreateInstance(typeof(T), args) as T; 

Se você tiver access à class que você vai usar, você pode usar essa abordagem que eu usei.

Crie uma interface que tenha um criador alternativo:

 public interface ICreatable1Param { void PopulateInstance(object Param); } 

Faça suas aulas com um criador vazio e implemente este método:

 public class MyClass : ICreatable1Param { public MyClass() { //do something or nothing } public void PopulateInstance (object Param) { //populate the class here } } 

Agora use seus methods genéricos:

 public void MyMethod(...) where T : ICreatable1Param, new() { //do stuff T newT = new T(); T.PopulateInstance(Param); } 

Se você não tem access, envolva a class de destino:

 public class MyClass : ICreatable1Param { public WrappedClass WrappedInstance {get; private set; } public MyClass() { //do something or nothing } public void PopulateInstance (object Param) { WrappedInstance = new WrappedClass(Param); } } 

Às vezes, uso uma abordagem que se assemelha às respostas usando injeção de propriedade, mas mantém o código mais limpo. Em vez de ter uma class / interface base com um conjunto de propriedades, ela contém apenas um método (virtual) Initialize () – que age como um “construtor do homem pobre”. Em seguida, você pode permitir que cada class manipule sua própria boot, da mesma forma que um construtor, o que também adiciona uma maneira conveniente de manipular cadeias de inheritance.

Se muitas vezes me encontrar em situações onde eu quero cada class na cadeia para inicializar suas propriedades exclusivas e, em seguida, chame seu Initialize () – método do pai que por sua vez inicializa propriedades exclusivas do pai e assim por diante. Isso é especialmente útil ao ter classs diferentes, mas com uma hierarquia semelhante, por exemplo, objects de negócios mapeados para / de DTO: s.

Exemplo que usa um dictionary comum para boot:

 void Main() { var values = new Dictionary { { "BaseValue", 1 }, { "DerivedValue", 2 } }; Console.WriteLine(CreateObject(values).ToString()); Console.WriteLine(CreateObject(values).ToString()); } public T CreateObject(IDictionary values) where T : Base, new() { var obj = new T(); obj.Initialize(values); return obj; } public class Base { public int BaseValue { get; set; } public virtual void Initialize(IDictionary values) { BaseValue = values["BaseValue"]; } public override string ToString() { return "BaseValue = " + BaseValue; } } public class Derived : Base { public int DerivedValue { get; set; } public override void Initialize(IDictionary values) { base.Initialize(values); DerivedValue = values["DerivedValue"]; } public override string ToString() { return base.ToString() + ", DerivedValue = " + DerivedValue; } } 

Isso é meio chato, e quando digo tipo de coisa eu posso dizer revoltante, mas supondo que você possa fornecer seu tipo parametrizado com um construtor vazio, então:

 public static T GetTInstance() where T: new() { var constructorTypeSignature = new Type[] {typeof (object)}; var constructorParameters = new object[] {"Create a T"}; return (T) new T().GetType().GetConstructor(constructorTypeSignature).Invoke(constructorParameters); } 

Irá efetivamente permitir que você construa um object de um tipo parametrizado com um argumento. Neste caso eu estou supondo que o construtor que eu quero tenha um único argumento do tipo object . Criamos uma instância fictícia de T usando o construtor vazio permitido de restrição e, em seguida, usamos a reflection para obter um de seus outros construtores.

Eu acredito que você tem que restringir T com uma instrução where para permitir apenas objects com um novo construtor.

Agora, aceita qualquer coisa, incluindo objects sem ela.