2009-12-05 14 views
123

En C# Se puede poner una restricción en un método genérico como:¿Hay un constructor genérico con restricción de parámetro en C#?

public class A { 

    public static void Method<T> (T a) where T : new() { 
     //...do something... 
    } 

} 

donde se especifica que T debe tener un constructor que no requiere ningún parámetro. Me pregunto si hay una manera de añadir una restricción como

El siguiente código no compila "existe un constructor con un parámetro float[,]?":

public class A { 

    public static void Method<T> (T a) where T : new(float[,] u) { 
     //...do something... 
    } 

} 

Una solución alternativa es también ¿útil?

+0

posible duplicado de [Cómo limitar el tipo genérico a debe tener un Constructor que toma ciertos parámetros?] (Http://stackoverflow.com/questions/853703/how-to-constrain-generic-type-to -must-have-a-construtor-that-takes-certain-param) – nawfal

Respuesta

115

Como has encontrado, no se puede hacer esto.

Como una solución que normalmente suministran un delegado que puede crear objetos de tipo T:

public class A { 

    public static void Method<T> (T a, Func<float[,], T> creator) { 
     //...do something... 
    } 

} 
+37

son restricciones de constructores parametrizados ausentes por una razón lógica, o es solo algo que aún no se ha agregado al lenguaje? –

+18

De acuerdo ...deberíamos tener 'new (float, double)', 'new (string)', etc. – SliverNinja

+8

@Sahuagin Creo que no es posible porque cuando heredas de una clase, no hay garantía de que la subclase tenga esa constructor está definido, ya que los constructores no son heredados. Sin embargo, cada clase tiene un constructor de parámetros vacío. – Matthew

5

No. Por el momento, la única restricción de constructor que puede especificar es para un constructor no-arg.

39

No existe tal construcción. Solo puede especificar una restricción de constructor vacía.

Resuelvo este problema con métodos lambda.

public static void Method<T>(Func<int,T> del) { 
    var t = del(42); 
} 

caso de uso

Method(x => new Foo(x)); 
+0

No hay forma de abstraer la creación de 'Foo' dentro del' Método'? –

+0

¿Qué sucede si el usuario del 'Método' hace' Método (x => new Foo()); '? ¿Hay alguna forma de garantizar que la lambda sea así? –

15

Aquí es una solución para esto que yo personalmente encuentro muy eficaz. Si piensas en qué es una restricción de constructor parametrizada genérica, en realidad es una asignación entre tipos y constructores con una firma particular. Puede crear su propia asignación utilizando un diccionario. Ponga estos en una clase estática "de fábrica" ​​y puede crear objetos de tipo variable sin tener que preocuparse por la construcción de una lambda constructor en cada ocasión:

public static class BaseTypeFactory 
{ 
    private delegate BaseType BaseTypeConstructor(int pParam1, int pParam2); 

    private static readonly Dictionary<Type, BaseTypeConstructor> 
    mTypeConstructors = new Dictionary<Type, BaseTypeConstructor> 
    { 
     { typeof(Object1), (pParam1, pParam2) => new Object1(pParam1, pParam2) }, 
     { typeof(Object2), (pParam1, pParam2) => new Object2(pParam1, pParam2) }, 
     { typeof(Object3), (pParam1, pParam2) => new Object3(pParam1, pParam2) } 
    }; 

entonces en su método genérico, por ejemplo:

public static T BuildBaseType<T>(...) 
     where T : BaseType 
    { 
     ... 
     T myObject = (T)mTypeConstructors[typeof(T)](value1, value2); 
     ... 
     return myObject; 
    } 
+1

¿por qué merece esto un pulgar hacia abajo? funciona muy bien en mi experiencia. –

+1

Estoy usando esto ahora, creo que es un buen patrón. Funciona muy bien con el patrón de fábrica. ¡Gracias! – Matthew

32

Al utilizar la reflexión para crear un objeto genérico, el tipo aún necesita el constructor correcto declarado o se lanzará una excepción. Puede pasar cualquier argumento siempre que coincida con uno de los constructores.

Utilizado de esta manera no puede poner una restricción en el constructor de la plantilla. Si falta el constructor, se debe manejar una excepción en tiempo de ejecución en lugar de obtener un error en tiempo de compilación.

// public static object CreateInstance(Type type, params object[] args); 

// Example 1 
T t = (T)Activator.CreateInstance(typeof(T)); 
// Example 2 
T t = (T)Activator.CreateInstance(typeof(T), arg0, arg1, arg2, ...); 
// Example 3 
T t = (T)Activator.CreateInstance(typeof(T), (string)arg0, (int)arg1, (bool)arg2); 
Cuestiones relacionadas