Reflector è uno dei miei decompilatori preferiti. Permette di vedere il meccanismo interno delle classi della Framework Common Library e di capire nei dettagli tanti aspetti del .NET Framework.
Reflector è disponibile gratuitamente all’indirizzo http://www.aisto.com/roeder/dotnet/ . Se lo scaricate usate di frequente il menu Check for Updates, dal momento che escono versioni aggiornate quasi ogni settimana.
E’ divertente decompilare i metodi dell’assembly Microsoft.VisualBasic.dll. Si scopre così che tante vecchie keyword del Visual Basic 6.0 che sono state “apparentemente” mantenute sono in realtà dei wrapper di metodi C#.
Tutti sappiamo che gli array nel .NET Framework hanno dimensione fissa. Ma allora come mai in VB.NET è presente l’istruzione ReDim Preserve? Era l’istruzione che in VB6 ridimensionava un array mantenendo intatto il suo contenuto.
Decompilando l’assembly Microsoft.VisualBasic.dll con Reflector si può cercare nella classe Strings il metodo privato e statico RedimPreserve
private static Array RedimPreserve(Array SourceArray, int Length)
{
Type type1 = SourceArray.GetType();
Array array2 = Array.CreateInstance(SourceArray.GetType().GetElementType(), Length);
Array.Copy(SourceArray, array2, Length);
return array2;
}
Come si vede, il ReDim Preserve non modifica le dimensioni dell’array originale, ma lo copia in un nuovo array la cui lunghezza è passata come parametro a questo metodo privato. Perciò, se siamo seguaci di Visual Basic .NET ma non vogliamo travestire troppo il nostro codice da Visual Basic 6, consideriamo la possibilità di usare direttamente Array.Copy al posto di ReDim Preserve.
Chi è passato da Visual Basic .NET a C# lamenta l’assenza di alcune utili funzioni come IsDate. Ma... tutto quello che si può fare con VB.NET si può fare anche con C#. Nulla di più facile che decompilare il metodo IsDate() di VB.NET (nella classe Information) ed ecco che sappiamo come tradurlo in C#. Il giro è un po’ tortuoso perché il metodo ci rimanda alla classe sealed DateType del namespace Microsoft.VisualBasic.CompilerServices. Ed ecco che nel metodo FromString troviamo il cuore della funzione:
public
static DateTime FromString(string Value, CultureInfo culture){
DateTime time1;
try {
time1 = DateTime.Parse(StringType.ToHalfwidthNumbers(Value), culture,
DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
}
catch (Exception) {
throw new InvalidCastException(Utils.GetResourceString("InvalidCast_FromStringTo", Strings.Left(Value, 0x20), "Date"));
}
return time1;}
Quindi per verificare se un oggetto è una data, la funzione IsDate chiama il metodo statico DateTime.Parse, intercetta un’eventuale eccezione e riscatena una InvalidCastException al chiamante.
Perciò, se passiamo a IsDate un oggetto che non è una data valida, il metodo scatenerà un’eccezione per restituirci false. A questo punto dobbiamo decidere se possiamo accettare il costo di un’eccezione (che come sappiamo non è piccolo) o se vogliamo usare metodi diversi da IsDate per validare le nostre date. Un’alternativa potrebbero essere le Regular Expressions.
In un precedente post ho ceduto anch’io alla tentazione e ho mostrato del codice C# che include il namespace Microsoft.VisualBasic: utilizzavo la funzione RGB per convertire un oggetto System.Drawing.Color a 32 bit in un oggetto Color di Excel, a 24 bit:
using Microsoft.VisualBasic;
using Utilities;
private void MyColorRange(object Rng, Color Col)
{
using (new switchCultureInfo())
((Excel.Range)Rng).Interior.Color = Information.RGB(Col.R, Col.G, Col.B);
}
Ma non c’è più bisogno di dire che anche in questo snippet possiamo facilmente sbarazzarci di VB:
using Utilities;
private void MyColorRange(object Rng, Color Col)
{
using (new switchCultureInfo())
((Excel.Range)Rng).Interior.Color = (((Col.B * 0x10000) + (Col.G * 0x100)) + Col.R);
}
Qualcuno potrebbe obiettare che tutto ciò è un’inutile pignoleria. Ma personalmente preferisco evitare di avere un assembly in più caricato in memoria piuttosto che cedere alle abitudini radicate dopo anni di sviluppo con VB. In fondo se vogliamo fare emettere un piccolo beep, costa davvero poco sforzo, anzichè scrivere una cosa del tipo
Microsoft.VisualBasic.Information.Beep();
Chiamare la funzione nativa della user32.dll:
[DllImport("user32", CharSet=CharSet.Unicode)]
internal static extern int MessageBeep(int uType);
Inoltre, ricordiamo che l’includere Microsoft.VisualBasic può portare a pericolose ambiguità su alcuni metodi. Se non si ha l’occhio ben addestrato è possibile ad esempio confondere il metodo statico Format della classe System.String con il metodo statico Format della classe Microsoft.VisualBasic.Strings, e purtroppo i due metodi non fanno la stessa cosa.
E’ vero che la non collisione dei nomi è garantita solo a livello di namespace e non a livello di classe, ma se cerchiamo di non dare nemmeno uno spiraglio a possibili problemi, ne trarremo solo giovamento.
?>