una - tipos genericos en c#
C#: ¿Tipos genéricos que tienen un constructor? (5)
C #, y VB.Net para el caso, no admiten la idea de restringir un genérico para tener un constructor con parámetros específicos. Solo soporta restricciones para tener un constructor vacío.
Una solución alternativa es hacer que la persona que llama pase a un Lambda de fábrica para crear el valor. Por ejemplo
public void CreateItem(Func<int,T> del) {
T oItem = del(10);
}
Sitio de llamadas
CreateItem(x => new SomeClass(x));
Tengo el siguiente código de prueba de C #:
class MyItem
{
MyItem( int a ) {}
}
class MyContainer< T >
where T : MyItem, new()
{
public void CreateItem()
{
T oItem = new T( 10 );
}
}
Visual Studio no puede compilarlo, el error está en la línea donde se usa ''nuevo'':
''T'': cannot provide arguments when creating an instance of a variable type
¿Es posible en C # crear un objeto de tipo genérico con un constructor sin parámetros? No es un problema hacer eso en las plantillas de C ++, así que tengo mucha curiosidad por qué no puedo hacer lo mismo en C #. ¿Tal vez se requiera algún ''donde'' adicional o la sintaxis es diferente?
En mi opinión, el mejor enfoque aquí es un método de inicialización, es decir,
interface ISomeInterface {
void Init(int i);
}
class Foo : ISomeInterface {
void ISomeInterface.Init(int i) { /* ... */ }
}
static class Program {
static T Create<T>(int i) where T : class, ISomeInterface, new() {
T t = new T();
t.Init(i);
return t;
}
static void Main() {
Foo foo = Create<Foo>(123);
}
}
Sin embargo, puede hacer lo que quiera con Expression
(pero sin soporte en tiempo de compilación):
using System;
using System.Linq.Expressions;
class Foo {
public Foo(int i) { /* ... */ }
}
static class Program {
static T Create<T>(int i) {
return CtorCache<T>.Create(i);
}
static class CtorCache<T> {
static Func<int, T> ctor;
public static T Create(int i) {
if (ctor == null) ctor = CreateCtor();
return ctor(i);
}
static Func<int, T> CreateCtor() {
var param = Expression.Parameter(typeof(int), "i");
var ci = typeof(T).GetConstructor(new[] {typeof(int)});
if(ci == null) throw new InvalidOperationException("No such ctor");
var body = Expression.New(ci, param);
return Expression.Lambda<Func<int, T>>(body, param).Compile();
}
}
static void Main() {
Foo foo = Create<Foo>(123);
}
}
Tenga en cuenta que esto almacena en caché y reutiliza al delegado para el rendimiento.
No hay tal restricción genérica, por lo que no es posible directamente (esto es una limitación de CLR). Si desea esto, debe proporcionar una clase de fábrica (que tiene un constructor sin parámetros) y pasarla como un segundo parámetro de tipo genérico.
Se puede hacer con reflexión:
public void CreateItem()
{
int constructorparm1 = 10;
T oItem = Activator.CreateInstance(typeof(T), constructorparm1) as T;
}
Pero no hay una restricción genérica para garantizar que T
implemente el constructor deseado, por lo que no recomendaría hacerlo a menos que tenga cuidado de declarar ese constructor en cada tipo que implemente la interfaz.
Un patrón que utilizo es hacer que la clase restringida implemente una interfaz que defina un método Init con la firma apropiada:
interface IMyItem
{
void Init(int a);
}
class MyItem : IMyItem
{
MyItem() {}
void Init(int a) { }
}
class MyContainer< T >
where T : MyItem, IMyItem, new()
{
public void CreateItem()
{
T oItem = new T();
oItem.Init( 10 );
}
}