2011-05-12 22 views
8

Así que esto es lo que estoy tratando de hacer.donde cláusula en un constructor en C#?

Estoy creando una clase genérica que asigna el tipo especificado por el parámetro genérico de una de dos maneras, determinado por qué constructor sobrecargado se utiliza.

Aquí está el ejemplo:

class MyClass<T> 
    where T : class 
{ 
    public delegate T Allocator(); 
    public MyClass() 
    { 
     obj = new T(); 
    } 

    public MyClass(Allocator alloc) 
    { 
     obj = alloc(); 
    } 

    T obj; 
} 

Esta clase requiere que el tipo T es un refType en todos los casos. Para el constructor predeterminado, queremos crear una instancia de T a través de su constructor predeterminado. Me gustaría poner un where T : new() en mi constructor por defecto, así:

public MyClass() 
    where T : new() 
{ 
    obj = new T(); 
} 

Sin embargo, esto no es válido C#. Básicamente, solo quiero agregar la restricción en el tipo T para tener un constructor predeterminado solo cuando se usa el constructor predeterminado de MyClass().

En el segundo constructor de MyClass, dejamos que el usuario determine cómo asignar para T con su propio método de asignación, por lo que obviamente tiene sentido que MyClass no aplique T sea constructivo predeterminado en todos los casos.

Tengo la sensación de que tendré que usar la reflexión en el constructor predeterminado para esto, pero espero que no.

Sé que esto se puede hacer porque la clase Lazy<T> en .NET 4.0 no requiere que T sea construible por defecto en el nivel de clase, sin embargo, tiene constructores similares a los de mi ejemplo. Me gustaría saber cómo lo hace al menos Lazy<T>.

Respuesta

8

Solo puede incluir restricciones en la declaración donde está introduciendo un parámetro de tipo genérico.

Sin embargo, se puede introducir un método genérico en un no genérico tipo: (. Me gustaría personalmente sólo tiene que utilizar Func<T> en lugar de declarar un tipo de delegado separada por cierto)

public class MyClass 
{ 
    public static MyClass<T> Create<T>() where T : class, new() 
    { 
     return new MyClass<T>(() => new T()); 
    } 
} 

public class MyClass<T> where T : class 
{ 
    T obj; 

    public MyClass(Allocator allocator) 
    { 
     obj = allocator(); 
    } 
} 

continuación, puede utilizar:

MyClass<Foo> foo = MyClass.Create<Foo>(); // Enforces the constraint 
+0

Cambiando mi respuesta a esta ... Leí algo sobre 'Activator.CreateInstance ()' y de hecho no hace la comprobación de la construcción predeterminada en tiempo de compilación (que yo prefiero). Fui con la clase de fábrica genérica en su lugar. –

+0

¿Cómo se puede definir MyClass dos veces sin usar la palabra clave 'parcial'? No estoy familiarizado con esta sintaxis. –

+1

@Robert Dailey: No he declarado 'MyClass' dos veces: he declarado' MyClass' (no genérico) y 'MyClass ' (genérico con un parámetro de tipo). Son tipos separados, al igual que 'Nullable' y' Nullable 'son tipos separados. –

7

Básicamente, solo quiero agregar la restricción en el tipo T para tener un constructor predeterminado solo cuando se usa el constructor predeterminado de MyClass().

No es posible aplicar esto con una restricción en T. Puede ser que where T : new() se especifique en la definición de MyClass<T> o que lo haga sin dicha restricción.

Tengo la sensación de que tendré que usar la reflexión en el constructor predeterminado para esto, pero espero que no.

No se puede imponer la restricción, pero se puede decir

obj = Activator.CreateInstance<T>(); 

que invocará el constructor por defecto para T.

Sé que esto se puede hacer porque la clase Lazy en .NET 4.0 no requiere que T sea constructible por defecto en el nivel de clase, pero tiene constructores similares a los de mi ejemplo. Me gustaría saber cómo va Lazy, al menos.

Lazy<T> requiere que especifique un delegado que devuelva instancias T. Básicamente, requiere que especifique un Func<T>.

+0

usted puede * * imponer la restricción - no sólo en t el constructor Introducirlo en un método genérico en otro lugar funciona bien. –

+0

@Jon Skeet: La restricción que requiere el OP no se puede aplicar en el parámetro de tipo 'T' que está definido por la clase genérica' MyClass '. – jason

+0

No en el tipo en sí, no ... pero puede crear un método genérico simple en un tipo no genérico y usarlo en lugar de un constructor público sin parámetros: vea mi respuesta. El punto, tal como lo entiendo, es proporcionar una forma simple, pero segura en tiempo de compilación, de crear una instancia de 'MyClass' usando el constructor sin parámetros para' T'. Mi respuesta hace eso. –

3

Usted puede tratar de utilizar Activator

class MyClass<T> where T : class 
{ 
    public MyClass() 
    { 
     obj = Activator.CreateInstance<T>(); 
    } 
} 
+0

también: 'Activator.CreateInstance ()' –

+0

Eso deja el tiempo de verificación al tiempo de ejecución, que no es ideal. –

+0

'Activator.CreateInstance ()' solo comprueba si es construible por defecto en tiempo de ejecución? –

-2

Bueno, ya que o bien tienen la restricción o no. Resulta que Lazy usa Activator para crear una instancia. Así es como Lazy<T> funciona código eliminado en caso de problemas de derechos de autor.

+2

No estoy seguro de que la licencia del código .NET BCL permita publicarlo en Internet ... –

+0

Observado. ¡Encerrame y tira la llave! –

+0

¿Qué tipo de respuesta es esa? –