2010-08-11 12 views
15

Ero in cerca di modi per fare l'inizializzazione pigra e trovarono Lazy<T> che è incluso nel .NET 4.pigri <T> di implementazione e .NET farmaci generici

Stavo pensando a rotazione mia implementazione di Lazy<T> per NET 3.5 (con una semplice politica di multi-thread), e ho urtato il seguente problema:

pigro ha fondamentalmente due tipi di costruttori:

class Lazy<T> { 

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

che utilizza costruttore di default di T per la creazione di un n istanza di T e

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

che consente al chiamante di decidere come viene creata l'istanza di T.

Ora qui è il problema:

Se voglio in fase di compilazione per il 1 ° ctor vorrei aggiungere una restrizione

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

a livello di classe. Questo mi permetterà di usare new T() per creare un'istanza; ma questa restrizione non è necessaria per il 2 ° ctor e, peggio, limita anche i tipi che posso usare (a quelli con un ctor predefinito)

Se voglio essere in grado di utilizzare qualsiasi tipo con il 2 ° ctor, io non imposterà alcuna restrizione e nel 1 ° ctor userà il reflection per assicurarsi che T abbia un ctor predefinito. Questo approccio, tuttavia, mancherà del controllo in fase di compilazione e genererà un'eccezione di runtime solo se il primo ctor viene utilizzato con il tipo sbagliato.

La mia domanda è: posso ottenere il meglio da entrambi i mondi?

Idealmente, vorrei ottenere il controllo in fase di compilazione per ogni utilizzo di Ctor # 1, ma allo stesso tempo essere in grado di utilizzare il codeC# 2 per i tipi che non hanno un Ctor predefinito.

Come funziona l'implementazione Microsoft? (Non ho prontamente accesso a .NET 4 fonti o DLL).

EDIT: (dopo "Reflector-ing" l'assemblea MS)

ho controllato l'implementazione di riferimento e non fa a tempo di compilazione controlli.
Usa la riflessione per il caso 'default ctor', ovviamente accompagnato dall'eccezione di runtime se le cose vanno male.

+3

È possibile utilizzare Reflector per osservare l'implementazione .NET 4 –

+0

@Thomas mi piace citando me stesso. Mi sembra importante: "Non ho prontamente accesso a .NET 4 fonti o DLL" ... o almeno questo era vero quando ho posto la domanda. Nel frattempo, grazie al suggerimento di Lucas, ho ottenuto una sospensione delle assemblee e ho aggiornato la domanda con le mie conclusioni. –

+0

OK, ho perso la parte "o dll" ... scusa –

risposta

12

mi aspetto che l'attuazione integrato usa semplicemente Activator.CreateInstance<T> per semplicità. Il modo più pulito che posso pensare di barare questo è con una fabbrica separata:

// 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

Il cheat di una persona è il modello di progettazione di un altro. :) – LBushkin

0

La mia domanda è: posso ottenere il meglio di entrambi i mondi?

No.

In sostanza non si ha tempo vincolo controllabile compilazione.

7

è possibile utilizzare un metodo factory statico invece di un sovraccarico al costruttore:

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()); 
    } 
} 

Ora, questo rompe la compatibilità (in qualche misura) con .NET 4.0 versione di Lazy<T>, ma raggiunge la sicurezza in fase di compilazione per entrambi i tipi di utilizzo.

Si potrebbe fare di questo un po 'più pulite, rendendo i costruttori per Lazy<T> interna protetta, e di fornire una classe factory statica che si utilizza sempre di creare istanze:

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

Erm ... scusate la mia ignoranza, ma come posso chiamare un censore protetto di Lazy da Lazy? –

+0

Intendevo scrivere "internal protected" - lo modificherò. – LBushkin

2

Perché non basta scaricare le extesions parallele e installare Lazy<T> per 3.5? Direct link

+1

Perché è difficile ottenere l'approvazione dell'azienda per l'utilizzo di qualsiasi libreria di terze parti. :( –

+0

Ooh ... ma ora posso farmi il naso nell'implementazione vaniglia :) –

0

Qualcosa di simile dovrebbe funzionare per voi. Lo abbiamo usato nel nostro codice locale per circa un anno prima di passare alla 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; 
    } 
    } 
} 
Problemi correlati