Passa ai contenuti principali

La classe Lazy(Of T)

Una interessante classe inserita nella versione 4.0 del framework è la Lazy(Of T).

Si tratta di un generic il cui scopo è quello di permettere la creazione di oggetti differita.

Uno scenario tipico che può capitare quando sviluppiamo è quello di avere una nostra classe che occupa molte risorse (memoria, accesso a database, accesso a filesystem, e via discorrendo) e desiderare che tale classe venga istanziata solo quando serve.

In più, alcune volte, sarebbe anche comodo poter avere un accesso concorrente a tale istanza.

Se il primo requisito è facilmente implementabile con un manciata di righe di codice, il secondo non è altrettanto banale.

La classe Lazy(Of T) ci viene in aiuto perché è, di fatto, un wrapper che gestisce la creazione dell’istanza del tipo T e il thread safe in maniera nativa.

La classe Lazy prevede una serie di costruttori il più semplice dei quali non prevede argomenti. Sia MyClass la nostra classe di cui vogliamo gestire la creazione differita:

  1. Public Class [MyClass]
  2.  
  3.     Public Sub New()
  4.         ConsoleHelper.WriteConsole("MyClass - New")
  5.     End Sub
  6.  
  7.     Public Sub New(i As Integer)
  8.         ConsoleHelper.WriteConsole(String.Format("MyClass - New with {0}", i))
  9.     End Sub
  10.  
  11.     Public Sub MyMethod()
  12.         ConsoleHelper.WriteConsole("MyClass - MyMethod")
  13.     End Sub
  14. End Class

La classe ConsoleHelper permette di utilizzare la Console per scrivere dei messaggi a cui viene anteposto un timestamp. Nei costruttori della classe MyClass ho inserito la scrittura dei messaggi per poter, effettivamente, capire quando il costruttore stesso viene invocato.

Prendiamo in esame il seguente pezzo di codice:

  1. Sub Main()
  2.     ConsoleHelper.WriteConsole("CreateSimpleLazy - Before")
  3.     Dim lazy = CreateSimpleLazy()
  4.     ConsoleHelper.WriteConsole("CreateSimpleLazy - After")
  5.     Threading.Thread.Sleep(1000)
  6.     lazy.Value.MyMethod()
  7.     Console.ReadLine()
  8. End Sub

e il seguente:

  1. Public Function CreateSimpleLazy() As Lazy(Of [MyClass])
  2.     Dim retVal = New Lazy(Of [MyClass])
  3.  
  4.     Return retVal
  5. End Function

Il metodo CreateSimpleLazy altro non fa che istanziare un oggetto di classe Lazy(Of MyClass) e restituirlo al chiamante.

Nel metodo chiamante eseguiamo la chiamata al metodo MyMethod della MyClass in maniera differita (cioè solo dopo aver atteso 1 secondo dall’istanziazione dell’oggetto Lazy).

Il risultato a video è il seguente:

SNAGHTML122e0a4b

Come possiamo osservare il costruttore della classe MyClass (riga “MyClass – new”) viene richiamato solo nel momento in cui si esegue l’istruzione lazy.Value.MyMethod e non quando viene creato l’oggetto Lazy.

In effetti la classe Lazy espone l’istanza della classe che wrappa tramite la proprietà Value all’interno della quale viene gestito l’effettivo richiamo del costruttore. Se l’oggetto wrappato non è stato ancora istanziato, nel momento in cui viene utilizzata la proprietà Value, viene richiamato il costruttore di default della classe wrappata.

Ma se la nostra classe non prevede un costruttore di default?

In questo caso ci viene in aiuto un altro costruttore della Lazy la cui sintassi è la seguente:

  1. Public Sub New(
  2.               valueFactory As Func(Of T)
  3.               )

Questo costruttore prevede che si possa utilizzare una Func(Of T) come parametro (ad esempio una lambda expression), all’interno della quale creeremo l’istanza della nostra classe e la restituiremo alla Lazy che la gestirà in maniera del tutto analoga al caso precedente:

  1. Sub Main()
  2.     ConsoleHelper.WriteConsole("CreateParametricLazy - Before")
  3.     Dim parametricLazy = CreateParametricLazy(100)
  4.     ConsoleHelper.WriteConsole("CreateParametricLazy - After")
  5.     Threading.Thread.Sleep(1000)
  6.     parametricLazy.Value.MyMethod()
  7.  
  8.     Console.ReadLine()
  9. End Sub

In questo caso utilizziamo il costruttore della classe MyClass che prevede un parametro intero:

  1. Public Function CreateParametricLazy(i As Integer) As Lazy(Of [MyClass])
  2.     Dim retVal = New Lazy(Of [MyClass])(Function()
  3.                                             Return New [MyClass](i)
  4.                                         End Function)
  5.  
  6.     Return retVal
  7. End Function

Il risultato che otteniamo è:

SNAGHTML1236d622

La classe Lazy, per default, ThreadSafe cioè permette accesso contemporaneo (e sicuro) alla proprietà Value da thread differenti. La modalità di default è la LazyThreadSafetyMode .ExecutionAndPubblication che prevede che un solo thread possa eseguire l’inizializzazione di T ma che tale istanza sia poi disponibili a tutti i thread che la richiedono.
Questo significa che se più thread tentano di eseguire l’inizializzazione della classe T, uno solo di questi ha accesso al costruttori e gli altri attendono l’effettiva creazione della stessa.

