Da DateTimeLib a DateTimeExtensionsLibrary a DateTimeExtensionSnippets

Premessa
Con Visual Basic 2008 sono stati introdotti i metodi Extension (dicitura adottata perché ormai invalsa nell'uso, benché la traduzione di Extension methods corretta sia 'metodi di estensione'). Ho avuto di recente occasione di suggerire un paio di metodi Extension, così mi è venuta l'idea di trasformare in libreria di estensioni (molto più adatta allo scopo) la mia libreria di metodi dedicati al tipo DateTime (link: articolo che conviene leggere per capire, download del codice a corredo, cioè della libreria).

Il problema
I metodi Extension esistono e agiscono solo a livello di namespace (e questo va benissimo), possono venire implementati solo in moduli (e pure questo va benissimo: è questa la vera ragion d'essere del Module, non il banale rimpiazzo del modulo.bas di VB6). Solo che... non si può compilare un modulo in una libreria (così sembrava dai miei tentativi).

La soluzione
Non poteva essere. Dopotutto, nelle librerie del Framework sono già presenti metodi Extension di tipi di base, quindi il modo c'era e andava scoperto. Ne ho parlato con Alessandro Del Sole che ha trovato la soluzione: basta implementare una qualsiasi classe vuota e dichiarare Public il modulo (normalmente il qualificatore è Friend):

Imports System.Runtime.CompilerServices

Public Class DateTimeExtensionsLibrary

End Class

<Extension()> _
Public Module DateTimeExtensions

Tutto qua. Il resto, essendo già pronto il codice della DateTimeLib, è stato un lavoro di rifacimento del codice secondo le specifiche richieste da un metodo Extension, in particolare quella di impostare il primo parametro del tipo che si vuole estendere.

[correzione] Come potete vedere dai primi feedback, abbiamo commesso degli errori: uno (mio) è quello di ritenere che non si possa compilare un progetto Class Library con solo un Module, un altro è quello (nostro) di dedurne che sia necessaria una classe vuota.

Un esempio
Nella libreria DateTimeLib c'è il metodo NextDow, con un overload, che restituisce la data del successivo giorno-di-settimana indicato, a partire dalla data di oggi (primo overload) o da quella indicata (secondo overload):

  ''' 
  ''' Finds the next specified day of week after the specified date
  ''' 
  ''' The day of the week to search for
  Public Shared Function NextDow(ByVal dow As DayOfWeek) As DateTime
    Return NextDow(dow, DateTime.Today)
  End Function
  Public Shared Function NextDow(ByVal dow As DayOfWeek, ByVal data As DateTime) As DateTime
    Dim dt As DateTime = data.AddDays(dow - data.DayOfWeek)
    If dt <= data Then dt = dt.AddDays(7)
    Return dt
  End Function

 Che, una volta referenziata la libreria  dcDateTimeLib e impostata la direttiva:

Imports dcDateTimeLib.DateTimeTool

si usa così:

    Dim results As String = _
        "giovedì prossimo sarà il " & NextDow(DayOfWeek.Thursday).ToShortDateString
     

Nella libreria di estensione non c'è più bisogno del primo overload e il metodo NextDow, estensione di DateTime, diventa:

  ''' 
  ''' Finds the next specified day of week after the instance date
  ''' 
  ''' The day of the week to search for
  <Extension()> _
  Public Function NextDow(ByVal instance As DateTime, ByVal dow As DayOfWeek) As DateTime
    Dim dt As DateTime = instance.AddDays(dow - instance.DayOfWeek)
    If dt <= instance Then dt = dt.AddDays(7)
    Return dt
  End Function

 Che, una volta referenziata la libreria DateTimeExtensionsLibrary e impostata la direttiva:

Imports DateTimeExtensionsLibrary.DateTimeExtensions

si usa così:

    Dim results As String = _
        "giovedì prossimo sarà il " & Today.NextDow(DayOfWeek.Thursday).ToShortDateString

 La differenza sta nel fatto che adesso NextDow è un metodo di istanza e non un metodo di servizio, simile a una 'funzione' di VB6. Non devo ricordarmi che esiste un metodo NextDow fatto per DateTime. Quando digito il punto dopo un tipo data, è Intellisense che mi presenta anche NextDow in mezzo agli altri metodi esposti dal tipo DateTime.

Gli snippet
Prima che Alessandro trovasse la soluzione 'classe vuota - modulo pubblico', io ho pensato che a volte non ci serve un mucchio di librerie, ciascuna col suo mucchio di metodi: a volte ci bastano pochi metodi presi da poche liberie diverse.
Allora ho pensato che ciascuno dei metodi Extension sarebbe potuto diventare 'anche' uno snippet.

L'idea è che, a seconda delle necessità dell'applicazione che stiamo sviluppando, possiamo inserire in un un modulo MyExtensionMethods quei pochi metodi che ci servono. A esempio, come in un caso presentato in lista, il solo metodo Quarter, che nella libreria DateTimeExtensionsLibrary (non è presente in dcDateTimeLib) è implementato così:

  ''' 
  ''' Returns the quarter containing the instance date
  ''' 
  <Extension()> _
  Public Function Quarter(ByVal instance As DateTime) As Integer
    Return ((instance.Month - 1) \ 3) + 1
  End Function

 Tramite uno specifico Snippet Editor, come quello di Bill McCarthy su CodePlex, oppure a mano, possiamo produrre questo snippet:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>QuarterTitle>
      <Author>Diego CattaruzzaAuthor>
      <Description>Returns the quarter containing the instance dateDescription>
      <HelpUrl>HelpUrl>
      <SnippetTypes />
      <Keywords />
      <Shortcut />
    </Header>
    <Snippet>
      <References />
      <Imports>
        <Import>
          <Namespace>System.Runtime.CompilerServicesNamespace>
        </Import>
      </Imports>
      <Declarations />
      <Code Language="VB" Kind="" Delimiter="$">
<![CDATA[
<Extension()> _ Public Function Quarter(ByVal instance As DateTime) As Integer Return ((instance.Month - 1) \ 3) + 1 End Function ]]>
</Code> </Snippet> </CodeSnippet> </CodeSnippets>

e salvarlo come Quarter.snippet nella nostra cartella \Visual Studio 2008\Code Snippets\Visual Basic\My Code Snippets\Datetime, appositamente creata per l'occasione. Quando ci serve Quarter, aggiungiamo un modulo alla nostra applicazione, dandogli un nome appropriato, come nomemiaapplicazioneExtensionsModule, e vi inseriamo lo snippet. La direttiva Imports verrà automaticamente implementata.

Conclusione
E' innegabile che con Visual Basic 2008, anche Express, abbiamo tra le mani un potente strumento di programmazione.

In area download trovate sia la libreria DateTimeExtensionsLibrary, con un progetto di test del tutto simile a quello relativo alla classe dcDateTimeLib, che lo zip della sottocartella My Code Snippets\Datetime.

Print | posted @ sabato 23 maggio 2009 00:00

Comments on this entry:

Gravatar # re: Da DateTimeLib a DateTimeExtensionsLibrary a DateTimeExtensionSnippets
by Antonio "tdj" Catucci at 21/05/2009 00:20

Scusa Diego, ma non riesco a capire qual è il problema che hai avuto.
Gravatar # re: Da DateTimeLib a DateTimeExtensionsLibrary a DateTimeExtensionSnippets
by Diego x Antonio at 21/05/2009 15:33

Non sapevo che si potesse compilare una Class Library con una classe vuota.
Quindi non trovavo la maniera di compilare una libreria con al suo interno soltanto il modulo.
Aggiungendo una semplicissima classe qualsiasi, anche vuota (in pratica una 'dummy class'), la libreria era compilabile.
Qualificare Public il modulo è stata solo la conseguenza del fatto che 'non si vedevano i metodi esposti nel modulo'.
Gravatar # re: Da DateTimeLib a DateTimeExtensionsLibrary a DateTimeExtensionSnippets
by Antonio "tdj" Catucci at 22/05/2009 00:35

Ad onor del vero io riesco a compilare tranquillamente una Class Library con solo un modulo senza dover aggiungere una classe vuota.

Tra l'altro VB converte il modulo in una classe quindi non vedo perchè non debba essere possibile un'operazione del genere.

Ho scaricato il tuo progetto ed ho eliminato la classe vuota e tutto funziona (ovviamente) senza problemi.

O abbiamo 2 versioni diverse di VS oppure io sono più fortunato :)

Mi dici il messaggio di errore che hai ottenuto?
Gravatar # re: Da DateTimeLib a DateTimeExtensionsLibrary a DateTimeExtensionSnippets
by Diego x Antonio at 22/05/2009 16:49

E' come dici tu.
E' comunque una cosa che non sapevo, quella di poter fare una class library senza alcuna classe.
Quindi l'unica cosa che serve è che il modulo sia Public, invece che Friend.

Ciò che accadeva 'prima' era che non potevo applicare l'attributo extension né a una classe, né a un metodo di essa (cercando di 'bypassare' la trasformazione da modulo in classe che fa VB 'dopo'), quindi mi sembrava non fosse possibile creare una class library, da cui tutto lo strogolamento mentale successivo.

Grazie Antonio :o))
Gravatar # Sostituire l
by Diego Cattaruzza at 10/08/2009 20:11

Comments have been closed on this topic.