2010-08-11 20 views
15

yo estaba buscando maneras de hacer la inicialización perezosa y encontraron Lazy<T> que se incluye en .NET 4.Lazy <T> de implementación y .NET genéricos

Estaba pensando en rodar mi propia implementación de Lazy<T> para .NET 3.5 (con una política multi-hilo sencillo), y me encontré con el siguiente problema:

Lazy tiene básicamente dos tipos de constructores:

class Lazy<T> { 

    public Lazy(){...} // ctor #1 

que utiliza constructor por defecto de T como la creación de una n ejemplo de T, y

public Lazy(Func<T> func){...} // ctor #2 

que le permite a la persona que llama decidir cómo se crea la instancia de T.

Ahora aquí está el problema:

Si quiero en tiempo de compilación para la primera comprobación de Héctor voy a añadir una restricción

class Lazy<T> where T: new() {...} 

a nivel de clase. Esto me permitirá usar new T() para crear una instancia; pero esta restricción no es necesaria para el segundo ctor y, lo que es peor, también restringe los tipos que puedo usar (para aquellos con un ctor predeterminado)

Si deseo poder usar cualquier tipo con el 2do ctor, no establecerá ninguna restricción, y en el 1er ctor utilizará la reflexión para asegurarse de que T tenga un ctor predeterminado. Sin embargo, este enfoque carecerá de la verificación en tiempo de compilación, y solo lanzará una excepción de tiempo de ejecución si el primer ctor se utiliza con el tipo incorrecto.

Mi pregunta es: ¿Puedo obtener lo mejor de ambos mundos?

Idealmente, me gustaría obtener la verificación en tiempo de compilación para cada uso de ctor # 1, pero al mismo tiempo poder usar ctor # 2 para los tipos que no tienen un ctor predeterminado.

¿Cómo lo hace la implementación de Microsoft? (No tengo acceso a las fuentes .NET 4 o dlls).

EDIT: (Después de "reflector-ing" la asamblea MS)

he comprobado la implementación de referencia y no lo hace en tiempo de compilación cheques.
Utiliza la reflexión para el caso de "ctor predeterminado", por supuesto, acompañado por la excepción de tiempo de ejecución si las cosas van mal.

+3

Puede utilizar el reflector para mirar el .NET 4 aplicación –

+0

@Thomas me gusta citando a mí mismo. Me hace sonar importante: "No tengo acceso fácilmente a las fuentes .NET 4 o dlls" ... o al menos eso era cierto cuando hice la pregunta. Mientras tanto, gracias a la sugerencia de Lucas, obtuve las asambleas y actualicé la pregunta con mis hallazgos. –

+0

OK, me perdí la parte "o dlls" ... lo siento –

Respuesta

12

espero que la aplicación incorporada simplemente utiliza Activator.CreateInstance<T> de sencillez. La manera más limpia que puedo pensar en hacer trampas esto es con una fábrica separada:

// non-generic factory class with generic methods 
public static class Lazy { 
    public static Lazy<T> Create<T>() where T : new() { 
     return Create<T>(() => new T()); 
    } 
    public static Lazy<T> Create<T>(Func<T> ctor) { ... } 
} 
public class Lazy<T> { ... } 
+0

El truco de una persona es el patrón de diseño de otra persona. :) – LBushkin

0

Mi pregunta es: ¿Puedo obtener lo mejor de en ambos mundos?

Básicamente que no tienen limitaciones de tiempo de compilación comprobable.

7

Se puede usar un método de fábrica estática en lugar de una sobrecarga al constructor:

public class Lazy<T> 
{ 
    public Lazy(Func<T> f) { /*...*/ } 

    public static Lazy<R> Default<R>() where R : T, new() 
    { 
     return new Lazy<R>(() => new R()); 
    } 
} 

Ahora, esto rompe la compatibilidad (hasta cierto punto) con .NET 4.0 versión de Lazy<T>, pero logra seguridad en tiempo de compilación para ambos tipos de uso.

Usted puede hacer esto un poco más limpio al hacer que los constructores de Lazy<T> interna protegida, y proporcionar una clase de fábrica estática que siempre utilice para crear instancias:

public static class Lazy { 
    static Lazy<T> Create<T>(Func<T>) { ... } 
    static Lazy<T> Create<T>() where T : new() { ... } 
} 
+1

Erm ... disculpe mi ignorancia, pero ¿cómo puedo llamar a un ctor protegido de Lazy de Lazy? –

+0

Quise escribir 'internal protected' - Lo corregiré. – LBushkin

2

¿Por qué no sólo tiene que descargar los extesions paralelas e instalar Lazy<T> de 3,5? Direct link

+1

Porque es un dolor obtener la aprobación de la compañía para usar bibliotecas de terceros. :( –

+0

Ooh ... pero ahora puedo curiosear en la implementación vainilla :) –

0

Algo como esto debería funcionar para usted. Usamos esto en nuestra base de código local durante aproximadamente un año antes de pasar a 4.0.

public class Lazy<T> { 
    private Func<T> func; 
    private T result; 
    private bool hasValue; 
    public Lazy(Func<T> func) { 
    this.func = func; 
    this.hasValue = false; 
    } 
    public T Value { 
    get { 
     if (!this.hasValue) { 
     this.result = this.func(); 
     this.hasValue = true; 
     } 
     return this.result; 
    } 
    } 
} 
Cuestiones relacionadas