Questo fatto comporta che se la nostra classe utilizza dei meccanismi di thread safe interni e questi non lavorano correttamente, si potrebbero creare dei dedlock da cui non si può uscire.

Possiamo stabilire il comportamento della classe Lazy rispetto all’accesso contemporaneo con opportuni costruttori della stessa che ci permettono di avere un’istanza non ThreadSafe oppure di stabilire esattamente il LazyThreadSafetyMode da utilizzare. I possibili valori di questa enumerazione sono:

  • None : non thread safe;
  • ExecutionAndPubblication: un unico thread può inizializzare T la cui istanza viene utilizzata da tutti i thread. E’ una modalità ThreadSafe;
  • PublicationOnly : più thread possono inizializzare T, il primo che riesce rende l’istanza disponibile e viene utilizzata dagli altri. Eventuali altre istanze vengono scartate. E’ una modalità ThreadSafe.

La tipologia di ThreadSafe scelta influenza anche il comportamento della classe Lazy rispetto alle eventuali eccezioni generate dal costruttore della classe contenuta.

Se stiamo utilizzando il costruttore di default, l’eventuale eccezione generata viene immediatamente sollevata (a prescindere da quale valore di LazyThreadSafetyMode  abbiamo scelto) mentre se stiamo utilizzando un costruttore con parametri l’eventuale eccezione generata si comporta in maniera differente in base al valore di LazyThreadSafetyMode scelto.

Se si è scelto PublicationOnly (valore di default) le eccezioni vengono immediatamente generate nel costruttore della Lazy (come nel caso precedente), mentre se stiamo utilizzando uno delle altre sue modalità di LazyThreadSafetyMode, allora le eccezioni vengono salvate in cache e proposte nel momento in cui viene invocata la proprietà Value.

Infine, la classe Lazy(Of T) prevede la proprietà IsValueCreated che permette di conoscere se l’istanza di T è stata già creata o meno.

 

Commenti

Post popolari in questo blog

VB.NET: Convertire un file DOC in RTF e PDF con office interop

In questo post vorrei proporvi del codice per poter convertire un file .doc in un file .rtf oppure .pdf utilizzando le API di interoperabilità di Office.Creeremo una classe, DocConverter, che esporrà le due funzionalità sopra citate.Cominciamo con il prevedere un attributo privato della classe che rappresenterà l’applicazione Word che utilizzeremo per la conversione. Creeremo l’istanza dell’attributo privato all’interno del costruttore della classe:PublicSubNew()
IfNot CreateWordApp() Then
ThrowNew ApplicationException("Assembly di interoperabilità con Office non trovato!")
EndIf
EndSub
Private _wordApp As Word.ApplicationClass
ProtectedFunction CreateWordApp() AsBoolean
Dim retval = True
Try
_wordApp = New Word.ApplicationClass()
_wordApp.Visible = False
Catch ex As System.Exception
_wordApp = Nothing
retval = False
EndTry
Return retval
EndFunction

La conversione del file doc sarà effettuata aprendo il file stesso ed eseguendo un’operazione di SaveAs:

Pr…

Cambiare la lingua di Visual Studio 2008

Oggi ho avuto qualche problema installando Windows Mobile 6 Professional SDK Refresh e Windows Mobile 6 Standard SDK Refresh.Scaricati i file di installazione e installati, ho provato a creare un progetto di tipo Windows Mobile 6.0 e mi sono beccato questo errore:Dopo qualche smanettamento abbiamo scoperto (e ringrazio il mitico Matteo per l’aiuto) che il mio Visual Studio 2008, pur essendo in Inglese (prova ne era il fatto che gli hotfix e la SP installata erano nella lingua di Albione) aveva come lingua impostata quella del sistema operativo (italiano).Ovviamente, non avrebbe mai potuto trovare la cartella 1040 (italiano) visto che l’installazione dell’SDK aveva supposto che la lingua del Visual Studio fosse Inglese (1033).La soluzione del problema è duplice:1) Duplicate la cartella 1033 presente nel percorso evidenziato dall’errore e la rinominate 10402) cambiate la lingua di Visual Studio.Per questa ultima eventualità basta andare nel menù Strumenti/Opzioni:e cambiare il linguaggi…

Tascam DR-05 registratore digitale per tutti

Diverso tempo fa ho acquistato il registratore digitale Tascam DR-05 e, ora, dopo diversi mesi di utilizzo, posso dire la mia a proposito.

Si tratta di un ottimo registratore digitale con microfono stereo che permette di ottenere registrazioni di ottima qualitaà anche in ambienti non perfetti dal punto di vista acustico.

Interessante la possibilità di utilizzare un cavalletto di piccole dimensioni come HAMA Mini treppiede o Mini Cavalletto Universale per tenere il microfono sollevato dal tavolo in fase di registrazione grazie allàattacco universale per macchina fotografica che il microfono ha nella parte inferiore.

Da segnalare anche il menu’ ben fatto e la possibilita’ di utilizzare una scheda SD esterna per memorizzare i file audio. Anche a livello di consumo batterie non e’ niente male. Consiglio l’uso delle alcaline non ricaricabili.

Il mio utilizzo e’ stato prettamente di registrazione di podcast vocali (no musica) ma le recensioni confermano che se la cava egregiamente con la mu…