Premetto che questo è il mio post numero 200....EVVIVA!!!
Nel mio ultimo post ho parlato di un metodo spartano,
brutale e poco consigliabile per generare un file XML in cui salvare le
impostazioni di una qualsiasi nostra applicazione. Il metodo che ho scritto,
sebbene funzioni, ha suscitato qualche reazione negativa, perchè come
giustamente è stato osservato non è assolutamente il modo
migliore per serializzare su XML. In questo post, dopo averci ragionato
su per circa 24 ore, propongo il metodo più politically-correct, quello migliore ed
adatto a sfruttare meglio le capacità del framework.
Ovvero, sfruttare i namespace
System.Xml e System.Xml.Serialization offerti
da .NET.
Vediamo innanzitutto dove sta il problema e come è stato
risolto.
Il problema? Color (nel
nostro caso) ed altre classi del framework (in tutti gli altri casi)
Supponiamo di avere una
classe Settings che debba esporre Name e Style. Name è di tipo string, mentre Style è di tipo Color
. La classe è implementata, come solito, da membri
privati e membri pubblici. L'ho dotata di due metodi statici:
public static void Serialize(Settings Which);
public static Settings Deserialize(string FileName);
Il primo serializza e genera il file XML. Il secondo legge il file e
restituisce un'istanza di Settings.
Detto questo, testiamo
un attimo con una manciata di righe C#, così:
Settings mySett = new Settings();
mySett.Name = "Nome";
mySett.Style = Color.Red;
Settings.Serialize(mySett);
Il metodo Serialize non fa altro che:
XmlSerializer ser = new XmlSerializer(typeof(Settings));
StreamWriter wri = new StreamWriter(@"D:\Pippo.xml", false, System.Text.Encoding.ASCII);
ser.Serialize(wri, Which);
Il codice è esclusivamente a scopo di test, quindi
perdonatemi il D:\Pippo.xml
cablato nel codice. Una volta che il codice ha girato, apro
il file XML appena creato, ed ottengo quanto segue:
<?xml version="1.0" encoding="us-ascii"?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>Nome</Name>
<Style />
</Settings>
La property Name della classe è stata serializzata
correttamente, mentre Style è vuota. Sto googlando ancora alla ricerca di
un motivo: ho trovato milioni e milioni di links, ma siccome non ne ho
trovato nessuno che ritengo valido, preferisco andare avanti meglio nella mia
ricerca, oppure che magari qualcuno segnali qualche buon link.
Come risolviamo il problema?
La prima
cosa che mi è venuta in mente è di usare un'altra classe al posto della classe
Color. Con "un'altra classe" intendo una classe
scritta da noi, che possa wrappare in qualche modo la classe Color.
Io ho
quindi creato una classe HappySignColor. Questa classe espone
banalmente 4 membri di tipo int, quindi perfettamente
serializzabili nativamente da .NET: i membri sono A (Alpha), R (Red), G (Green)
e B (Blue). Ho creato un paio di costruttori utili & veloci ed ho creato un
metodo GetColor():
public Color GetColor()
{
Color ret = Color.FromArgb(_A, _R, _G, _B);
return(ret);
}
che non fa altro che ritornare un'istanza di Color costruita con la
combinazione ARGB. Questa classe, ripeto, è perfettamente serializzabile.
Adesso, basta andare nella classe Settings ed usare
HappySignColor al posto di Color ed il gioco è fatto. Proviamo un po'.
Il codice di test finale
Dopo aver opportunamente
corretto Settings, usiamo questo codice di test:
Settings mySett = new Settings();
mySett.Name = "Nome";
mySett.Style = new HappySignColor(0, 255, 100, 50);
Settings.Serialize(mySett);
La property Style è di tipo HappySignColor, ricordiamocelo.
La chiamata a Serialize cosa produce? Produce un file XML di
questo tipo:
<?xml version="1.0" encoding="us-ascii"?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>Nome</Name>
<Style>
<A>255</A>
<R>255</R>
<G>100</G>
<B>50</B>
</Style>
</Settings>
La property Style è stata "scomposta" nei suoi 4 membri
pubblici ed il gioco è fatto! Il metodo Deserialize funziona al
contrario: dato un filename XML, lo apre, lo legge, lo deserializza e
ritorna al chiamante un oggetto di tipo Settings.
Ultimo appunto: come utilizzare HappySignColor
Giusto per
precisazione. Abbiamo visto come Style sia di tipo HappySignColor. Non posso
usarla nativamente come Color: un eventuale istruzione this.BackColor = mySett.Style; genera una bella
exception. Per questo motivo ho creato il metodo GetColor(). Io
posso scrivere qualcosa tipo this.BackColor = mySett.Style.GetColor(); et voilà. Questa volta il gioco è
veramente fatto!!!