Estoy tratando de crear un nuevo objeto de tipo T a través de su constructor cuando lo agrego a la lista.
Recibo un error de compilación: el mensaje de error es:
'T': no puede proporcionar argumentos al crear una instancia de una variable
¡Pero mis clases tienen un argumento de constructor! ¿Cómo puedo hacer que esto funcione?
public static string GetAllItems<T>(...) where T : new()
{
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(new T(listItem)); // error here.
}
...
}
c#
.net
generics
new-operator
LB.
fuente
fuente
Respuestas:
Para crear una instancia de un tipo genérico en una función, debe restringirla con el indicador "nuevo".
Sin embargo, eso solo funcionará cuando desee llamar al constructor que no tiene parámetros. No es el caso aquí. En su lugar, deberá proporcionar otro parámetro que permita la creación de objetos basados en parámetros. Lo más fácil es una función.
Entonces puedes llamarlo así
fuente
T result = thunk == null ? new T() : thunk();
el beneficio de esto para mí es consolidar la lógica de laT
creación en un lugar en lugar de crear a vecesT
dentro y a veces fuera del método.en .Net 3.5 y después de que pueda usar la clase activadora:
fuente
Activator.CreateInstance
esta única cosa, parecerá que su constructor no se utiliza en absoluto, y alguien podría intentar "limpiar" y eliminarlo (para causar un error de tiempo de ejecución en algún tiempo al azar en el futuro). Es posible que desee considerar agregar una función ficticia donde usa este constructor solo para obtener un error de compilación si intenta eliminarlo.Como nadie se molestó en publicar la respuesta 'Reflexión' (que personalmente creo que es la mejor respuesta), aquí va:
Editar: esta respuesta está en desuso debido a .NET 3.5's Activator.CreateInstance, sin embargo, todavía es útil en versiones anteriores de .NET.
fuente
Inicializador de objeto
Si su constructor con el parámetro no está haciendo nada además de establecer una propiedad, puede hacerlo en C # 3 o mejor usando un inicializador de objeto en lugar de llamar a un constructor (lo cual es imposible, como se ha mencionado):
Con esto, también puede colocar cualquier lógica de constructor en el constructor predeterminado (vacío).
Activator.CreateInstance ()
Alternativamente, puede llamar a Activator.CreateInstance () de esta manera:
Tenga en cuenta que Activator.CreateInstance puede tener algunos gastos generales de rendimiento que puede querer evitar si la velocidad de ejecución es una de las principales prioridades y le es posible mantener otra opción.
fuente
T
proteger sus invariantes (dado queT
tiene> 0 dependencias o valores requeridos, ahora puede crear instanciasT
que estén en un estado no válido / inutilizable. A menos queT
sea algo muy simple como un modelo de vista DTO, diría que evite esto.Muy antigua pregunta, pero nueva respuesta ;-)
La versión ExpressionTree : (creo que la solución más rápida y limpia)
Como dijo Welly Tambunan , "también podríamos usar el árbol de expresión para construir el objeto"
Esto generará un 'constructor' (función) para el tipo / parámetros dados. Devuelve un delegado y acepta los tipos de parámetros como una matriz de objetos.
Aquí está:
Ejemplo MyClass:
Uso:
Otro ejemplo: pasar los tipos como una matriz
Vista de depuración de expresión
Esto es equivalente al código que se genera:
Pequeño inconveniente
Todos los parámetros de valuetypes se encuadran cuando se pasan como una matriz de objetos.
Prueba de rendimiento simple:
Resultados:
Usar
Expressions
es +/- 8 veces más rápido que invocarConstructorInfo
y +/- 20 veces más rápido que usarActivator
fuente
var myConstructor = CreateConstructor(typeof(MyClass<int>), typeof(int));
esto está funcionando bien. No lo sé.Esto no funcionará en tu situación. Solo puede especificar la restricción de que tiene un constructor vacío:
Lo que podría hacer es usar la inyección de propiedades definiendo esta interfaz:
Entonces podrías alterar tu método para que sea así:
La otra alternativa es el
Func
método descrito por JaredPar.fuente
Debe agregar where T: new () para que el compilador sepa que T está garantizado para proporcionar un constructor predeterminado.
fuente
Si simplemente desea inicializar un campo miembro o propiedad con el parámetro constructor, en C #> = 3 puede hacerlo más fácilmente:
Esto es lo mismo que dijo Garry Shutler, pero me gustaría poner una nota adicional.
Por supuesto, puede usar un truco de propiedad para hacer más cosas que solo establecer un valor de campo. Una propiedad "set ()" puede desencadenar cualquier procesamiento necesario para configurar sus campos relacionados y cualquier otra necesidad del objeto en sí, incluida una verificación para ver si se debe realizar una inicialización completa antes de usar el objeto, simulando una construcción completa ( sí, es una solución fea, pero supera la nueva limitación () de M $).
No puedo asegurar si es un agujero planificado o un efecto secundario accidental, pero funciona.
Es muy divertido cómo la gente con EM agrega nuevas características al lenguaje y parece no hacer un análisis completo de los efectos secundarios. Todo lo genérico es una buena evidencia de esto ...
fuente
Descubrí que recibía un error "no puedo proporcionar argumentos al crear una instancia del parámetro de tipo T", así que necesitaba hacer esto:
fuente
Si tiene acceso a la clase que va a usar, puede usar este enfoque que yo usé.
Cree una interfaz que tenga un creador alternativo:
Haga sus clases con un creador vacío e implemente este método:
Ahora usa tus métodos genéricos:
Si no tiene acceso, ajuste la clase de destino:
fuente
Esto es un poco sucio, y cuando digo un poco sucio, puedo decir repugnante, pero suponiendo que pueda proporcionar su tipo parametrizado con un constructor vacío, entonces:
Efectivamente le permitirá construir un objeto a partir de un tipo parametrizado con un argumento. En este caso, supongo que el constructor que quiero tiene un único argumento de tipo
object
. Creamos una instancia ficticia de T utilizando la restricción permitida constructor vacío y luego utilizamos la reflexión para obtener uno de sus otros constructores.fuente
A veces uso un enfoque que se asemeja a las respuestas usando la inyección de propiedades, pero mantiene el código más limpio. En lugar de tener una clase / interfaz base con un conjunto de propiedades, solo contiene un método (virtual) Initialize () que actúa como un "constructor de pobres". Luego puede dejar que cada clase maneje su propia inicialización tal como lo haría un constructor, lo que también agrega una forma conveniente de manejar cadenas de herencia.
Si a menudo me encuentro en situaciones en las que quiero que cada clase de la cadena inicialice sus propiedades únicas, y luego llame al método Initialize () de su padre, que a su vez inicializa las propiedades únicas del padre y así sucesivamente. Esto es especialmente útil cuando se tienen diferentes clases, pero con una jerarquía similar, por ejemplo, objetos de negocios que se asignan a / desde DTO: s.
Ejemplo que usa un diccionario común para la inicialización:
fuente
Si todo lo que necesita es la conversión de ListItem a su tipo T, puede implementar esta conversión en clase T como operador de conversión.
fuente
Creo que debe restringir T con una instrucción where para permitir solo objetos con un nuevo constructor.
En este momento acepta cualquier cosa, incluidos los objetos sin él.
fuente