Una solución un poco más elegante podría ser encapsular la clase (o clases) que vamos a guardar, incluyéndola(s) dentro de otra clase auxiliar, de modo que podamos reutilizar esa estructura:
[Serializable]
public class ClaseAGuardar
{
Ejemplo e;
public void SetDatos(Ejemplo e1)
{
e = e1;
}
public Ejemplo GetDatos()
{
return e;
}
}
Y crear una segunda clase, que sea la encargada de guardar y recuperar los datos:
public class Serializador
{
string nombre;
public Serializador(string nombreFich)
{
nombre = nombreFich;
}
public void Guardar(ClaseAGuardar objeto)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(nombre,
FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, objeto);
stream.Close();
}
public ClaseAGuardar Cargar()
{
ClaseAGuardar objeto;
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(nombre,
FileMode.Open, FileAccess.Read, FileShare.Read);
objeto = (ClaseAGuardar)formatter.Deserialize(stream);
stream.Close();
return objeto;
}
}
De modo que un único fuente que contuviera estas tres clases podría ser:
// Ejemplo_09_03a.cs
// Ejemplo de persistencia
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
// ---------------------------------------------
// La clase "de prueba"
[Serializable]
public class Ejemplo
{
int numero;
public void SetNumero(int n)
{
numero = n;
}
public int GetNumero()
{
return numero;
}
}
// ---------------------------------------------
// La clase "que realmente se va a guardar"
[Serializable]
public class ClaseAGuardar
{
Ejemplo e;
public void SetDatos(Ejemplo e1)
{
e = e1;
}
public Ejemplo GetDatos()
{
return e;
}
}
// ---------------------------------------------
// La clase "encargada de guardar"
public class Serializador
{
string nombre;
public Serializador(string nombreFich)
{
nombre = nombreFich;
}
public void Guardar(ClaseAGuardar objeto)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(nombre,
FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, objeto);
stream.Close();
}
public ClaseAGuardar Cargar()
{
ClaseAGuardar objeto;
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(nombre,
FileMode.Open, FileAccess.Read, FileShare.Read);
objeto = (ClaseAGuardar)formatter.Deserialize(stream);
stream.Close();
return objeto;
}
}
// ---------------------------------------------
// Y el programa de prueba
public class Prueba
{
public static void Main()
{
Ejemplo ejemplo = new Ejemplo();
ejemplo.SetNumero(5);
Console.WriteLine("Valor: {0}", ejemplo.GetNumero());
ClaseAGuardar guardador = new ClaseAGuardar();
guardador.SetDatos(ejemplo);
Serializador s = new Serializador("ejemplo.dat");
s.Guardar(guardador);
Ejemplo ejemplo2 = new Ejemplo();
Console.WriteLine("Valor 2: {0}", ejemplo2.GetNumero());
ejemplo2 = s.Cargar().GetDatos();
Console.WriteLine("Y ahora: {0}", ejemplo2.GetNumero());
}
}
Vamos a comprobar que se comporta correctamente en un caso más complicado, haciendo que nuestra clase contenga varios atributos, entre ellos un array de objetos de otra clase. La clase "Serializador" no cambiará, y en un caso real apenas cambiaría la clase "ClaseAGuardar" (que aquí tampoco va a modificarse):
// Ejemplo_09_03b.cs
// Ejemplo de persistencia
// Introduccin a C#, por Nacho Cabanes
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
// ---------------------------------------------
// Las dos clases "de prueba": una contiene a otra
[Serializable]
public class MiniEjemplo
{
int dato;
public void SetDato(int n)
{
dato = n;
}
public int GetDato()
{
return dato;
}
}
// -----
[Serializable]
public class Ejemplo
{
int numero;
float numero2;
MiniEjemplo[] mini;
public Ejemplo()
{
mini = new MiniEjemplo[100];
for (int i=0; i<100; i++)
{
mini[i] = new MiniEjemplo();
mini[i].SetDato( i*2 );
}
}
public void SetNumero(int n)
{
numero = n;
}
public int GetNumero()
{
return numero;
}
public void SetMini(int n, int valor)
{
mini[n].SetDato(valor);
}
public int GetMini(int n)
{
return mini[n].GetDato();
}
}
// ---------------------------------------------
// La clase "que realmente se va a guardar"
[Serializable]
public class ClaseAGuardar
{
Ejemplo e;
public void SetDatos(Ejemplo e1)
{
e = e1;
}
public Ejemplo GetDatos()
{
return e;
}
}
// ---------------------------------------------
// La clase "encargada de guardar"
public class Serializador
{
string nombre;
public Serializador(string nombreFich)
{
nombre = nombreFich;
}
public void Guardar(ClaseAGuardar objeto)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(nombre,
FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, objeto);
stream.Close();
}
public ClaseAGuardar Cargar()
{
ClaseAGuardar objeto;
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(nombre,
FileMode.Open, FileAccess.Read, FileShare.Read);
objeto = (ClaseAGuardar)formatter.Deserialize(stream);
stream.Close();
return objeto;
}
}
// ---------------------------------------------
// Y el programa de prueba
public class Prueba
{
public static void Main()
{
Ejemplo ejemplo = new Ejemplo();
ejemplo.SetNumero(5);
ejemplo.SetMini(50, 500);
Console.WriteLine("Valor: {0}", ejemplo.GetNumero());
ClaseAGuardar guardador = new ClaseAGuardar();
guardador.SetDatos(ejemplo);
Serializador s = new Serializador("ejemplo.dat");
s.Guardar(guardador);
Ejemplo ejemplo2 = new Ejemplo();
Console.WriteLine("Valor 2: {0}", ejemplo2.GetNumero());
ejemplo2 = s.Cargar().GetDatos();
Console.WriteLine("Y ahora: {0}", ejemplo2.GetNumero());
Console.WriteLine("dato 50: {0}", ejemplo2.GetMini(50));
}
}
Esto daría como resultado:
Valor: 5
Valor 2: 0
Y ahora: 5
dato 50: 500
Ejercicios propuestos:
Ejercicio propuesto 9.3.1: