La biblioteca de clases de la plataforma .NET
Fernando Berzal Galiano & Francisco Cortijo Bon
[../language/index.xml]
El lenguaje C#
[../databases/index.xml]
Acceso a bases de datos
Esta sección describe algunas clases e interfaces de la plataforma .NET con el objetivo de que el lector sea capaz de utilizarlas al desarrollar sus propios programas.
Antes de que existiese la plataforma .NET, cada lenguaje de programación tenía su propia biblioteca de clases, lo que provocaba que no todos los lenguajes dispusiesen de la misma funcionalidad (p.ej. algunos APIs no estaban soportados en Visual Basic, por lo que había que recurrir a C/C++ para desarrollar determinados tipos de aplicaciones). Además, la funcionalidad proporcionada por distintas clases estaba repartida en componentes COM/COM+, controles ActiveX, DLLs del sistema... lo cual dificultaba su organización (además de hacer casi imposible la implementación de extensiones de las clases disponibles).
-
La plataforma .NET incluye una colección de clases bien organizada cuya parte independiente del sistema operativo ha sido propuesta para su estandarización (http://msdn.microsoft.com/net/ecma/).
-
La biblioteca de clases de la plataforma .NET integra todas las tecnologías Windows en un marco único para todos los lenguajes de programación (Windows Forms, GDI+, Web Forms, Web Services, impresión, redes...).
-
La biblioteca de clases .NET proporciona un modelo orientado a objetos que sustituye a los componentes COM.
System
System
[index.xml]
La biblioteca de clases de la plataforma .NET
[collections.xml]
Colecciones
El espacio de nombres System incluye la clase base para todos los objetos (System.Object), los tipos de datos primitivos y algunos no tan primitivos (cadenas, textos, fechas, horas y calendarios), además de un conjunto de interfaces estándar y soporte para la E/S a través de consola.
System.Object
System.Object
La clase System.Object es la clase base para todos los tipos de la plataforma .NET. Cualquier tipo hereda implícitamente de esta clase, de forma que se unifica el sistema de tipos. Esto facilita que las colecciones se puedan usar para almacenar cualquier cosa y es menos propenso a errores que el tipo VARIANT utilizado en COM (al ser un sistema fuertemente tipificado).
Métodos de System.Object
- System.Object.Equals(Object o) comprueba si dos objetos son iguales.
- System.Object.ReferenceEquals(Object o) comprueba si dos referencias apuntan al mismo objeto.
- System.Object.Finalize() es invocado por el recolector de basura para liberar el objeto.
- System.Object.GetHashCode() se emplea en la implementación colecciones de objetos (del tipo System.Collections.HashTable) y debe implementarse en las subclases de System.Object para crear buenas funciones hash (por defecto, se utiliza la identidad del objeto).
- System.Object.GetType() devuelve el tipo de un objeto (punto de entrada para el sistema de reflexión de la plataforma .NET).
- System.Object.MemberwiseClone() crea un clon del objeto utilizando la capacidad de reflexión de la plataforma .NET
- System.Object.ToString() devuelve una representación textual del objeto. Por defecto, devuelve el nombre de la clase, por lo que deberemos implementarlo nosotros. No está diseñado para mostrar mensajes al usuario (para esa función se debería utilizar el interfaz IFormattable)
public class Person
{
String name;
public override string ToString()
{
return name;
}
}
...
Name n1 = new Name("Fred");
Name n2 = new Name("Fred");
Name n3 = n2; // n2 & n3 apuntan al mismo objeto
if (n1 == n2) ... // false
if (n2 == n3) ... // true
if (n1.Equals(n2)) ... // true
if (n2.Equals(n3)) ... // true
primitives
Tipos primitivos
Se elimina la distinción entre los tipos primitivos del lenguaje y los demás objetos (al estilo de Smalltalk). Los tipos primitivos son comunes para toda la plataforma .NET, si bien se muestran en cada lenguaje según su sintaxis:
- C#: bool, int, long, string, double, float...
- Visual Basic.NET: Boolean, Integer, String...
Algunos tipos primitivos de interés son: System.Byte, System.Char, System.Boolean (valores lógicos: true o false), System.Guid (identificador universal de 128 bits, que además incluye un generador System.Guid.NewGuid()) y los valores nulos (System.DBNull para valores NULL en bases de datos,
System.Empty para representar el valor VT_EMPTY en COM y System.Missing para trabajar con parámetros opcionales).
System.String
Cadenas de caracteres
La clase System.String es la utilizada en todos los lenguajes de la plataforma .NET para representar cadenas de caracteres, que se almacenan utilizando UNICODE. El tipo System.String es inmutable, por lo que los métodos que parecen modificar una cadena en realidad lo que hacen es crear una nueva. La inmutabilidad de los objetos de este tipo hace recomendable el uso de String.Format o StringBuilder para manipular cadenas de caracteres (como en Java).
La clase String incluye métodos para:
- Realizar búsquedas en cadenas de caracteres: IndexOf(), LastIndexOf(), StartsWith(), EndsWith()
- Eliminar e insertar espacios en blanco: Trim(), PadLeft(), PadRight()
- Manipular subcadenas: Insert(), Remove(), Replace(), Substring(), Join(), Split()
- Modificar cadenas de caracteres: ToLower(), ToUpper(), Format() (al estilo del printf de C, pero seguro).
Comparación de cadenas
public static int Main()
{
string s = "abc";
string s1 = s; // s1 y s hacen referencia al mismo objeto
string s2 = "abc";
if (s1 == s2)
Console.WriteLine("OK");
else
Console.WriteLine("Esto no debería pasar");
return 0
}
System.DateTime
Fechas
- System.DateTime permite representar fechas (100 d.C. - 9999 d.C.) y realizar operaciones aritméticas con ellas, así como darles formato y leerlas de una cadena en función de la configuración local.
- System.TimeSpan sirve para trabajar con duraciones de tiempo (que se pueden expersar en distintas unidades).
- System.TimeZone permite trabajar con husos horarios.
System.Console
Consola
La clase System.Console proporciona la funcionalidad básica de E/S (equivalente a stdin, stdout y stderr en C). Para escribir se puede utilizar Write() o WriteLine() empleando la sintaxis de String.Format, mientras Read() lee un caracter y ReadLine() lee una línea completa.
Console.Write("Snow White and the {0} dwarfs", 7);
System-classes
Clases útiles de System
Clase
Función
System.URI
Identificadores universales de recursos
System.Random
Generador de números aleatorios
System.Convert
Conversiones de tipos básicos
System-interfaces
Interfaces de System
IFormattable
IFormattable
El interfaz IFormattable nos permite dar formato a la
representación del valor de un objeto:
interface IFormattable
{
String Format(String format, IServiceObjectProvider sop);
}
El método Format da formato al valor de la instancia actual tal como se le especifique:
String.Format("Please order {0} widgets at {1} each.", i, f);
String.Format("{0:U}", DateTime.Now);
El parámetro IServiceProvider se puede emplear para obtener características especiales de la configuración local (p.ej. delimitadores para números y fechas).
Incluso se puede implementar el interfaz ICustomFormatter para redefinir el formato en el que se muestran los valores de los tipos predefinidos.
IDisposable
IDisposable
Este interfaz nos permite controlar explícitamente la liberación de recursos:
class ResourceWrapper : IDisposable
{
private IntPrt handle; // Puntero a un recurso externp
private OtherResource otherRes;
bool disposed = false;
private void free ()
{
if (!disposed) {
CloseHandle (handle);
dispose = true;
}
}
public void Dispose
{
free();
OtherRes.Dispose();
GC.Suppress.Finalization(this);
}
public void Finalize ()
{
free();
Base.Finalize();
}
}
Colecciones
Colecciones
arrays
Arrays
Los arrays, representados mediante la clase System.Array, constituyen la única colección de datos que queda fuera del espacio de nombres System.Collections. La clase System.Array corresponde a los arrays en cualquier lenguaje de programación de la plataforma .NET y sirven para almacenar objetos de forma polimórfica (esto es, sirven para almacenar instancias de cualquier clase que herede de System.Object [todas]).
Los arrays pueden tener un número arbitrario de dimensiones y su tamaño se especifica al crearlos (CreateInstance). Una vez creado el array, su tamaño es fijo.
Entre sus cualidades más destacadas destaca el hecho de que los arrays pueden ordenarse (siempre y cuando se comparen objetos que implementen el interfaz IComparable o se indique un comparador IComparer). Además, si el array está ordenado, se pueden realizar búsquedas binarias en él.
public static void Main()
{
// Creación e inicialización arrays
int[] intArray = new int[5] { 1, 2, 3, 4, 5 };
Object[] objArray = new Object[5] { 26, 27, 28, 29, 30 };
// Copia de los dos primeros elementos de intArray en objArray
Array.Copy( intArray, objArray, 2 );
// Volcado en consola de los valores actuales de los arrays
Console.Write( "intArray: " ); PrintValues( intArray );
Console.Write( "objArray: " ); PrintValues( objArray );
// Copia de los dos últimos elementos de objArray en intArray
Array.Copy( objArray, objArray.GetUpperBound(0) - 1,
intArray, intArray.GetUpperBound(0) - 1, 2 );
}
collection-interfaces
Interfaces
Aparte de los arrays, la plataforma .NET también suministra una serie de interfaces estándar para la implementación de colecciones de objetos de distintos tipos (similar a las colecciones de Java).
IEnum...
IEnumerable & IEnumerator
El interfaz System.Collections.IEnumerable permite iterar sobre una colección de datos, proporcionando un mecanismo estándar para todas las colecciones:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
El método GetEnumerator() devuelve un iterador que implementa el interfaz System.Collections.IEnumerator:
public interface IEnumerator
{
Boolean MoveNext();
Object Current { get; }
void Reset();
}
SetType st = new SetType(...);
// Se obtiene un iterador para enumerar los elementos de la colección
IEnumerator e = st.GetEnumerator();
// Se recorre la colección utilizando el iterador como cursor
while (e.MoveNext()) {
// Se lee un elemento de la colección
ItemType it = (ItemType) e.Current;
// Se emplea el elemento para hacer lo que haga falta
Console.WriteLine(it);
}
ICollection
ICollection
El interfaz System.Collections.ICollection se deriva de IEnumerable y proporciona un interfaz básico para todas las colecciones: Count(), CopyTo(), IsSynchronized()
IList
IList
El interfaz System.Collections.IList también deriva de ICollection y permite trabajar con listas. Proporciona un indexador Item para acceder a los elementos de la lista en función de su posición y una serie de métodos para trabajar con el contenido de la lista: Add(), Remove(), Contains(), Clear()
IDictionary
IDictionary
El interfaz System.Collections.IDictionary especializa el interfaz ICollection y se utiliza para implementar diccionarios (conjuntos de pares clave-valor, a modo de memoria asociativa). Este interfaz proporciona un indexador Item para consultar un valor dada su clave y los mismos métodos que IList para trabajar con el diccionario: Add(), Remove(), Contains(), Clear() .
collection-classes
Clases
Aparte de los interfaces estándar que sirven como base para la implementación de colecciones, la biblioteca de clases de la plataforma .NET también incluye algunas colecciones ya implementadas:
ArrayList
ArrayList
La clase System.Collections.ArrayList implementa el interfaz IList para proporcionar arrays dinámicos cuyo tamaño puede variar dinámicamente (a diferencia de System.Array, que mantiene fijo su tamaño una vez que ha sido creado).
using System;
using System.Collections;
public class SampleArrayList
{
public static void Main()
{
// Creación
ArrayList myAL = new ArrayList();
// Inicialización
myAL.Add("Hello");
myAL.Add("World");
myAL.Add("!");
// Visualización
Console.WriteLine( "myAL" );
Console.WriteLine( "\tCount: {0}", myAL.Count );
Console.WriteLine( "\tCapacity: {0}", myAL.Capacity );
Console.Write( "\tValues:" );
PrintValues( myAL );
}
public static void PrintValues( IEnumerable myList )
{
IEnumerator myEnumerator = myList.GetEnumerator();
while ( myEnumerator.MoveNext() )
Console.Write("\t{0}", myEnumerator.Current );
Console.WriteLine();
}
}
BitArray
BitArray
La clase System.Collections.BitArray proporciona una implementación eficiente y compacta de arrays de bits.
HashTable
HashTable
La clase System.Collections.HashTable implementa un diccionario (IDictionary) mediante una tabla hash.
SortedList
SortedList
La clase System.Collections.SortedList implementa una lista ordenada sin duplicados que puede indexada por enteros (posición) y por cadenas (contenido).
Stack
Stack
La clase System.Collections.Stack implementa una pila LIFO con sus métodos Push() y Pop(). Al implementar el interfaz IEnumerable, podemos enumerar sus elementos:
using System;
using System.Collections;
public class SampleStack
{
public static void Main()
{
Stack myStack = new Stack();
myStack.Push("Hello");
myStack.Push("World");
myStack.Push("!");
Console.WriteLine( "myStack" );
Console.WriteLine( "\tCount: {0}", myStack.Count );
Console.Write( "\tValues:" );
PrintValues( myStack );
Console.Write( "\tLIFO:" );
EmptyStack ( myStack );
}
public static void PrintValues( IEnumerable myCollection )
{
IEnumerator myEnumerator = myCollection.GetEnumerator();
while ( myEnumerator.MoveNext() )
Console.Write( "\t{0}", myEnumerator.Current );
Console.WriteLine();
}
public static void EmptyStack( Stack myStack )
{
object value = myStack.Pop();
while (value!=null) {
Console.Write ("\t{0}", value);
value = myStack.Pop();
}
Console.WriteLine();
}
}
Queue
Queue
La clase System.Collections.Queue implementa una cola FIFO con sus métodos Enqueue() y Dequeue(). Igual que sucede con las pilas, las colas también son enumerables:
using System;
using System.Collections;
public class SampleQueue
{
public static void Main()
{
Queue myQ = new Queue();
myQ.Enqueue("Hello");
myQ.Enqueue("World");
myQ.Enqueue("!");
Console.WriteLine( "myQ" );
Console.WriteLine( "\tCount: {0}", myQ.Count );
Console.Write( "\tValues:" );
PrintValues( myQ );
Console.Write( "\tFIFO:" );
EmptyQueue (myQ);
}
public static void PrintValues ( Ienumerable myCollection )
{
IEnumerator myEnumerator = myCollection.GetEnumerator();
while ( myEnumerator.MoveNext() )
Console.Write( "\t{0}", myEnumerator.Current );
Console.WriteLine();
}
public static void EmptyQueue( Queue myQ )
{
object value = myQ.Dequeue();
while (value!=null) {
Console.Write ("\t{0}", value);
value = myQ.Dequeue();
}
Console.WriteLine();
}
}
IO
Entrada / Salida
files
Ficheros y directorios
La biblioteca de clases de la plataforma .NET proporciona una serie de clases que nos permiten trabajar con el sistema de archivos:
-
System.IO.Directory y System.IO.File proporcionan métodos estáticos para manipular directorios y ficheros, respectivamente.
-
System.IO.DirectoryInfo Y System.IO.FileInfo incluyen métodos para manipular instancias de directorios y ficheros, respectivamente. System.IO.DirectoryInfo representa un directorio concreto, a partir del cual se pueden obtener sus subdirectorios (con GetDirectories([mask])) y los ficheros que incluye (con GetFiles([mask]). Por su parte, System.IO.FileInfo representa un fichero concreto, que se puede obtener directamente especificando su path o a partir de la enumeración de ficheros de un directorio con GetFiles().
Una vez que tenemos un fichero con el que trabajar, utilizamos uno de sus métodos Open... para poder acceder y modificar su contenido: Open(), OpenRead(), OpenWrite(), OpenText(). Cualquiera de los métodos anteriores devuelve una instancia de System.IO.Stream.
streams
Streams
La clase base de los streams en la plataforma .NET es la clase abstracta System.IO.Stream, que proporciona funciones de acceso síncrono (Read(), Write()) y asíncrono (BeginRead(), BeginWrite(), EndRead(), EndWrite()).
- System.IO.FileStream se utiliza para acceder directamente al contenido de ficheros. De hecho es el tipo devuelto por una llamada a File.Open().
- System.IO.MemoryStream permite construir streams en memoria.
stream-readers
Lectura
Los stream readers proporcionan acceso de lectura a un stream:
- System.IO.BinaryReader permite leer datos en binario: ReadInt16(), ReadBoolean(), ReadDouble(), etc.
- System.IO.TextReader es una clase abstracta utilizada como base común de las dos siguientes:
- System.IO.StreamReader hereda de TextReader e implementa los métodos ReadLine() para leer una línea de texto y ReadToEnd() para leer lo que quede de stream.
- System.IO.StringReader también hereda de TextReader y se utiliza para simular streams a partir de cadenas de caracteres.
Lectura de un fichero de texto
static void Main(string[] args)
{
string filename = @"../../AssemblyInfo.cs";
string line;
Console.WriteLine ("*****************************");
StreamReader stream = new StreamReader(filename);
// StreamReader stream = File.OpenText (filename);
do {
line = stream.ReadLine();
Console.WriteLine (line);
} while (line != null);
stream.Close();
Console.WriteLine ("*****************************");
Console.ReadLine();
}
stream-writers
Escritura
Los stream writers sirven para escribir en streams:
- System.IO.BinaryWriter nos permite escribir datos en binario, mediante la utilización del método sobrecargado Write().
- System.IO.TextWriter es la clase abstracta que sirve de base para StreamWriter y StringWriter.
- System.IO.StreamWriter hereda de TextWriter y sirve para escribir cadenas de texto en streams.
- System.IO.StringWriter también hereda de TextWriter y simula streams en cadenas de caracteres.
public class MyWriter
{
private Stream stream;
public MyWriter(Stream stream)
{
this.stream = stream;
}
// Almacena la representación binaria de un double
public void WriteDouble(double myData)
{
byte[] b = myData.GetBytes();
stream.Write(b,0,b.Length);
}
public void Close()
{
stream.Close();
}
}
static void Main(string[] args)
{
string filenameI = @"../../AssemblyInfo.cs";
string filenameO = @"../../COPIA_AssemblyInfo.cs";
string line;
StreamReader streamI = new StreamReader(filenameI);
StreamWriter streamO = new StreamWriter(filenameO);
while ((line = streamI.ReadLine()) != null) {
streamO.WriteLine (line);
}
streamI.Close();
streamO.Close();
}
networking
System.Net
El espacio de nombres System.Net contiene todas las clases relativas al uso de protocolos de red para transmitir datos. Proporciona todo lo necesario para utilizar los protocolos de red IP (sockets) e IPX, así como otros protocolos de aplicación (p.ej. HTTP, incluidos sus mecanismos de autentificación [Basic, Digest, NTLM Challenge/Reponse] y el uso de cookies).
HTTP
HTTP
La clase abstracta System.Net.WebRequest es la clase base para el uso de protocolos de red como HTTP. Esta clase sirve de punto de acceso común para distintos protocolos. Además, permite registrar nuevos protocolos mediante el método RegisterPrefix().
La clase HttpWebRequest da soporte a los protocolos HTTP y HTTPS. El contenido de la solicitud HTTP[S] se rellena con un stream (WebRequest.GetRequestStream()) y la solicitud se ejecuta con GetResponse(). Los datos devueltos tras ejecutar GetResponse() se pueden leer mediante el método WebResponse.GetReponseStream().
HttpWebRequest HttpWReq =
(HttpWebRequest) WebRequestFactory.Create("http://elvex.ugr.es");
HttpWebResponse HttpWResp =
(HttpWebResponse)HttpWReq.GetResponse();
SMTP
SMTP
MailMessage MyMessage = new MailMessage();
MyMessage.To = "berzal@acm.org";
MyMessage.From = "MyApplication";
MyMessage.Subject = "Unhandled Error!!!";
MyMessage.BodyFormat = MailFormat.Html;
MyMessage.Body = "<html><body><h1> ERROR </h1></body></html>";
SmtpMail.Send(MyMessage);
Serialization
Serialización
La serialización es el proceso de convertir un objeto, o un grafo conexo de objetos, en una secuencia de bytes.
using System;
using System.IO;
using System.Collections;
using System.Serialization;
using System.Serialization.Formatters.Binary;
class SerializeExample
{
public static void Main(String[] args)
{
ArrayList l = new ArrayList();
for (int x=0; x < 100; x++)
l.Add (x);
FileStream s = File.Create("foo.bin");
BinaryFormatter b = new BinaryFormatter();
b.Serialize(s, l);
}
}
using System;
using System.IO;
using System.Collections;
using System.Serialization;
using System.Serialization.Formatters.Binary;
class DeserializeExample
{
public static void Main(String[] args)
{
FileStream s = File.Open("foo.bin");
BinaryFormatter b = new BinaryFormatter();
ArrayList p = (ArrayList) b.Deserialize(s);
p.ToString();
}
}
Un tipo no es serializable salvo que se marque específicamente con el atributo Serializable:
[Serializable] public class MyClass {}
[Serializable] public class MyClass
{
[NotSerialized] int _size;
}
El proceso de serialización puede personalizarse si implementamos el interfaz ISerializable, el interfaz IDeserializationEventListener o creamos nuestros propios "formateadores" (custom formatters).
ISerializable
Un único método:
void GetObjectData (SerializationInfo info,
StreamingContext context);
y un constructor que puede ser privado:
private <T> (SerializationInfo info,
StreamingContext context)
IFormatter
public interface IFormatter
{
// Propiedades
SerializationBinder Binder { get; set; }
StreamingContext Context { get; set; }
ISurrogateSelector SurrogateSelector { get; set; }
// Métodos
object Deserialize(Stream serializationStream);
void Serialize(Stream serializationStream, object graph);
}