TABLA DE CONTENIDO Pág.

Anuncio
TABLA DE CONTENIDO
Pág.
1.
INTERFACES ............................................................................................................ 1
1.1
DEFINICIÓN ..................................................................................................................1
1.2
1.3
INTERFAZ BANDERA.........................................................................................................4
EJEMPLOS DE SITUACIONES EN LAS CUALES SE EMPLEAN INTERFACES. ............................................4
1.1.1
1.1.2
2
AGRUPACIÓN DE CLASES POR FUNCIONALIDAD: NAMESPACES. ........................... 18
2.1
2.2
2.3
3
LANZAR UNA EXCEPCIÓN................................................................................................. 24
LOS BLOQUES TRY, CATCH Y FINALLY. ............................................................................... 26
CONSIDERACIONES RESPECTO AL MANEJO DE LAS EXCEPCIONES. ................................................ 27
MANEJO DE COLECCIONES EN .NET. ........................................................................ 29
4.1
4.2
4.3
4.4
4.5
5
ESPACIOS DE NOMBRES ANIDADOS. ................................................................................... 19
EL USING. .................................................................................................................. 21
EL ACCESO FÍSICO. ....................................................................................................... 21
MANEJO DE ERRORES EN .NET................................................................................. 24
3.1
3.2
3.3
4
Encabezado .........................................................................................................2
El cuerpo.............................................................................................................2
LA INTERFAZ SYSTEM.COLLECTIONS.ICOLLECTION................................................................. 29
LA INTERFAZ SYSTEM.COLLECTIONS.ICOMPARER................................................................... 31
LA INTERFAZ SYSTEM.COLLECTIONS.IENUMERATOR. .............................................................. 32
LA CLASE SYSTEM.COLLECTIONS.ARRAYLIST........................................................................ 32
ALGUNAS OBSERVACIONES ADICIONALES. ............................................................................ 37
FLUJOS Y SERIALIZACIÓN EN .NET ......................................................................... 39
5.1
5.2
LA DEFINICIÓN DE FLUJO. ............................................................................................... 39
CLASES PARA EL MANEJO DE FLUJOS EN .NET ....................................................................... 40
5.3
5.4
EL CONCEPTO DE SERIALIZACIÓN ...................................................................................... 49
CONCEPTOS BÁSICOS PARA EL MANEJO DE SERIALIZACIÓN EN .NET ............................................ 49
5.2.1
5.2.2
5.2.3
5.2.4
5.2.5
5.2.6
5.2.7
5.2.8
FileMode. .......................................................................................................... 40
FileAccess.......................................................................................................... 40
System.IO.FileShare ........................................................................................... 40
File. .................................................................................................................. 41
StreamReader.................................................................................................... 41
BinaryReader. .................................................................................................... 44
StreamWriter. .................................................................................................... 46
BinaryWriter. ..................................................................................................... 47
1. Interfaces
Una interfaz es una forma de establecer un contrato respecto a ofrecer un
determinado comportamiento, independiente de la forma en la cual este se preste.
Para clarificar esto se puede citar el caso de una interfaz muy común, tanto en
.NET como en Java, la interfaz IComparable. Esta interfaz puede ser empleada
por cualquier clase que requiera otorgar a sus objetos la posibilidad de
compararse entre ellos, sin importar a qué jerarquía de herencia pertenezcan. Un
programador que esté desarrollando un programa de recursos humanos, bien
puede requerir que los objetos Empleados puedan compararse, para así
garantizar que no haya duplicados;y otro programdor que esté desarrollando la
aplicación de Facturación puede requerir la habilidad de comparar objetos
pertenecientes a la clase Factura con objetos pertenecientes a la clase Recibos de
Pago. Es claro que los objetos pertenecen a jerarquias de herencia diferentes, e
incluso en el segundo caso se requiere comparar objetos pertenecientes a clases
diferentes. En este tipo de situaciones el poder establecer el compromiso de
ofrecer un comportamiento toma particular valor.
La solución a situaciones como la presentada en el párrafo anterior, no se puede
plantear en términos de crear una nueva clase que contenga estos servicios en
forma abstracta, y hacer que las clases mencionadas hereden de ella, ya que
pertenecen a jerarquías de herencia diversas, y .NET no permite la herencia
múltiple. ¿Así que se deben manejar en forma aislada? No, pues como una
forma de dar respuesta a este tipo de situaciones se cuenta con una estructura
denominada Interfaz, la cual sólo puede contener el encabezado de métodos y
propiedades, es decir lo necesario para establecer el compromiso de prestar unos
ciertos servicios, sin importar la forma en la cual ellos se implementen.
Este mecanismo, como ya se dijo, sólo establece el compromiso de que ciertos
servicios se prestarán, sin embargo no dice qué se hará para prestarlos, para ello
se requiere que las clases implementen esta estructura y le den cuerpo a estos
métodos, de manera que efectivamente presten el servicio. Por lo tanto las
interfaces son estructuras que permiten ofrecer un cierto servicio, pero son las
clases que la implementan las encargadas de dar cuerpo a estos servicios y por
tantos son los objetos de estas clases, quienes están en capacidad de prestar
dichos servicios.
1.1
Definición
Al definir una interfaz sólo se trabaja con encabezados, definiendo el encabezado
de ella misma y el encabezado de cada uno de los servicios que se deberán
implementar.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
1
1.1.1 Encabezado
El encabezado contiene información sobre la interfaz, los detalles se presentan a
continuación:
• modificador de acceso: Indica qué clases conocerán la existencia de la interfaz
que se está definiendo. Si se quiere que la conozcan todas las clases, se
debe utilizar el modificador de acceso public, pero si se quiere que sólo la
conozcan las clases que comparten el espacio de nombres con ella se debe
emplear el modificador de acceso internal, este último se asume si se omite.
• interface: Es una palabra reservada e indica que lo que se está definiendo es
una interfaz.
• nombre: Es el nombre que se le quiere dar a la interfaz, por convención se
recomienda que empiece con la letra i en mayúscula (I), y su nombre de una
idea del tipo de compromiso que se adquiere al implementar esta interfaz.
• : NombreDeLaInterfazPadre: Esta sintaxis se emplea si la interfaz que se está
definiendo va a heredar de una, o varias, interfaces existentes. A diferencia
de las clases, las interfaces admiten la herencia múltiple así que cada
interfaz puede heredar de tantas interfaces como se requiere, y basta
separarlas con una coma ( , ).
1.1.2 El cuerpo.
El cuerpo de la interfaz: después de crear el encabezado, se empieza a definir su
contenido es decir el encabezado de los elementos que requiere que la interfaz
contenga. Estos elementos, como ya se dijo, pueden ser de estos tipos:
• Propiedades
• Métodos
Ahora se entrará en detalle respecto a los ítems de la lista anterior:
Propiedades: Aquí se presenta el compromiso de ofrecer una propiedad
determinada, y se específica si contendrá get, set o ambos. Su encabezado debe
contener:
• modificador de acceso: NO se puede asociar un modificador de acceso a la
propiedad en su definición en la interfaz, pero las clases que la
implementen tendrán que emplear el modificador de acceso public, de lo
contrario se considerará que se está definiendo una nueva propiedad.
• indicador de pertenencia: En una interfaz no se pueden definir propiedades
pertenecientes a la clase, sólo a objetos.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
2
• tipo: para cada propiedad debe indicarse qué tipo de información puede
recibir o devolver, este tipo no puede ser modificado en la clase, o se
considerará que se está definiendo una nueva propiedad.
• nombre: las propiedades deben tener un nombre, el cual debe iniciar con
mayúscula.
• métodos asociados: como se mencionó anteriormente aquí se debe definir si
la propiedad contará con el método get y el método set o ambos.
Métodos: son los servicios que se comprometerán a prestar los objetos
pertenecientes a las clases que implementen la interfaz que se está definiendo.
Dado que en C# hay tres tipos de métodos, deben hacerse algunas precisiones al
respecto:
• constructores: estos son los métodos que construyen los objetos de la
clase a la cual pertenecen, o sea que NO tiene ningún sentido definir el
encabezado de un constructor en una interfaz, además porque el
constructor tiene que llamarse igual a la clase.
• Main(string args[]): este método pertenece a la clase, y en las interfaces
no pueden darse encabezado a métodos que vayan a pertenecer a la
clase, sólo a sus instancias.
• métodos definidos por usuarios: Son los métodos que permiten definir qué
servicios se comprometerán a prestar las clases que implementen esta
interfaz. Al definir el encabezado de estos métodos deben tenerse en
cuenta los siguientes elementos:
¾ modificador de acceso: Igual que a las propiedades, no puede
asignarse ningún modificador de acceso a un método al definirlo en
la interfaz, pero las clases que implementen dicha interfaz deben
emplear el modificador public, de lo contrario se interpretará como
la definición de un nuevo método.
¾ indicador de modificación: Tal como se ha mencionado, en las
interfaces los métodos no pueden contener cuerpo sólo el
encabezado, es decir todos los métodos definidos en interfaces son
abstractos, sin necesidad de emplear la palabra reservada abstract.
¾ indicador de pertenencia: las interfaces sólo definen métodos
asociados a los objetos, así que este indicador tiene que ser omitido,
pues static no es admitido.
¾ tipo de retorno: debe indicarse el tipo a retornar.
¾ nombre: los métodos tienen que tener un nombre.
¾ parámetros: al definir la interfaz, debe indicarse el tipo y orden de los
parámetros que el método recibirá.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
3
1.2
Interfaz bandera.
Al iniciar este tema, el de interfaces, se mencionó que se emplean para establecer
el compromiso de prestar una serie de servicios, es decir tener un
comportamiento previamente definido. Sin embargo hay otro caso en el cual las
interfaces son muy empleadas, es cuando se requiere definir que un cierto método
puede recibir objetos de diversas clase, pertenecientes aún a jerarquías de
herencia diversas, en este caso la forma más sencilla de lograr esto es hacer que
todas las clases implementen una interfaz denominada “bandera”, y en el
encabezado del método indicar que se recibirá uno o varios objetos pertenecientes
a clases que implementen esta interfaz. Es decir una interfaz bandera indica que
esa clase requiere ser identificada como apta para algo, pero no necesariamente
compromete un comportamiento.
1.3
Ejemplos de situaciones en las cuales se emplean interfaces.
Para ilustrar la utilización de interfaces se presentará una situación, el análisis
hecho y por último el código.
Interfaces como una forma de herencia múltiple.
Esta es una biblioteca pública, en la cual se manejan dos tipos de productos,
obras literarias y películas.
Aquí se consideran obras literarias tanto las revistas como los libros, de ellas
requerimos el nombre, la editorial; sin son revistas también queremos conocer su
número, el volumen y la fecha de edición, además de los artículos que se
incluyen en ese número, de los cuales necesitamos el nombre, el tema, una
breve descripción y su autor o autores. Pero si son libros lo que se requiere
saber es a qué edición pertenecen, el autor o autores y la fecha en la cual fue
publicado.
De las películas requerimos conocer el nombre, el género, la clasificación, la
fecha de lanzamiento y un indicador de si es estreno o no. Los formatos en los
cuales las tenemos son cinta de VHS, en las cuales se guarda una sola película,
a la cual de acuerdo al género y la clasificación se le asigna la edad para venta,
pues las películas de VHS se venden, no se prestan. El otro formato es DVD, en
el cual podemos guardar varias películas, y para este formato también se debe
calcular la edad de préstamo de acuerdo al género y la clasificación de la
película más fuerte que contenga este formato.
Como ya se mencionó las películas en VHS están para la venta, y las películas
en DVD, al igual que las obras literarias, están para préstamos. Para cada
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
4
préstamo debemos contar con una forma de registrarlo, guardar la devolución y
calcular la multa, en caso de que se requiera.
Diagrama de Clases:
ObraLiteraría
IPrestable
string nombre
string editorial
bool prestar()
bool recibir()
double multa()
ObraLiteraria(string, string)
Nombre { set, get }
Editorial { set, get }
Revista
int número
int volumen
string fechaEdición
Libro
int edición
string fechaPublicación
Revista(string,string,int,int,string)
Número { set, get }
Volumen { set, get }
FechaEdición { set, get }
bool incluirArtículo(Artículo)
Artículo obtenerArtículo(string,string)
Libro(string,string,int,string)
Libro(string,string,int,string,int)
Edición { set, get }
FechaPublicación { set, get }
bool incluirAutor(Autor)
Autor buscarAutor(string)
DVD
string estado
VHS
string estado
DVD(string)
Estado { set, get}
int edad()
VHS(string)
Estado { set,
get}
*
Película
string nombre
string género
string clasificación
string fecha
bool estreno
*
1
*
*
Artículo
string nombre
string tema
string descripción
*
Artículo(string,string,string)
Artículo(string,string,string,int
)
Nombre { set, get }
Tema { set, get }
Descripción { set, get }
bool incluirAutor(Autor)
*
Autor
string nombre
string especialidad
Autor(string,string)
Nombre { set, get }
Especialidad{ set, get
*
*
Película(string,string,string,
string,bool)
Nombre { set, get }
Género { set, get }
Clasificación { set, get }
Fecha { set, get }
Estreno { set, get }
1
Código:
// definición de la clase ObraLiteraria.
using System;
using interfaces;
namespace ítems {
public class ObraLiteraria : IPrestable {
string nombre;
string editorial;
string estado;
public string Nombre {
set {
nombre = value; }
get {
return nombre; }
}
public string Editorial {
set {
editorial = value; }
get {
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
5
return editorial; }
}
public string Estado {
set {
estado = value; }
get {
return estado; }
}
public ObraLiteraria(string newNombre, string newEditorial) {
nombre = newNombre;
editorial = newEditorial;
estado = "disponible";
}
public bool prestar() {
if (estado.Equals("disponible")){
estado = "prestado";
// . . .
return true;
}
return false;
}
public bool recibir() {
if (estado.Equals("prestado")) {
estado = "disponible";
// . . .
return true;
}
return false;
}
public double multa() {
double valorMulta = 0;
// . . .
return valorMulta;
}
}
}
// definición de la clase Revista.
using System;
using System.Collections;
namespace ítems {
public class Revista : ObraLiteraria {
int número;
int volumen;
string fechaEdición;
ArrayList misArtículos;
public int Número {
get {
return número; }
}
public int Volumen {
get
{
return volumen; }
}
public string FechaEdición {
get {
return fechaEdición; }
}
public ArrayList MisArtículos {
set {
misArtículos = value; }
get {
return misArtículos; }
}
public bool incluirArtículo(Artículo elArtículo) {
if (misArtículos.Contains(elArtículo))
return false;
misArtículos.Add(elArtículo);
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
6
return true;
}
public Artículo obtenerArtículo(string elNombre, string elTema){
foreach (object uno in misArtículos) {
Artículo dos = (Artículo)uno;
if (dos.Nombre == elNombre && dos.Tema == elTema)
return dos;
}
return null;
}
public Revista(string newNombre, string newEditorial, int newNúmero, int newVolumen, string newFecha) :
base(newNombre, newEditorial) {
número = newNúmero;
volumen = newVolumen;
fechaEdición = newFecha;
}
public override int GetHashCode() {
return (this.Nombre.GetHashCode() + número * 2 + volumen * 3);
}
public override bool Equals(object obj) {
return this.GetHashCode() == obj.GetHashCode();
}
}
}
// definición de la clase Libro.
using System;
namespace ítems {
public class Libro : ObraLiteraria {
int edición;
string fechaPublicación;
Autor[] losAutores;
public int Edición{
set{
edición = value; }
get{
return edición; }
}
public string FechaPublicación {
set {
fechaPublicación = value; }
get {
return fechaPublicación; }
}
public Autor[] LosAutores {
set {
losAutores = value; }
get {
return losAutores; }
}
public Libro(string newNombre, string newEditorial, int newEdición, string newFecha) : base( newNombre,
newEditorial) {
edición = newEdición;
fechaPublicación = newFecha;
losAutores = new Autor[1];
}
public Libro(string newNombre, string newEditorial, int newEdición, string newFecha,int cantidad) :
base(newNombre, newEditorial){
edición = newEdición;
fechaPublicación = newFecha;
losAutores = new Autor[cantidad];
}
public override int GetHashCode(){
return this.Nombre.GetHashCode();
}
public override bool Equals(object obj){
return this.GetHashCode() == obj.GetHashCode();
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
7
public bool incluirAutor(Autor elNuevo) {
// retornará false si ya está registrado o si el arreglo ya está lleno.
foreach(Autor uno in losAutores) {
if (uno.Equals(elNuevo)) {
return false;
}
}
for (int contador = 0; contador < losAutores.Length; contador++) {
if (losAutores[contador] == null) {
losAutores[contador] = elNuevo;
return true;
}
}
return false;
}
public Autor buscarAutor(string elNombre) {
foreach(Autor uno in losAutores) {
if (uno.Nombre.Equals(elNombre)) {
return uno;
}
}
return null;
}
}
}
// definición de la clase Artículo.
using System;
namespace ítems {
public class Artículo {
string nombre;
string tema;
string descripción;
Autor[] losAutores;
public string Nombre {
set {
nombre = value; }
get {
return nombre; }
}
public string Tema {
set {
tema = value; }
get {
return tema; }
}
public string Descripción {
set {
descripción = value; }
get {
return descripción; }
}
public Autor[] LosAutores {
set {
losAutores = value; }
get {
return losAutores; }
}
public Artículo(string newNombre, string newTema, string newDescripción) {
nombre = newNombre;
tema = newTema;
descripción = newDescripción;
losAutores = new Autor[1];
}
public Artículo(string newNombre, string newTema, string newDescripción, int cantidad) {
nombre = newNombre;
tema = newTema;
descripción = newDescripción;
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
8
losAutores = new Autor[cantidad];
}
public override bool Equals(object unArtículo) {
return this.GetHashCode() == unArtículo.GetHashCode();
}
public override int GetHashCode() {
return ( (this.tema.GetHashCode()*2) + (this.nombre.GetHashCode()) );
}
public bool incluirAutor(Autor elNuevo) {
// retornará false si ya está registrado o si el arreglo ya está lleno.
foreach(Autor uno in losAutores) {
if (uno.Equals(elNuevo)) {
return false;
}
}
for (int contador = 0; contador < losAutores.Length; contador++){
if (losAutores[contador] == null){
losAutores[contador] = elNuevo;
return true;
}
}
return false;
}
public Autor buscarAutor(string elNombre) {
foreach(Autor uno in losAutores) {
if (uno.Nombre.Equals(elNombre)) {
return uno;
}
}
return null;
}
}
}
// definición de la clase Autor.
using System;
namespace ítems {
public class Autor {
string nombre;
string especialidad;
public string Nombre{
set {
nombre = value; }
get{
return nombre; }
}
public string Especialidad {
set {
especialidad = value; }
get {
return especialidad; }
}
public Autor(string newNombre, string newEspecialidad) {
this.nombre = newNombre;
this.especialidad = newEspecialidad;
}
public override int GetHashCode(){
return Nombre.GetHashCode();
}
public override bool Equals(object obj){
return this.GetHashCode() == obj.GetHashCode();
}
}
}
// definición de la clase DVD.
using System;
using System.Collections;
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
9
using interfaces;
namespace ítems {
public class DVD : IPrestable {
static int elConsecutivo;
int consecutivo;
string estado;
ArrayList lasPelículas;
public string Estado{
set{
estado = value; }
get{
return estado; }
}
public ArrayList LasPelículas{
set{
lasPelículas = value; }
get{
return lasPelículas; }
}
static DVD () {
elConsecutivo = 0;
}
public DVD(ArrayList newPelículas) {
consecutivo = DVD.elConsecutivo++;
estado = "diponible";
lasPelículas = newPelículas;
}
public override int GetHashCode() {
return this.consecutivo;
}
public override bool Equals(object obj){
return this.GetHashCode() == obj.GetHashCode();
}
public bool incluirPelícula(Película laPelícula) {
if (lasPelículas.Contains(laPelícula))
return false;
lasPelículas.Add(laPelícula);
return true;
}
public Película obtenerPelícula(string elNombre) {
foreach (object uno in lasPelículas) {
Película dos = (Película)uno;
if (dos.Nombre == elNombre)
return dos;
}
return null;
}
public bool prestar() {
if (estado.Equals("disponible")) {
estado = "prestado";
// . . .
return true;
}
return false;
}
public bool recibir() {
if (estado.Equals("prestado")) {
estado = "disponible";
// . . .
return true;
}
return false;
}
public double multa() {
double valorMulta = 0;
// . . .
return valorMulta;
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
10
}
}
// definición de la clase VHS.
using System;
namespace ítems {
public class VHS {
static int elConsecutivo;
int consecutivo;
string estado;
Película laPelícula;
public string Estado {
set {
estado = value; }
get {
return estado; }
}
public Película LaPelícula {
set {
laPelícula = value; }
get {
return laPelícula; }
}
static VHS() {
elConsecutivo = 0;
}
public override int GetHashCode() {
return this.consecutivo;
}
public override bool Equals(object obj) {
return this.GetHashCode() == obj.GetHashCode();
}
public VHS(Película newPelícula) {
consecutivo = VHS.elConsecutivo++;
estado = "en la tienda";
laPelícula = newPelícula;
}
}
}
// definición de la clase Película.
using System;
namespace ítems {
public class Película {
string nombre;
string género;
string clasificación;
string fecha;
bool estreno;
public string Nombre {
set{
nombre = value; }
get{
return nombre; }
}
public string Género {
set {
género = value; }
get {
return género; }
}
public string Clasificación {
set {
clasificación = value; }
get {
return clasificación; }
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
11
public string Fecha {
set {
fecha = value; }
get {
return fecha; }
}
public bool Estreno {
set {
estreno = value; }
get {
return estreno; }
}
public Película(string newNombre, string newGénero, string newClasificación, string newFecha, bool newEstreno){
nombre = newNombre;
género = newGénero;
clasificación = newClasificación;
fecha = newFecha;
estreno = newEstreno;
}
public override int GetHashCode(){
return this.nombre.GetHashCode();
}
public override bool Equals(object obj){
return this.GetHashCode() == obj.GetHashCode();
}
}
}
// definición de la interfaz IPrestable.
using System;
namespace interfaces {
public interface IPrestable {
bool prestar();
bool recibir();
double multa();
}
}
Interfaces como bandera.
En este supermercado vendemos diversos productos, pero por ahora
centrémonos en los alimentos y la ropa, para poder explicarle como es que se
acomodan las cosas. Los alimentos se agrupan de acuerdo a si son carnes,
frutas o verduras, lácteos o rancho y licores y tienen IVA sólo si son carnes frías
o pertenecen a la sección de rancho y licores. La ropa se separa como ropa de
hombre, mujer o niño y se le cobra IVA sólo si está catalogada como ropa de
lujo. En la caja se registra el producto y si está marcado como producto con IVA
se calcula, del precio de venta, cuánto corresponde al IVA.
El porcentaje para calcular el IVA, por fortuna está unificado y corresponde al
10%.
Diagrama de Clases:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
12
Producto
string código
string nombre
double precioVenta
string proveedor
Liquidador
Producto(string,string,string)
Código { set, get }
Nombre { set, get }
PrecioVenta { set, get}
Proveedor { set, get}
Ropa
Alimentos
string marca
string talla
string género
int edad
string calidad
double peso
double valorKilogramo
Alimentos(string string,string,
string,double,double)
Calidad { set, get }
Peso { set, get }
ValorKilogramo { set, get }
Lácteos
string fechaEmpaque
string fechaVencimiento
bool requiereRefrigeración
Lácteos (string, string, string,
string, double, double, string,
string, bool)
FechaEmpaque { set, get }
FechaVencimiento { set, get }
RequiereRefrigeración{ set, get }
Ropa (string, string,
string, strin, string,
string, int)
Marca { set, get }
Talla { set, get }
Género { set, get }
Edad { set, get }
Carnes
string tipo
string fechaEmpaque
string fechaVencimiento
Carne (string, string, string,
string, double, double, string,
string, string)
Tipo { set, get }
FechaEmpaque { set, get }
FechaVencimiento { set, get }
RanchoLicores
DeLujo
string marca
string tamaño
string posiciónAlmacenamiento
RanchoLicores (string, string, string,
string, double, double, string, string,
string)
Marca { set, get }
Tamaño { set, get }
PosiciónAlamacenamiento { set, get }
IIva
Frías
Código:
// definición de la clase Producto.
using System;
namespace Supermercado {
public class Producto {
string código;
string nombre;
double precioVenta;
string proveedor;
public string Código {
set {
código = value; }
get {
return código; }
}
public string Nombre {
set {
nombre = value; }
get {
return nombre; }
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
13
public double PrecioVenta {
set {
precioVenta = value; }
get {
return precioVenta;
}
}
public string Proveedor {
set {
proveedor = value; }
get {
return proveedor; }
}
public Producto(){ }
public Producto(string newCódigo, string newNombre, double newPrecio, string newProveedor) {
código = newCódigo;
nombre = newNombre;
precioVenta = newPrecio;
proveedor = newProveedor;
}
}
}
// definición de la interfaz IIva.
using System;
namespace interfaces {
public interface IIva {
// observe que NO define ningún método.
}
}
// definición de la clase Alimentos.
using System;
namespace Supermercado {
public class Alimentos : Producto {
string calidad;
double peso;
double valorKilogramo;
public string Calidad {
set {
calidad = value; }
get {
return calidad; }
}
public double Peso {
set {
peso = value; }
get {
return peso; }
}
public double ValorKilogramo{
set{
valorKilogramo = value; }
get{
return valorKilogramo; }
}
public Alimentos(string newCódigo, string newNombre, double newPrecio, string newProveedor, string newCalidad,
double newPeso, double newValor) : base(newNombre, newProveedor, newPrecio, newCalidad) {
calidad = newCalidad;
peso = newPeso;
valorKilogramo = newValor;
}
public Alimentos() { }
}
}
// definición de la clase Lácteos.
using System;
namespace Supermercado {
public class Lácteos:Alimentos {
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
14
string fechaEmpaque;
string fechaVencimiento;
bool requiereRefrigeración;
public string FechaEmpaque {
set {
fechaEmpaque = value; }
get {
return fechaEmpaque; }
}
public string FechaVencimiento {
set {
fechaVencimiento = value; }
get {
return fechaVencimiento; }
}
public bool RequiereRefrigeración {
set {
requiereRefrigeración = value; }
get {
return requiereRefrigeración;
}
}
public Lácteos(string newCódigo, string newNombre, double newPrecio, string newProveedor, string newCalidad,
double newPeso, double newValor, string newEmpaque, string newVencimiento, bool
newRefrigeración) : base (newCódigo, newNombre, newPrecio, newProveedor, newCalidad,
newPeso, newValor) {
fechaEmpaque = newEmpaque;
fechaVencimiento = newVencimiento;
requiereRefrigeración = newRefrigeración;
}
}
}
// definición de la clase Carnes.
using System;
namespace Supermercado {
public class Carnes : Alimentos {
string tipo;
string fechaEmpaque;
string fechaVencimiento;
public string Tipo {
set {
tipo = value; }
get {
return tipo; }
}
public string FechaEmpaque {
set {
fechaEmpaque = value; }
get {
return fechaEmpaque; }
}
public string FechaVencimiento {
set {
fechaVencimiento = value; }
get {
return fechaVencimiento; }
}
public Carnes(string newCódigo, string newNombre, double newPrecio, string newProveedor, string newCalidad,
double newPeso, double newValor, string newTipo, string newEmpaque, string newVencimiento): base
(newCódigo, newNombre, newPrecio, newProveedor, newCalidad, newPeso, newValor) {
tipo = newTipo;
fechaEmpaque = newEmpaque;
fechaVencimiento = newVencimiento;
}
public Carnes(){}
}
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
15
// definición de la clase Frías.
// observe cuidadosamente la definición de esta clase, ¿ en qué difiere de su clase padre ?.
using System;
using interfaces;
namespace Supermercado {
public class Frías : Carnes, IIva {
public Frías (string newCódigo, string newNombre, double newPrecio, string newProveedor, string newCalidad,
double newPeso, double newValor, string newTipo, string newEmpaque, string newVencimiento) :
base (newCódigo, newNombre, newPrecio, newProveedor, newCalidad, newPeso, newValor,
newTipo, newEmpaque, newVencimiento)
{
}
public Frías(){}
}
}
// definición de la clase RachoLicores.
using System;
namespace Supermercado {
public class RanchoLicores : Alimentos {
string marca;
string tamaño;
string posiciónAlmacenamiento;
public string Marca {
set {
marca = value; }
get {
return marca; }
}
public string Tamaño {
set {
tamaño = value; }
get {
return tamaño; }
}
public string PosiciónAlmacenamiento {
set {
posiciónAlmacenamiento = value; }
get {
return posiciónAlmacenamiento; }
}
public RanchoLicores (string newCódigo, string newNombre, double newPrecio, string newProveedor, string
newCalidad, double newPeso, double newValor, string newMarca, string newTamaño, string
newPosición) : base (newCódigo, newNombre, newPrecio, newProveedor, newCalidad,
newPeso, newValor) {
marca = newMarca;
tamaño = newTamaño;
posiciónAlmacenamiento = newPosición;
}
}
}
// definición de la clase Ropa.
using System;
namespace Supermercado {
public class Ropa : Producto {
string marca;
string talla;
string género;
int edad;
public string Marca{
set {
marca = value; }
get {
return marca; }
}
public string Talla {
set {
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
16
talla = value; }
get {
return talla; }
}
public string Género {
set {
género = value; }
get {
return género; }
}
public int Edad {
set {
edad = value; }
get {
return edad; }
}
public Ropa(){}
public Ropa (string newCódigo, string newNombre, double newPrecio, string newProveedor, string newMarca, string
newTalla, string newGénero, int newEdad) : base(newCódigo, newNombre, newPrecio,
newProveedor) {
marca = newMarca;
talla = newTalla;
género = newGénero;
edad = newEdad; }
}
}
// definición de la clase Frías.
// observe cuidadosamente la definición de esta clase, ¿ en qué difiere de su clase padre ?.
using System;
using interfaces;
namespace Supermercado {
public class DeLujo : Ropa, IIva {
public DeLujo(string newCódigo, string newNombre, double newPrecio, string newProveedor, string newMarca,
string newTalla, string newGénero, int newEdad) : base (newCódigo, newNombre, newPrecio,
newProveedor, newMarca, newTalla, newGénero, newEdad) { }
public DeLujo() { }
}
}
// definición de la clase Liquidador.
// observe cuidadosamente esta clase, ¿ encuentra alguna razón para la creación de la interfaz IIva ?.
using System;
using interfaces;
namespace Supermercado.caja {
public class Liquidador {
public static double liquidaCompra(Producto elProducto, int laCantidad){
IIva conIva = elProducto as IIva;
if (conIva == null){
return compraSinIva(elProducto, laCantidad);
}
return compraConIva(elProducto, laCantidad);
}
public static double compraSinIva(Producto elProducto, int laCantidad) {
return elProducto.PrecioVenta * laCantidad;
}
public static double compraConIva(Producto elProducto, int laCantidad) {
return (elProducto.PrecioVenta * laCantidad) * 1.10 ;
}
}
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
17
2
Agrupación de clases por funcionalidad: namespaces.
Tal como se ha mencionado antes .NET permite agrupar las clases mediante un
mecanismo de organización denominado namespaces, lo que se ha presentado
como espacio de nombres a lo largo de este material, el cual es completamente
independiente de la herencia y muy similar a los directorios que emplea el sistema
operativo. De hecho, cuando se define un espacio de nombres, .NET pide al
sistema operativo que cree una carpeta para él, y todo lo que se crea en él
quedará ubicado, físicamente, en esa carpeta. Así que los espacios de nombres
no son más que estructuras que permiten organizar las clases de acuerdo a
criterios definidos por quien programa, aunque generalmente se utilizan para
agrupar por funcionalidad.
Tal como se dijo al inicio de este punto, los espacios de nombres son una
estructura de .NET, así que en un mismo espacio de nombres pueden ubicarse
clases elaboradas desde C#.NET, C++.NET, ADO.NET o cualquiera de las otras
herramientas de programación que proporciona el ambiente.
Estas estructuras son completamente independientes unas de otras, y si al definir
una clase se requiere emplear clases ubicadas en espacios de nombres
diferentes, tiene que emplearse la palabra reservada using seguida del nombre de
cada uno de los espacios de nombres en que se encuentran ubicadas dichas
clases. De hecho hasta ahora se ha utilizado esta cláusula asociada a System, y
a UtilidadesVarias, que son los espacios de nombres que contienen las clases
Console y Lecturas que se han empleado para hacer operaciones a través de la
consola.
Además, y referente a lo mencionado al final del párrafo anterior, debe hacerse
claridad respecto al hecho de que el namespace System, es fundamental en este
ambiente, y de hecho nada podría hacerse sin él, pues es el que contiene la
definición de los struct que hemos empleado como tipos básicos (int, double, bool,
char, etc.), la definición de las clases Array y string, entre muchas otros tipos
básicos para el trabajo en este entorno.
Ejercicio Resuelto:
Ilustre, mediante un gráfico, cómo quedaría el espacio de nombres
figurasgeométricas, que contiene las clases que se presentaron en el ejemplo que
se empleó durante la explicación referente a las clases.
Respuesta:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
18
System
...
Triángulo
Cuadrado
TriánguloRectángulo
...
Círculo
FigurasGeométricas
Y el nombre completo de cada una de las clases contenidas en este espacio de
nombres sería:
•
•
•
•
•
FigurasGeométricas.FigurasGeométricas
FigurasGeométricas.Triángulo
FigurasGeométricas.Cuadrado
FigurasGeométricas.TriánguloRectángulo
FigurasGeométricas.Círculo.
Nótese que la última parte de la respuesta no se había presentado hasta este
momento, y es que el nombre completo de cada una de los tipos que admite .NET
incluye el nombre del espacio de nombres que la contiene, siendo el nombre
completo:
NombreDelNamespace.NombreDelTipo
razón por la cual es posible la creación de varios tipos con igual nombre, siempre
y cuando estén en espacios de nombres diferentes.
2.1
Espacios de nombres anidados.
Tal como se ha mencionado, los espacios de nombres se emplean con mucha
frecuencia para agrupar clases que tienen una funcionalidad similar, pero bien
puede ocurrir que en el proceso de especialización empiecen a surgir clases que
se refieran a algo mucho más específico, de esta forma bien podría requerirse
crear un nuevo espacio de nombres que esté contenido dentro del espacio
original. De hecho .NET permite tantos anidamientos en los espacios de nombres
como se quiera tener, sin ningún inconveniente en ello. Pero debe tenerse
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
19
presente que los espacios de nombre, sin importar que estén anidados o no, son
completamente independientes unos de otros.
Ejercicio Resuelto:
Ilustre, mediante un gráfico, cómo quedaría el espacio de nombres
FigurasGeométricas, si se le agrega un espacio de nombres que contenga todo lo
relacionado con lo triángulos.
Respuesta:
FigurasGeométricas
Cuadrado
A
FigurasGeométricas
Círculo
TriánguloRectángulo
Triángulo
TriánguloEquilátero
TriánguloIsósceles
Triángulos
Y el nombre completo de cada una de las clases contenidas en este espacio de
nombres sería:
•
•
•
•
•
•
•
FigurasGeométricas.FigurasGeométricas
FigurasGeométricas.Cuadrado
FigurasGeométricas.Círculo.
FigurasGeométricas.Triángulos.Triángulo
FigurasGeométricas.Triángulos.TriánguloRectángulo
FigurasGeométricas.Triángulos.TriánguloEquilátero
FigurasGeométricas.Triángulos.TriánguloIsósceles
De manera que si alguna persona necesita emplear la clase Cuadrado y la
clase Triángulo en la clase que va a construir, el encabezado de esta nueva
clase debería incluir:
using FigurasGeométricas;
using FigurasGeométricas.Triángulos;
Aquí se considera importante hacer énfasis en que los espacios de nombres son
completamente independientes entre sí, incluso si un espacio de nombres
contiene a otro. Por esta razón, en el ejemplo anterior, no es suficiente con
emplear el using con FigurasGeométricas, para que estén disponibles las clases
contenidas en el espacio de nombres Triángulos.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
20
2.2
El using.
Tal como se ha mencionado hasta el momento el using se emplea para indicar
que en la clase que se está construyendo se emplearán clases contenidas en un
espacio de nombres determinado. Ahora se quieren hacer algunas precisiones al
respecto:
• Tal como se ha dicho, los espacios de nombres son independientes unos
de otros, de manera que si se requiere hacer referencias a clases ubicadas
en diversos espacios de nombres, debe haber tantos using como espacios
de nombres se deseen usar.
• El using concede acceso a todo lo contenido en el espacio de nombres que
se asocie a él, es decir si se requiere emplear una clase y un struc
contenidos en un espacio de nombres determinado basta con dar el using
con este nombre asociado y estos estarán disponibles.
• El using no es indispensable, puesto que se puede omitir y cada vez que se
requiera hacer referencia a una clase ubicada en otro espacio de nombres,
se puede dar su nombre completo, es decir el nombre del espacio de
nombres seguido del punto y el nombre de la clase. Dado que esto último
puede resultar realmente incómodo se recomienda no hacerlo y emplear el
using.
2.3
El acceso físico.
Tal como se mencionó al empezar a tratar este tema, cada espacio de nombres
está asociado a una carpeta manejada por el sistema operativo. Cuando se
emplea el using se está solucionando un problema de tiempo de compilación; pero
cuando se está en tiempo de ejecución esta información no es suficiente, pues
.NET no sabe en qué ubicación física se encuentran las carpetas que se
referencian. Por lo anterior cada proyecto tiene un componente que se denomina
References, en el cual el proyecto contiene las direcciones físicas de aquellos
componentes a los cuales hace referencia.
Tal como se muestra en la siguiente gráfica:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
21
Como puede verse en esta imagen, las referencias pertenecen a cada proyecto, y
dicen a qué espacios de nombres puede hacer referencia él. En este caso el
proyecto “ConsoleApplication2” no sabría como hacer referencia al proyecto
“Utilidades Varias”, para solucionar esto se debe indicar que se desea agregar una
nueva referencia, haciendo “clic” derecho sobre este ítem, ante lo cual se
presentará la siguiente ventana:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
22
En ella debe seleccionar el proyecto, y asegurarse de que aparezca en la ventana
inferior. Una vez hecho esto debe seleccionarse el botón Aceptar, después de lo
cual el componente “References” debe mostrar una referencia a
“UtilidadesVarias”, tal como se muestra en la siguiente imagen.
Otra revisión que debe hacerse es verificar que la solución tiene configurado como
proyecto de inicio, aquel proyecto que contiene el método Main(), lo cual se
verifica a través de las propiedades de la solución.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
23
3
Manejo de errores en .NET.
Dado que .NET es un ambiente completamente orientado a objetos, el manejo de
situaciones de error, o situaciones no deseadas, se hace mediante objetos.
Aunque realmente lo que se hace es lanzar un objeto, perteneciente a la clase
Exception o a una de sus hijas, el cual contiene información referente al tipo de
situación que se presentó.
Este objeto tiene que ser “atrapado” dentro del
método en que fue lanzado y las operaciones de corrección o finalización que
deban llevarse, o sea que los errores no pueden propagarse por diversos
métodos. Esto puede parecer confuso, pero se discutirá en detalle a lo largo de
este numeral.
3.1
Lanzar una excepción.
Cuando ocurre una situación excepcional y se decide lanzar una excepción, basta
con indicar a .NET que se quiere lanzar (throw) un objeto perteneciente a una
clase especial (Exception), el cual llevará información referente al tipo de error
ocurrido. En este punto es muy importante hacer énfasis en que el objeto a lanzar
tiene que pertenecer a la clase System.Exception o a alguna clase que descienda
de ella, pues en caso contrario no se admitirá que se asocie a la palabra
reservada throw, tal como se ilustra en la siguiente gráfica:
Aquí es importante saber qué propiedades y métodos se puede emplear en asocio
a los objetos pertenecientes a la clase Exception:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
24
Propiedades:
•
•
•
HelpLink: Retorna una cadena que contiene una dirección (URL), del lugar
en el cual se encuentra más información referente a la excepción, o un
archivo que contiene mayor información (URN).
InnerException: Devuelve el objeto Exception, que causó está Exception. Es
importante resaltar que si se generó una cadena de excepciones esta
propiedad retorna la última, pero si sólo se ha generado una excepción esta
propiedad retornará null, por tanto deberá recurrirse al método
GetBaseException().
Message: Retorna una cadena que contiene una explicación sobre el error
ocurrido, esta cadena debe ser pasada al constructor como parámetro.
Generalmente se construyen las excepciones, justo al momento de
lanzarlas, pero esto no es necesario puesto que puede lanzarse una
excepción previamente construida, de manera que el siguiente código es
válido:
public static void unGato(int dividendo, int divisor) {
try {
if (divisor == 0) {
Exception miExcepción = new ArithmeticException("divisor no válido");
throw miExcepción; }
}
}
•
•
•
•
Source: Retorna una cadena con el nombre del objeto o la aplicación que
causó el error.
StackTrace: Muestra el estado de la pila al momento de generarse la
excepción. Es importante tener presente que esta pila sólo contiene los
nombres de los métodos que se han ejecutado hasta ese momento.
TargetSite: Retorna un objeto System.Reflection.MethodBase, el cual
contiene una referencia al método desde el cual se lanzó la excepción.
Hresult: Esta es una propiedad protegida, a diferencia de las anteriores que
son públicas, y retorna un valor de entero conformado por tres dígitos, el
primero indica la severidad del problema, es decir si es una advertencia o
un error, el segundo indica el código del dispositivo con el cual se presentó
el problema y el último corresponde al código del error.
Métodos:
•
GetBaseException(): Retorna una referencia a la excepción principal, es decir
a la primera excepción generada, y causante de la cadena de excepciones
que generó la excepción a la cual se le pide este servicio.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
25
3.2
Los bloques Try, Catch y Finally.
Dado que el manejo de excepciones rompe el ciclo de ejecución del código, que
el mecanismo ha sido ideado para manejar situaciones excepcionales y para el
control de errores que no pudieron ser previstos por el programador, se han
ideado tres bloques de control los cuales deben estudiarse, y cuya finalidad debe
tenerse presente, cuando se trata de controlar las excepciones correctamente.
Estos bloques son el bloque try, el boque catch y el bloque finally.
El primero de estos bloque se denomina bloque try, ya que es el bloque en el cual
va la sentencia throw asociada a la excepción que se lanzará. Ninguna excepción
puede ser lanzada por fuera de un bloque de este tipo, es decir este bloque es
obligatorio cuando se están manejando excepciones. Este tipo de bloque puede
ser definido dentro de cualquiera de los tipos de método que se manejan en este
ambiente, es decir constructores, método Main o métodos definidos por el usuario.
La forma en la cual se define este método es muy sencilla, pues basta asociar la
palabra reservada try, al bloque de código en el cual podrá presentarse la
excepción, tal como se ha ilustrado en las imágenes que se han presentado con
este tema.
El segundo bloque se denomina bloque catch, y tiene como finalidad el indicar qué
se debe hacer cuando ocurre una excepción. Este bloque, que en realidad
pueden ser varios bloques catch, se puede manejar de dos formas, la primera
consiste en indicar en el encabezado del mismo el tipo de excepción que se desea
atrapar en este bloque y la segunda en obviando el tipo de excepción que se
desea atrapar, en cuyo caso ese bloque atrapará todas las excepciones que se
lancen. Este bloque, o bloques, de código no es requerido, pero si se emplea
tiene que ir inmediatamente después del bloque try. Estas situaciones se ilustran
en el siguiente porción de código:
try {
if (divisor == 0) {
Exception miExcepción = new ArithmeticException("divisor no válido");
throw miExcepción;
}
if (dividendo / divisor > 100) {
throw new Exception();
}
return dividendo/divisor;
}
catch (ArithmeticException e)
{// este bloque atrapará una excepción lanzada por división por cero.
Console.WriteLine(e.Message);
}
catch
{// este bloque atrapará cualquier otro tipo de excepción.
Console.WriteLine("El resultado está por fuera del rango");
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
26
}
Aquí es importante dejar presente que el orden de los dos bloques catch no
habría podido invertirse pues el último catch atrapa todas las excepciones, así
que el bloque que atraparía la excepción aritmética se consideraría código no
alcanzable, tal como se ilustra en la siguiente imagen:
O sea que no pueden ir bloques catch generales antes de bloques catch más
específicos, pues se tratará de código no alcanzable y ocasionará error en tiempo
de compilación.
El último bloque es el bloque finally, y tiene como finalidad el permitir que haya
código de ejecución obligatoria; es decir una cierta porción de código que tiene
que ejecutarse ocurra la excepción o no. La razón por la cual existe este tipo de
bloque es que las instrucciones relacionadas con retornar recursos que el sistema
ha asignado a un proceso determinado, no pueden dejar de ejecutarse porque una
determinada instrucción no se concluyó. Este bloque no es obligatorio, pero si se
emplea debe ir después del try, y del catch si lo hubo.
Aquí se debe hacer claridad sobre la obligatoriedad de estos bloques, siempre que
se requiera lanzar excepciones, esto tiene que hacerse dentro de un bloque try, si
se desea controlar o hacer algo para su manejo, esto debe hacerse en un bloque
catch, y si se requiere liberar recursos, lo que se requiera para ello deberá
incluirse en un bloque finally; ninguno de estos dos últimos bloques es obligatorio,
pero siempre que se crea un bloque try, es necesario incluir por lo menos uno de
los dos.
3.3
Consideraciones respecto al manejo de las excepciones.
Es importante dejar presente que el manejo de excepciones es una opción que se
presenta para situaciones excepcionales, es decir situaciones que no pudieron ser
previstas por quien elaboró el programa: una conexión a un dispositivo que no
está disponible, o una lectura sobre un archivo que está corrupto, etc. Pero como
este mecanismo puede interrumpir la ejecución normal del código, debe
manejarse con cuidado, es decir debe tratarse de evitar como mecanismo de
validación, y continuar tratando de hacer las validaciones de la manera
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
27
convencional. El ejemplo que ilustra el uso de varios bloques catch, en el cual se
previene una división por cero, perfectamente pudo hacerse con la instrucción if, y
no con excepciones, y puede considerarse un buen ejemplo de lo que no debe
hacerse con excepciones, sino a través de validaciones normales.
Otra consideración importante, en particular para aquellas personas que han
trabajado con el lenguaje Java, es que .NET exige que la excepción sea capturada
dentro del método en el cual se lanza, o sea que el mecanismo de propagación de
excepciones que maneja Java no existe en este ambiente (la palabra reservada
throws, que en Java es reservada, aquí no lo es).
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
28
4
Manejo de colecciones en .NET.
Una colección es una agrupación, cuyo tamaño final es desconocido al momento
de su creación, la cual contiene elementos que por alguna razón deben guardarse
juntos. Dependiendo del tipo de estos elementos, o del orden en el cual se
requiera que se guarden, o de algún otro elemento que deba ser tenido en cuenta
al momento de definir la forma de agrupación, las colecciones reciben diversos
nombres como listas, pilas, colas, árboles, conjuntos, grafos, etc.
Dentro de la filosofía de Orientación a Objetos, las colecciones son elementos
especiales en la medida en la que son un objeto que va a contener objetos. Al ser
un objeto deben contar con una estructura y estar en capacidad de prestar unos
servicios; al contener objetos deben ser capaces de contener cualquier tipo de
objeto dentro de los existentes en el contexto en el cual se está trabajando.
Dentro de .NET hay clases que implementan algunas de las colecciones más
conocidas, pero también se cuenta con los elementos necesarios para la creación
de nuevas colecciones. Estos elementos se encuentran agrupados en un
namespace denominado System.Collections.
Generalmente las colecciones se implementan partiendo de un arreglo de objetos
de la clase object, dado que al ser object la clase padre de todas en .NET, se
garantiza que la colección podrá contener objetos de cualquier clase. Para
resolver el que puedan aumentar su tamaño sin restricciones, lo que suele
hacerse es que el método que adiciona elementos verifica si el arreglo ya está
lleno, en cuyo caso crea un arreglo más grande y traslada los elementos del “viejo
arreglo” al “nuevo arreglo” sin que el usuario sufra ningún tipo de pérdida de
información, además sí la colección es apuntada por varias referencias este
método debe asegurarse de que todas se actualicen.
4.1
La interfaz System.Collections.ICollection.
Además de lo mencionado en el párrafo anterior, las colecciones en .NET también
deben implementar la interfaz System.Collections.ICollection, la cual hereda de la
interfaz System.Collections.IEnumerable, y cuenta con los métodos y propiedades
mínimas que debe tener una estructura para ser considerada una colección en
este ambiente. Dichos métodos se presentan a continuación:
• System.Collections.IEnumerable.GetEnumerator: Este método pertenece a la interfaz
IEnumerable, pero al ser esta la interfaz padre de ICollection también debe
ser definido por las clases que implementen esta última. Este método está
definido para retornar un objeto perteneciente a una de las clases que
implementan la interfaz System.Collections.IEnumerator. Retorna un objeto
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
29
capaz de recorrer la colección de principio a fin, avanzando de uno en uno y
estando es capacidad de referenciar al objeto que se encuentra enfrente. El
comportamiento, lógico, del enumerador se ilustra en la siguiente gráfica:
laColección
Posición del enumerador al momento de pedirlo.
laColección
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
30
laColección
Este enumerador es particularmente valioso en este ambiente pues es el que
permite la utilización de la instrucción foreach, la cual es muy útil al momento
de recorrer estructuras de datos.
• int System.Collections.ICollection.Count: Esta propiedad, al igual que los elementos
que se mencionan a continuación, pertenecen directamente a la interfaz
ICollection. De los dos métodos permitidos para las propiedades, get y set,
esta propiedad sólo trabaja el método get, y al implementarlo debe retornar la
cantidad de elementos contenidos por la colección.
• bool System.Collections.ICollection.IsSynchronized: Es una propiedad, e igual que la
anterior sólo requiere la implementación del método get. Retorna un valor
booleano, el cual indica si el acceso a la colección está sincronizado o no.
• object System.Collections.ICollection.SyncRoot: También es una propiedad que sólo
requiere la implementación del método get, debe retornar un objeto capaz de
manejar la sincronización del acceso a la colección.
• void System.Collections.ICollection.CopyTo( System.Array, int ): A diferencia de los
elementos mencionados antes, este es un método que debe guardar nuevas
referencias a los elementos de la colección, en el arreglo que recibe como
primer parámetro y a partir de la posición dada por el índice que recibe como
segundo parámetro.
4.2
La interfaz System.Collections.IComparer.
Esta interfaz debe ser implementada por las colecciones que requieren
conservarse ordenadas. Sólo contiene un método:
• int System.Collections.IComparer.Compare( object, object ): Retorna un valor entero
que debe ser cero ( 0 ) si los objetos a comparar son iguales, un valor
negativo si el primero es menor que el segundo y un valor positivo si el
primero es mayor que el segundo. Esta es una convención, es decir no hay
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
31
nada que fuerce este comportamiento, sin embargo cuando alguien invoque
este método, esperará que este sea el comportamiento a obtener.
4.3
La interfaz System.Collections.IEnumerator.
Las clases que implementen esta interfaz se comprometen a proporcionar los
elementos necesarios para contar con un enumerador, es decir un objeto capaz de
recorrer una colección de principio a fin, desplazándose elemento por elemento.
Para ellos cuenta con los siguientes métodos y propiedades:
• bool System.Collections.IEnumerator.MoveNext: Proporciona el método que permite,
al enumerador, desplazarse al siguiente elemento de la colección. Retorna
true, si el desplazamiento puedo llevarse a cabo y false si el enumerado ya
se encuentra al final de la colección.
• void System.Collections.IEnumerator.Reset: Lleva al enumerador a su posición
inicial, es decir “mirando” al primer elemento de la colección.
• object System.Collections.IEnumerator.Current: Esta propiedad sólo se compromete
con la implementación del método get, y retorna una referencia al elemento
que actualmente está siendo señalado por el enumerador.
4.4
La clase System.Collections.ArrayList.
Dentro del espacio destinado a las colecciones no sólo existen interfaces, también
hay algunas clases, aunque esto es relativamente pobre. Dentro de las clases
existentes se encuentra la clase ArrayList.
La clase ArrayList se comporta como un arreglo que está en capacidad de crecer,
de manera completamente transparente para quien lo usa, una vez se llena. Esto
tiene ventajas muy grandes cuando el número de elementos a contener es grande
pero no está determinado por una cantidad exacta.
Para mostrar la estructura del esta clase se empleará una herramienta
, la cual es un visor del API y se encuentra ubicada en
denominada
Microsoft Visual Studio .NET\FrameworkSDK\Bin. Este visor emplea las siguientes
convenciones para presentar los elementos del API:
Símbolo
Significado
Indica que hay más información disponible sobre el ítem. En
algunos casos basta hacer doble clic sobre el ítem para verla.
Se asocia a los namespace
Se asocia a las clases
Se asocia a los struct
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
32
Se asocia a las interfaces
Se asocia a los campos
Se asocia a los campos static
Se asocia a los métodos
Se asocia a los métodos static
Se asocia a las propiedades
La información que ildasm nos proporciona sobre la clase ArrayList es la que se
presenta a continuación:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
33
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
34
De los elementos mostrados se presentarán, en este material, sólo algunos que se
consideran relevantes al tema que se viene tratando.
• Interfaces que implementa:
System.Collections.IList: Interfaz que hereda de System.Collections.ICollection
y System.Collections.IEnumerable. Por lo tanto la clase ArrayList deberá
implementar, no sólo los métodos de la interfaz IList, sino que también
deberá implementar los métodos de estas dos interfaces.
¾ System.ICloneable: Esta interfaz establece el compromiso de implementar el
método necesario para crear un clon1 del objeto sobre el cual se invoca
dicho método.
¾
• Atributos con que cuenta:
¾
_defaultCapacity: Es un atributo static, y contiene el tamaño que se dará a la
¾
colección si al momento de su creación no se indica otro tamaño.
_items: Es el arreglo, como se puede ver en la gráfica anterior se define para
contener objetos de la clase object.
1
En este ambiente se considera clon a un nuevo objeto que contiene exactamente lo mismo que el objeto
original. Sí el objeto a clonar contiene dentro de sus atributos referencias a otros objetos, el método que crea
el clon debe decidir si el nuevo objeto contendrá referencias a estos mismos objetos, o sí estos objetos
también se deben clonar.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
35
• Métodos y propiedades que implementa:
¾
int System.Collections.ArrayList.Add( object ): adiciona el objeto que recibe como
parámetro al final de la lista, retorna el valor de índice al momento de la
adición.
¾ void System.Collections.ArrayList.AddRange( ICollection ): agrega la colección que
recibe como parámetro, al final de la lista.
¾
¾
¾
int System.Collections.ArrayList.BinarySearch( object )
int System.Collections.ArrayList.BinarySearch( object, IComparer )
int System.Collections.ArrayList.BinarySearch( int, int, object, IComparer ): realiza una
búsqueda binaria del objeto que recibe como parámetro, puede recibir un
objeto comparador, el cual se empleará para realizar la comparación, y
puede realizar la búsqueda sobre una porción del arreglo, definida por los
índices de la posición en la cual debe empezar la búsqueda y la posición en
la cual debe terminar. Retorna la posición en la cual se encuentra el objeto,
en caso de no encontrarlo retorna un valor negativo.
¾ void System.Collections.ArrayList.Clear: limpia la lista.
¾ bool System.Collections.ArrayList.Contains( object ): retorna un valor booleano que
indica si el objeto pasado como parámetro se encuentra en la lista o no.
¾
¾
¾
System.Collections.ArrayList.IndexOf( object )
System.Collections.ArrayList.IndexOf( object, int )
System.Collections.ArrayList.IndexOf( object, int, int ): retorna la posición en la cual
encuentra por primera vez, pues esta colección puede contener duplicados,
el objeto pasado como parámetro. La búsqueda se puede hacer sobre toda
la colección, o sobre una parte de ella, lo cual se indica dando un valor
entero si se requiere que busque a partir de él y hasta el final o dos valores
enteros para dar tanto la posición de inicio como la posición de finalización
de la búsqueda. Si el objeto no es encontrado se retorna -1.
¾
¾
void System.Collections.ArrayList.Insert( int, object )
void System.Collections.ArrayList.InsertRange( int, ICollection ):
¾
¾
¾
int System.Collections.ArrayList.LastIndexOf( object )
int System.Collections.ArrayList.LastIndexOf( object, int )
int System.Collections.ArrayList.LastIndexOf( object, int, int ): retorna la posición en la
estos métodos
permiten la adición de un objeto, o una colección de ellos, en una posición
determinada de la colección. Recuérdese que si se emplea el método Add,
la adición se hace siempre al final de la colección.
cual encuentra por última vez el objeto pasado como parámetro. La
búsqueda se puede hacer sobre toda la colección, o sobre una parte de
ella, lo cual se indica dando un valor entero si se requiere que busque a
partir de él y hasta el final o dos valores enteros para dar tanto la posición
de inicio como la posición de finalización de la búsqueda. Si el objeto no es
encontrado se retorna -1.
¾
¾
void System.Collections.ArrayList.Remove( object )
void System.Collections.ArrayList.RemoveAt( int )
void System.Collections.ArrayList.RemoveRange( int, int ): estos métodos permiten la
eliminación de un objeto o grupo de ellos, bien sea indicando el objeto, su
posición o el rango a eliminar.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
36
¾ static ArrayList System.Collections.ArrayList.Repeat( object, int ): este método guarda
en un objeto ArrayList una referencia al objeto pasado como primer
parámetro y lo repite tantas veces como lo indique el segundo parámetro.
¾ void System.Collections.ArrayList.Sort( )
¾ void System.Collections.ArrayList.Sort( IComparer )
¾ void System.Collections.ArrayList.Sort( int, int, IComparer ): estos métodos ordenan la
¾
¾
¾
¾
¾
¾
¾
4.5
ArrayList, se puede especificar un orden especial a través de un objeto
comparador, y también se puede ordenar sólo una parte de la lista.
int System.Collections.ArrayList.Capacity: esta propiedad permite obtener o
modificar la capacidad, es decir el número de elementos que está en
capacidad de contener en ese momento, del ArrayList.
int System.Collections.ArrayList.Count: esta propiedad permite obtener la cantidad
de elementos que contiene el ArrayList.
bool System.Collections.ArrayList.IsFixedSize: esta propiedad permite saber si el
tamaño del ArrayList se ha determinado como fijo.
bool System.Collections.ArrayList.IsReadOnly: esta propiedad permite saber si el
ArrayList se ha marcado como de sólo lectura.
bool System.Collections.ArrayList.IsSynchronized: esta propiedad permite saber si el
ArrayList se ha marcado como sincronizado.
object System.Collections.ArrayList.SyncRoot: esta propiedad permite obtener un
objeto capaz de sincronizar el ArrayList.
object System.Collections.ArrayList.this[int]: esta propiedad permite obtener o
modificar el objeto ubicado en la posición indicada, por el subíndice, del
ArrayList.
Algunas observaciones adicionales.
Dado que este es un tema importante y sobre el cual se suele trabajar mucho, no
es poco probable que en muy poco tiempo las personas que proponen la
estructura central de .NET hayan incorporado nuevas colecciones al namespace
System.Collections, por ello es recomendable que antes de tomar una decisión
respecto a utilizar alguna de las conocidas se de una mirada al contenido de este
namespace.
A continuación se presentan las clases e interfaces contenidas en el Framework 1.0
versión 1.0.3705 de Microsoft .NET. Puede ser un ejercicio interesante el revisar los
servicios y la filosofía que tienen clases como System.Collections.Queue,
System.Collections.SortedList y System.Collections.Stack.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
37
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
38
5
Flujos y Serialización en .NET
Cuando se construyen aplicaciones es muy frecuente que se necesite leer o
escribir datos que se requiere persistan una vez la aplicación termine su ejecución;
es decir es frecuente que se busque un mecanismo que permita que la
información asociada a una aplicación pueda seguir existiendo aún si la aplicación
no está en ejecución. Para ello, habitualmente, se emplean archivos que son
almacenados en un disco duro; y esto puede hacerse de diversas formas ya que la
información puede ser escrita tal y como se encuentra en memoria, o puede
requerirse que la información tenga un formato especial. Para responder a esta
necesidad .NET trabaja los conceptos de Flujos y Serialización, que se empezarán
a discutir en esta parte del material, y cuyos elementos se encuentran en los
namespace System.IO y System.Runtime.Serialization.
Dentro de esto se tratarán diversos aspectos que cubren temas como identificar el
tipo de flujo que se debe establecer, que clases provee .NET para esto, qué es
serializar objetos y qué mecanismos de serialización provee .NET.
5.1
La definición de flujo.
Se denomina flujo al canal que se establece entre dos “dispositivos” que necesitan
comunicarse; para cada flujo se deben definir varias cosas como son dispositivos
a comunicar, la dirección en la cual fluirá la información y el formato de en el cual
viajará la información entre ellos. Por ejemplo, se requiere establecer una
conexión de lectura entre una aplicación y un archivo de texto existente en el disco
duro:
• dispositivos a conectar: la aplicación y el disco duro.
• dirección: como la aplicación va a leer del archivo la dirección será del
archivo a la aplicación, es decir la aplicación verá el flujo como una entrada.
• por último se debe indicar el formato en el cual está la información, cadenas
de caracteres, en este caso.
Los dispositivos a conectar se pueden agrupar en dispositivo físicos, tales como
un archivo en el disco duro, un puerto de red, el teclado o el monitor; y en
dispositivos lógicos como, citando el caso más común, los flujos que se acaban de
mencionar, es decir los que establecen conexiones con los dispositivos físicos.
La dirección se determina a partir del código en el cual se está definiendo el flujo,
si se requiere recibir información a través del flujo, este se considerará un flujo de
entrada, y por tanto su nombre contendrá “in”, “input” o “read”; pero si se trata de
enviar información a través de él, se considerará como un flujo de salida y su
nombre contendra “out”, “output” o “write”.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
39
Respecto al formato en el cual viajará la información a través del flujo, se
consideran tres grandes formatos:
• bytes,
• texto, se considera formato texto el que viaje como caracter o como
cadena, y
• objetos, lo cual se conoce como el proceso de serialización.
5.2
Clases para el manejo de flujos en .NET
Las clases, y enumeraciones, que ofrece .NET para el manejo de los flujos se
encuentran agrupadas en un espacio de nombre denominado System.IO. En este
material no se presentarán todos estos elementos, sólo los necesarios para ilustrar
el manejo de los flujos.
5.2.1 FileMode.
Se trata de una enumeración que contiene los diversos modos con que se puede
pedir al Sistema Operativo que abra un archivo:
• Append: debe abrir el archivo y ubicarse al final de él. El archivo se abre en
modo escritura y lanzará una excepción si se intenta leer.
• Create: debe crear un archivo, sobreescribiéndolo si ya existe.
• CreateNew: debe crear un archivo, lanzará una excepción si ya existe.
• Open: debe abrirse un archivo existente, lanzará una excepción si no existe.
• OpenOrCreate: debe abrirse un archivo existente, si no existe debe crearse.
• Truncate: debe abrir un archivo existente e ignorar y sobreescribir su contenido.
5.2.2 FileAccess.
Se trata de una enumeración que contiene los diversos modos con que se puede
establecer el acceso a un archivo:
• Read.
• ReadWrite. Este es el valor que se asume por omisión.
• Write.
5.2.3 System.IO.FileShare
Se trata de una enumeración que indica si el archivo puede ser accesado,
simultáneamente, por varios flujos.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
40
• Inheritable: sólo los procesos hijos del proceso que establece la conexión podrán
tener acceso al archivo.
• None: no se admite que ningún otro proceso establezca conexión con el archivo.
Este valor se asume por omisión.
• Read: sólo permite que se establezcan conexiones adicionales que indiquen
harán operaciones de lectura.
• ReadWrite: permite que se establezcan otras conexiones con fines de lectura o
escritura.
• Write: sólo permite que se establezcan conexiones adicionales que indiquen
harán operaciones de escritura.
5.2.4 File.
Esta clase fue diseñada para ofrecer servicios a objetos de otras clases que deban
manipular archivos, de hecho su constructor es privado y todos los métodos que
ofrece han sido definidos como static. A continuación se presentarán algunos de
los métodos que ofrece:
• System.IO.FileStream Open(string,FileMode,FileAccess,FileShared )
System.IO.FileStream Open(string,FileMode,FileAccess )
System.IO.FileStream Open(string,FileMode )
Retorna un objeto FileStream, asociado al archivo especificado en el primer
parámetro, respetando los parámetros suministrados.
• void Copy(string,string,bool )
void Copy(string,string)
Copia el archivo fuente, primer parámetro, en el archivo destino, segundo
parámetro, respetando el permiso de sobreescritura si este último ya existe. Si
se omite este último permiso se asume que está denegado.
• System.IO.StreamWriter AppendText(string )
Retorna un objeto StreamWriter, asociado al archivo especificado en el
parámetro, en modo adición de texto al final del mismo.
• void Delete(string)
Elimina el archivo indicado en el parámetro, si este no existe no lanza
excepción.
Esta clase ofrece algunos servicios más, que pueden ser revisados con cualquiera
de las herramientas que se han presentado para explorar el API de .NET.
5.2.5 StreamReader.
Esta clase permite establecer una conexión para lectura con un dispositivo, y lo
lee como una secuencia de bytes que representan caracteres en una codificación
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
41
determinada. Para trabajar con esta clase, deben crearse objetos pertenecientes a
ella pues, a diferencia de la clase anterior, sus métodos no son static.
• StreamReader (string, System.Text.Encoding, bool, int )
StreamReader (string, System.Text.Encoding, bool)
StreamReader (string, System.Text.Encoding)
StreamReader (string ,bool)
StreamReader (string)
StreamReader (System.IO.Stream, System.Text.Encoding, bool, int)
StreamReader (System.IO.Stream, System.Text.Encoding, bool)
StreamReader (System.IO.Stream, System.Text.Encoding)
StreamReader (System.IO.Stream, bool)
StreamReader (System.IO.Stream)
Para crear un objeto perteneciente a esta clase existen, como se puede ver en
la lista anterior, varias opciones. El primer grupo de ellas, recibe una cadena de
caracteres que contiene la ruta y el nombre del archivo, el segundo grupo recibe
un objeto Stream, el cual contiene la dirección de la cual se leerá la secuencia
de caracteres. Además de esto puede recibir algunos parámetros adicionales
como son, un objeto de la clase Encoding, o alguna de sus clases hijas, el cual
contendrá información sobre le tipo de codificación que se empleó para escribir,
y por tanto para leer, el archivo; una variable boleana que indica si debe
buscarse algún tipo de señal especial al inicio del archivo; y una variable entera
que especifica el tamaño mínimo del buffer a emplear durante la lectura.
• string ReadLine ( )
Retorna la línea del archivo apuntada por el cursor, avanza el cursor una línea.
Al terminarse el archivo, retorna null.
• string ReadToEnd ( )
Retorna, en una línea, el contenido del archivo desde donde se encuentra el
apuntador hasta el final del mismo. Si no hay nada que leer, retorna una cadena
vacía.
• void Close ( )
Cierra la conexión del flujo.
Ejemplo:
Se ha escrito el siguiente código que lee un texto desde un archivo cuyo nombre y
ubicación se piden al usuario, una vez leido se muestra por pantalla.
Código:
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace AplicaciónConFlujos {
class ClaseUno {
[STAThread]
static void Main(string[] args) {
ArrayList unArreglo = new ArrayList();
string elPath = Console.ReadLine();
StreamReader unArchivo = new StreamReader(elPath);
string unaCadena = unArchivo.ReadLine();
while (unaCadena != null){
unArreglo.Add(unaCadena);
unaCadena = unArchivo.ReadLine();
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
42
unArchivo.Close();
foreach (object unObjeto in unArreglo){
unaCadena = (string) unObjeto;
Console.WriteLine(unaCadena);
}
Console.Read();
}
}
}
Al dar como cadena “LosCamello.txt”, busca en el directorio de trabajo actual, y
muestra por pantalla:
(realmente aparece la poesía completa…)
Otra, interesante, opción para el código:
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace AplicaciónConFlujos {
public class ClaseDos {
public static void Main(string[] args) {
string elPath = Console.ReadLine();
StreamReader unArchivo = new StreamReader(elPath);
string unaCadena = unArchivo.ReadToEnd();
unArchivo.Close();
Console.WriteLine(unaCadena);
Console.Read();
}
}
}
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
43
Al dar la misma cadena, se obtiene:
(realmente aparece la poesía completa…)
Es decir con ambos códigos, ¡ el resultado es exactamente el mismo !
5.2.6 BinaryReader.
Esta clase permite establecer una conexión para lectura con un archivo existente
en el disco duro, y establece que los datos a leer serán bytes. Esta clase también
requiere que se creen objetos pertenecientes a ella, y tampoco ofrece métodos
static.
• BinaryReader (Stream, System.Text.Encoding )
BinaryReader (Stream)
Para crear un objeto perteneciente a esta clase existen dos opciones: ambas
requieren un objeto Stream, el cual establece la conexión con el archivo, y una
de ellas puede especificar el tipo de codificación que debe ser empleado para la
lectura. Los objetos de la clase Stream, no pueden ser creados directamente,
pues esta clase no ofrece un constructor público, así que puede emplearse el
método OpenRead(string), de la clase File, el cual retorna un objeto Stream
asociado al archivo cuyo nombre completo es pasado como parámetro.
• int ReadInt32()
char ReadChar()
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
44
double ReadDouble()
byte ReadByte()
string ReadString()
bool ReadBoolean()
Leen los bytes correspondientes a cada tipo de datos, dependiendo de la
codificación establecida. Esto implica que al leer el archivo, se conoce la
estructura que se empleó para escribir los datos y por tanto se leen en el orden
correcto.
• bytes[] ReadBytes(int)
char[] ReadChar(int)
Leen tantos bytes como se requieran para cubrir el número de datos indicados
en el parámetro. Guarda el resultado de la lectura en un arreglo de caracteres.
Ejemplo:
Asuma que se tiene un archivo denominado “valores.dat”, el cual contiene valores escritos
como bytes, y se quiere leerlos.
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace AplicaciónConFlujos {
class ClaseUno {
[STAThread]
static void Main(string[] args)
{
BinaryReader otroArchivo = new BinaryReader(File.OpenRead("E:/Mis documentos/Cursos/NET/valores.dat"));
int nuevoValorUno = otroArchivo.ReadInt32();
double nuevoValorDos = otroArchivo.ReadDouble();
char[] nuevoValorTres = otroArchivo.ReadChars(2);
string nuevoValorCuatro = otroArchivo.ReadString();
otroArchivo.Close();
Console.WriteLine(nuevoValorUno);
Console.WriteLine(nuevoValorDos);
Console.WriteLine(nuevoValorTres[0]+" "+nuevoValorTres[1]);
Console.WriteLine(nuevoValorCuatro);
Console.Read();
}
}
}
Al ejecutar este código se obtiene, por pantalla:
Pero al revisar el archivo “valores.dat”, con una herramienta como el Bloc de notas de
Windows, se encuentra:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
45
5.2.7 StreamWriter.
Esta clase permite establecer una conexión para escritura con un dispositivo, y
escribe una secuencia de bytes que representan los caracteres enviados, en una
codificación determinada. Esta clase también permite la creación de objetos, y de
hecho es la forma correcta de trabajar con ella, pues no ofrece métodos static.
• StreamWriter (string, System.Text.Encoding, bool, int )
StreamWriter (string, System.Text.Encoding, bool)
StreamWriter (string, System.Text.Encoding)
StreamWriter (string ,bool)
StreamWriter (string)
StreamWriter (System.IO.Stream, System.Text.Encoding, bool, int)
StreamWriter (System.IO.Stream, System.Text.Encoding, bool)
StreamWriter (System.IO.Stream, System.Text.Encoding)
StreamWriter (System.IO.Stream, bool)
StreamWriter (System.IO.Stream)
Para crear un objeto perteneciente a esta clase existen, tantos constructores
como para la clase StreamReader, y funcionan igual que en ella, pero
establecen la conexión de escritura.
• void Write(string )
Escribe la cadena que recibe como parámetro en el dispositivo al cual se está
conectado.
• void Flush( )
Obliga a que todos los buffer de escritura sean vaciado a sus depósitos finales.
• void Close()
Cierra la conexión con el dispositivo.
Ejemplo:
Asuma que se debe tomar los valores guardados en el archivo “valores.dat”, con el cual
se trabajó en un ejemplo anterior, y guardarlos en un archivo “valores.txt", cuidando que
queden en formato texto.
Código:
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace AplicaciónConFlujos {
class ClaseUno {
[STAThread]
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
46
static void Main(string[] args)
{
BinaryReader otroArchivo = new BinaryReader(File.OpenRead("E:/Mis documentos/Cursos/NET/valores.dat"));
int nuevoValorUno = otroArchivo.ReadInt32();
double nuevoValorDos = otroArchivo.ReadDouble();
char[] nuevoValorTres = otroArchivo.ReadChars(2);
string nuevoValorCuatro = otroArchivo.ReadString();
otroArchivo.Close();
StreamWriter enTexto = new StreamWriter("E:/Mis documentos/Cursos/NET/valores.txt");
enTexto.WriteLine(nuevoValorUno);
enTexto.WriteLine(nuevoValorDos);
enTexto.WriteLine(nuevoValorTres);
enTexto.WriteLine(nuevoValorCuatro);
enTexto.Flush();
enTexto.Close();
}
}
}
Ahora al mirar el archivo “valores.txt”, con el Bloc de notas, se encuentra:
5.2.8 BinaryWriter.
Esta clase permite establecer una conexión para escritura con un dispositivo, y
establece que los datos a escribir serán bytes. Esta clase también requiere que se
creen objetos pertenecientes a ella, y tampoco ofrece métodos static.
• BinaryWriter (Stream, System.Text.Encoding )
BinaryWriter (Stream)
Para crear un objeto perteneciente a esta clase existen también existen dos
opciones, y se comportan igual que con los BinaryReader.
• void Write(int)
void Wirte(char)
void Write (double)
void Write(byte)
void Write(string)
void Write (bool)
Escribe los bytes correspondientes a cada tipo de datos, dependiendo de la
codificación establecida.
• void Flush( )
Obliga a que todos los buffer de escritura sean vaciado a sus depósitos finales.
• void Close()
Cierra la conexión con el dispositivo.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
47
Ejemplo:
Asuma que se debe tomar el texto guardado en el archivo “LosCamello.txt”, con el cual
se trabajó en un ejemplo anterior, y guardarlo como bytes, en un archivo
“LosCamellos.dat”.
Código:
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace AplicaciónConFlujos {
class ClaseUno {
[STAThread]
static void Main(string[] args) {
string elPath = "E:/Mis documentos/Cursos/NET/LosCamellos.txt";
StreamReader unArchivo = new StreamReader(elPath);
String unaCadena = unArchivo.ReadToEnd();
unArchivo.Close();
BinaryWriter otroArchivo = new BinaryWriter(File.OpenWrite("E:/Mis documentos/Cursos/NET/LosCamellos.dat"));
otroArchivo.Write(unaCadena);
otroArchivo.Flush();
otroArchivo.Close();
}
}
}
En esta caso al mirar el archivo “LosCamellos.dat”, con el Bloc de notas, se encuentra:
Sin embargo si la escritura del texto en bytes se hace empleando un objeto
UnicodeEncoding, es decir:
using System;
using System.Collections;
using System.IO;
using System.Text;
namespace AplicaciónConFlujos {
class ClaseUno {
[STAThread]
static void Main(string[] args) {
string elPath = "E:/Mis documentos/Cursos/NET/LosCamellos.txt";
StreamReader unArchivo = new StreamReader(elPath);
String unaCadena = unArchivo.ReadToEnd();
unArchivo.Close();
BinaryWriter otroArchivo = new BinaryWriter(File.OpenWrite("E:/Mis
documentos/Cursos/NET/LosCamellos.dat"), new UnicodeEncoding());
otroArchivo.Write(unaCadena);
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
48
otroArchivo.Flush();
otroArchivo.Close();
}
}
}
Al mirar el archivo “LosCamellos.dat”, con el Bloc de notas, se encuentra:
5.3
El concepto de serialización
Con lo presentado en este capítulo, hasta el momento, se tienen herramientas que
permiten a un programador dar persistencia a los datos que está manejando en
dos formatos diferentes. Pero ¿cómo dar persistencia a los objetos con que se
trabaje?, ¿debe escribirse, uno a uno, cada uno de los atributos que conforman la
estructura del objeto, para garantizar que la información podrá ser recupearada al
reiniciar el trabajo?, ¿cómo saber a qué clase pertenecia el objeto?, ¿se debe
crear un nuevo objeto, de esa clase, y asignar a sus atributos los valores
recuperados?, si uno de los atributos es una referencia a otro objeto ¿qué hacer?,
si es una colección y contiene varias referencias a un mismo objeto ¿cuántas
veces se escribe el objeto?
Por fortuna existe el concepto de serialización que es, básicamente, la forma de
dar persistencia a objetos, permitiendo a .NET que encargarse del manejo de
todos los detalles pertinentes para escribir los objetos y para leerlos.
5.4
Conceptos básicos para el manejo de serialización en .NET
En .NET la serialización es un proceso bien sencillo; además es recursivo, es decir
si usted serializa un objeto que contiene referencias a otros estos serán
automáticamente serializados.
Las pautas para serializar objetos en .NET están claramente establecidas:
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
49
• cada clase debe indicar si permite que los objetos que se creen a partir de ella
sean serializados o no, este último valor es el que se asume por defecto.
• se debe establecer un flujo de comunicación con el archivo en el cual se
guardarán los objetos serializados.
• se debe seleccionar el formato de serialización que desea emplearse.
• se escriben los objetos a serializar.
• se cierra el flujo.
Cuando una clase desea indicar que permite que los objetos creados a partir de
ella puedan ser serializados basta con que, antes de su nombre, maneje el
atributo Serializable entre corchetes, tal como se ilustra a continuación:
[Serializable]
public class Estudiantes {
Pero puede tenerse clases cuyos objetos no requiere serializar todos sus
atributos, en cuyo caso basta emplear el atributo NonSerialized, también entre
corchetes, justo antes de la definición del atributo, así:
[Serializable]
public class Estudiantes {
string nombres;
string apellidos;
string código;
int edad;
[NonSerialized]
double horaLlegadaHoy;
string[] cursosQueToma;
En el caso presentado la clase Estudiantes, indica que los objetos creados a partir
de ella serán seriliazados, pero que el atributo horaLlegadaHoy no debe ser
sometido a este proceso.
Una vez hecho lo anterior, debe indicarse qué tipo de flujo desea establecerse con
el archivo en el cual se guardarán los objetos serializados, para lo cual hay una
restricción y es que los métodos Serialize que ofrece .NET sólo reciben objetos de
la clase Stream, así que el flujo a establecer debe ser de este tipo.
Stream otroArchivo = File.OpenWrite("E:/Mis documentos/Cursos/NET/objetos.dat");
Ahora debe seleccionarse el formato de serialización a emplear, respecto a esto
existen tres opciones, la primera es serializar a formato binario, que es la opción a
emplear si se quiere dar persistencia, es decir si la serialización se hace para que
los objetos sean guardados en un archivo en el disco duro; la segunda opción es
serializar a formato SOAP, y es la opción que debe emplearse si la serialización se
hace para transmitir por la red la información concerniente a los objetos; por último
.NET permite crear formatos de serialización especiales, que pueden responder a
necesidades específicas de un programador.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
50
Dado que por ahora se trabaja la serialización como un mecanismo para dar
persistencia a los objetos se especificará como formato el binario, para lo cual
debe incluirse el espacio de nombre apropiado:
using System.Runtime.Serialization.Formatters.Binary;
Después debe crearse el objeto encargado de escribir los objetos a serializar:
BinaryFormatter unFormato = new BinaryFormatter();
Ahora ya se le puede pedir que escriba el objeto en el flujo creado para ello:
unFormato.Serialize(otroArchivo,unEstudiante);
Por último debe cerrarse el flujo:
otroArchivo.Close();
Si al conluir la ejecución de la clase que está realizando las mencionadas
instrucciones, se revisa el archivo creado, ”objetos.dat” en este caso, se verá una
secuencia como la que se presenta a continuación:
Una vez los objetos estén serializados, debe existir una opción que permita
leerlos, y permitirles que recuperen su condición de objetos, para ello debe:
• se debe establecer un flujo de comunicación con el archivo desde el cual se
leerán los objetos previamente serializados.
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
51
• se debe seleccionar el formato de deserialización con el cual deben leerse.
• se leen los objetos.
• se cierra el flujo.
A continuación se presenta un código que permite leer los objetos escritos en el
archivo”objetos.dat”
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
namespace AplicaciónConFlujos {
class ClaseUno {
[STAThread]
static void Main(string[] args) {
Stream otroArchivo = File.OpenRead("E:/Mis documentos/Cursos/NET/objetos.dat");
Estudiantes unEstudiante = new Estudiantes();
BinaryFormatter unFormato = new BinaryFormatter();
unEstudiante = (Estudiantes)unFormato.Deserialize(otroArchivo);
otroArchivo.Close();
Console.WriteLine("Nombres: "+unEstudiante.nombres);
Console.WriteLine("Apellidos: "+unEstudiante.apellidos);
Console.WriteLine("Código: "+unEstudiante.código);
Console.WriteLine("Edad: "+unEstudiante.edad);
Console.Write("Cursos: ");
foreach (string unCurso in unEstudiante.cursosQueToma){
Console.Write(unCurso+" ");
}
Console.WriteLine("\nHora Llegada: "+unEstudiante.horaLlegadaHoy);
Console.Read();
}
}
}
Lo cual produce como salida:
El atributo marcado como no serializable, queda con el valor por defecto para los
atributos numéricos, es decir cero (0).
Material .NET Parte Dos -Algoritmos para Telemática
Elaborado por Luz E. Jiménez - Universidad Icesi
52
Descargar