Actividad integradora 6 CREAR UN RECURSO MULTIMEDIA
manual de C#
1. ¿Qué es C#?
C# o C Sharp es un lenguaje de programación que está incluido en la Plataforma .NET y corre en
el Lenguaje Común en Tiempo de Ejecución (CLR, Common Language Runtime). El primer
lenguaje en importancia para el CLR es C#, mucho de lo que soporta la Plataforma .NET está
escrito en C#.
C# intenta ser el lenguaje base para escribir aplicaciones .NET
C# deriva de C y C++, es moderno, simple y enteramente orientado a objetos, simplifica y
moderniza a C++ en las áreas de clases, namespaces, sobrecarga de métodos y manejo de
excepciones. Se elimino la complejidad de C++ para hacerlo más fácil de utilizar y menos
propenso a errores.
Algunas características de C# son:
C# provee el beneficio de un ambiente elegante y unificado.
No soporta herencia múltiple, solamente el runtime .NET permite la herencia múltiple
en la forma de interfaces, las cuales no pueden contener implementación.
No maneja apuntadores, para emular la función de los apuntadores se
utiliza delegates el cual provee las bases para el .NET event model.
Por default trabaja con código administrado.
La Plataforma .NET provee un colector de basura que es responsable de administrar la
memoria en los programas C#.
El manejo de errores está basado en excepciones.
Soporta los conceptos como encapsulación, herencia y polimorfismo de la programación
orientada a objetos.
El Modelo completo de clases está construido en la cima del .NET Virtual Object System
(VOS). El modelo de objetos es parte de la infraestructura y ya no forma parte del
lenguaje de progrmación.
No existen funciones globales, variables o constantes. Todo deber ser encapsulado
dentro de la clase, como un miembro de la instancia (accesible via una instancia de
clase) o un miembro estático (via el tipo).
Los métodos que se definen en las clases son por default no virtuales (no pueden ser
sobre escritos al derivar clases)
Soporta los modificadores de acceso private, protected, public y agrega un cuarto
modificador internal.
Solamente se permite una base clase, si se requiere herencia múltiple es posible
implementar intefaces.
No es posible utilizar variables no inicializadas.
No es posible hacer el cast de un entero a un tipo de referencia (objeto).
Los parámetros que son pasados son type-safe.
El soporte de versiones lo provee el CLR.
Permite acceder a diferentes APIs a través de .NET Common Language Specification, el
cual define el estádar de interoperabilidad entre lenguajes que se adhieran a este
estándar.
La Plataforma .NET provee un acceso transparente a COM.
Soporta OLE
Permite la interoperabilidad con APIs al estilo C y DLLs, esta característica para acceder
a APIs nativas es llamada Platform Invocation Services (PInvoke)
2. Por default el código es safe mode, pero es posible declarar clases o sólo métodos
unsafe, esta declaración permite utilizar apuntadores, estructuras y almacenamiento de
arreglos estáticos.
C# depende del runtime que provee la Plataforma .NET, el runtime administra la
ejecución de código.
Lenguaje Orientado a Objetos
Según Bjarne Stroustrup autor del lenguaje de programación C++, para que un lenguaje sea
llamado Orientado a Objetos debe soportar tres conceptos: objetos, clases y herencia.
Aunque los lenguajes orientados a objetos se construyen sobre los conceptos de :
Encapsulación
Herencia
Polimorfismo
Objeto
Un Objeto es una instancia de un tipo de clase.
La instanciación es el acto de crear una instancia de un objeto, la instancia es un objeto,
la instanciación usa el operadornew, después la instanciación es posible comunicarnos con el
objeto a través de sus miembros.
Un Objeto es una colección de información relacionada y funcional.
Un objeto se compone de:
Datos que describen el objeto y
Operaciones que pueden ser realizadas por el objeto
Herencia
La Herencia es la habilidad para heredar datos y funcionalidad de un objeto padre, la herencia
es una característica fundamental de un sistema orientado a objetos.
A través de la herencia es posible crear o derivar una nueva clase basada en una clase existente.
Una clase derivada es la nueva clase que esta siendo creada y la clase base es una de las cuales
la nueva clase es derivada. La nueva clase derivada hereda todos los miembros de la clase base
por consiguiente permite reusar el trabajo previo.
En C# se puede asumir que la clase derivada podría heredar todos los miembros de la clase base.
La herencia es un ejemplo del diseño orientado a objetos conocido como una relación "is -a" (es-un),
por ejemplo:
"un empleado es una persona".
Al utilizar la herencia la clase base necesita ser diseñada teniendo en mente la herencia, si los
objetos no tienen la estructura apropiada la herencia no podría funcionar correctamente.
Una clase derivada no debería requerir más ni prometer menos que su clase base sobre cualquiera
de sus interfaces heredadas.
Una interfaz de clase es un contrato entre esta y los programadores que usan la clase.
upcasting, cuando un programador tiene una referencia a la clase derivada, el programador
siempre puede tratar a esa clase como si fuera la clase base.
En el lenguaje común en tiempo de ejecución .NET todos los objetos heredan de la última clase
base llamada object y existe sólo una herencia simple de objetos.
Un objeto puede derivar sólo de una clase base.
Clase
Una Clase es una plantilla para un objeto.
Una Clase define las operaciones que un objeto puede realizar y define un valor que mantiene el
estado del objeto, los componentes principales de una clase son: métodos, eventos y propiedades.
Una instancia de una clase es un objeto, se accede a la funcionalidad de un objeto invocando sus
métodos y accediendo a sus propiedades, eventos y campos.
Una clase utiliza modificadores para especificar la accesibilidad de la clase y sus componentes, los
componentes de una clase son llamados miembros por lo que existen diferentes tipos de
3. miembros. Una referencia se refiere a una instancia, una instancia es la creación de un objeto del
tipo clase que se está declarando. Una clase utiliza ninguno, uno o másconstructores para ayudar
a definir la instancia de una clase. Existe una palabra reservada llamada this que sirve para
hacer referencia a la clase actual en el ámbito en el cual es utilizada. Cuando se hace referencia
a una variable de instancia que tiene el mismo nombre de un parámetro se debe
utilizar this.name.
Al crear y manipular objetos no es necesario administrar la memoria que estos ocupan ya que
existe un mecanismo que se encarga de esto llamado garbage collector (recolector de basura),
pero es una buena práctica no olvidar liberar los recursos.
Funciones Miembro
Una Función Miembro puede ser un constructor, es decir, una pieza de código que es invocada
en una instancia del objeto.
Campos Estáticos
Un Miembro Estático definine miembros de un objeto que no son asociados con una instancia de
clase específica.
Un Campo Estático es el tipo más simple de un miembro estático, para declarar un campo
estático se utiliza el modificador static.
Un campo estático puede accederse a través del nombre de la clase, en vez de la instancia de la
clase (objeto):
using System;
class MiContador{
//Campo Estático
public static int iContador = 0;
public MiContador(){
iContador++;
}
}
class App{
public static void Main(){
MiContador ContadorA = new MiContador();
Console.WriteLine(MiContador.iContador);
MiContador ContadorB = new MiContador();
Console.WriteLine(MiContador.iContador);
}
}
El ejemplo determina cuantas instancias del objeto han sido creadas.
Clase Base
Una Clase base es un objeto padre de donde se basa un nuevo trabajo.
Clase Derivada
Una Clase derivada es un objeto hijo.
Clase Abstracta
Una Clase Abstracta define las funciones que una clase derivada debe implementar.
Una Clase Abstracta define un contrato en donde las clases derivadas deben definir las funciones
que la clase padre marca utilizando la palabra reservada abstract, además que la clase padre
también se define como abstract.
using System;
abstract public class Persona{//Indica que la clase es abstracta
//Propiedades
public string sNombre;
public int iEdad;
//Constructor
public Persona(string sNombre, int iEdad){
this.sNombre = sNombre;
this.iEdad = iEdad;
4. }
//Métodos
abstract public string Tipo();//Método que la clase
derivada debe implementar
}
//Herencia Simple
public class Empleado : Persona{
public Empleado(string sNombre, int iEdad):base(sNombre,
iEdad){}
override public string Tipo(){
return "Empleado";
}
}
class App{
//Aplicación
public static void Main(){
Console.WriteLine("--- Arreglo de Objetos ---");
Empleado[] aProgramadores = new Empleado[2];
aProgramadores[0] = new Empleado("Bill Gates", 50);
aProgramadores[1] = new Empleado("Eric S. Raymond", 60);
for(int i = 0; i < aProgramadores.Length; i++){
Console.WriteLine("aProgramadores["+i+"].sNombre : " +
aProgramadores[i].sNombre);
Console.WriteLine("aProgramadores[" + i + "].iEdad : "
+ aProgramadores[i].iEdad);
Console.WriteLine("aProgramadores[" + i + "].Tipo : " +
aProgramadores[i].Tipo());
}
}
}
Clase Sealed
Una Clase sealed se utiliza para prevenir que una clase sea utilizada como una clase base, su
principal uso es para prevenir la derivación no planeada.
sealed class ClaseBase{
ClaseBase(){}
}
class ClaseDerivada : ClaseBase{
}
class Sellada{
public static void Main(){
ClaseDerivada CD = new ClaseDerivada();
}
}
Al compilar el código se muestra el siguiente mensaje:
sealed.cs(4,7): error CS0509: 'ClaseDerivada' : cannot inherit from sealed
class 'ClaseBase'
sealed.cs(1,14): (Location of symbol related to previous error)
El error es porque ClaseDerivada no puede utilizar ClaseBase como una clase base
porque ClaseBase es sealed, es decir, no permite derivaciones.
Overloading/Sobrecarga
5. La Sobrecarga (Overloading) hace posible utilizar dos o más clases con el mismo nombre, pero
con parámetros diferentes. La sobrecarga es común especialmente para los constructores para
definir diferentes maneras de crear una instancia nueva.
Cuando una función sobrecargada es invocada el compilador selecciona la función apropiada que
coincide con los parámetros.
Herencia Simple
La Herencia Simple permite derivar una clase en una clase nueva, que contiene la definición de
la clase de la cual deriva, es decir, hereda todos los miembros datos de la clase, aunque pueden
existir miembros a los cuales no se pueda tener acceso por ser private.
Los constructores no pueden ser heredados, por lo que es necesario escribir constructores y si
funcionalmente no existe alguna modificación se invoca el constructor de la clase que hereda
utilizando la sintaxis base. Si se omite el constructor de la clase base y es invocado el compilador
podría invocar el constructor de la clase base sin parámetros.
using System;
public class Persona{
//Propiedades
public string sNombre;
public int iEdad;
private double dSueldo;
//Constructor
public Persona(string sNombre, int iEdad){
this.sNombre = sNombre;
this.iEdad = iEdad;
}
//Métodos
public string Tipo(){
return "Persona";
}
public void AsignarSueldo(double dSueldo){
this.dSueldo = dSueldo;
}
public double ObtenerSueldo(){
return this.dSueldo;
}
}
//Herencia Simple
public class Empleado : Persona{
public Empleado(string sNombre, int iEdad):base(sNombre,
iEdad){}
public new string Tipo(){
return "Empleado";
}
double dSueldo;
public new void AsignarSueldo(double dSueldo){
this.dSueldo = dSueldo * dSueldo;
}
public new double ObtenerSueldo(){
return this.dSueldo;
}
}
class App{
//Aplicación
public static void Main(){
Persona Mexicano = new Persona("Gerado Ángeles
Nava", 33);
6. Console.WriteLine("Mexicano.sNombre : " +
Mexicano.sNombre);
Console.WriteLine("Mexicano.iEdad : " +
Mexicano.iEdad);
double dSueldo = 123.456;
Mexicano.AsignarSueldo(dSueldo);
Console.WriteLine("Mexicano.iSueldo : " +
Mexicano.ObtenerSueldo());
Console.WriteLine("Mexicano.Tipo : " +
Mexicano.Tipo());
Console.WriteLine("--- Herencia Simple ---");
Empleado Programador = new Empleado("Carlos
Salinas G.", 53);
Console.WriteLine("Programador.sNombre : " +
Programador.sNombre);
Console.WriteLine("Programador.iEdad : " +
Programador.iEdad);
Programador.AsignarSueldo(dSueldo);
Console.WriteLine("Programador.iSueldo : " +
Programador.ObtenerSueldo());
Console.WriteLine("Programador.Tipo : " +
Programador.Tipo());
}
}
Polimorfismo y Funciones Virtuales
El polimorfismo es la funcionalidad que permite a código antiguo invocar código nuevo, también
permite extender el sistema sin modificar el código existente, esto se logra sobreescribiendo o
redefiniendo el código, para lo cual se utilizan funciones virtuales y la palabra clave override.
Las funciones abstractas son automaticamente funciones virtuales, las cuales permiten al
programador usar polimorfismo para hacer su código simple.
Virtual significa que cuando una invocación a funciones miembro, el compilador debería buscar
por el tipo real del objeto y no por el tipo de la referencia, e invocar en base al tipo la función
apropiada.
using System;
public class Persona{
//Propiedades
public string sNombre;
public int iEdad;
//Constructor
public Persona(string sNombre, int iEdad){
this.sNombre = sNombre;
this.iEdad = iEdad;
}
//Métodos
virtual public string Tipo(){
return "Persona";
}
}
//Herencia Simple
public class Empleado : Persona{
public Empleado(string sNombre, int iEdad):base(sNombre,
iEdad){}
override public string Tipo(){
return "Empleado";
7. }
}
class App{
//Aplicación
public static void Main(){
Persona Mexicano = new Persona("Gerado Ángeles Nava", 33);
Console.WriteLine("Mexicano.sNombre : " +
Mexicano.sNombre);
Console.WriteLine("Mexicano.iEdad : " + Mexicano.iEdad);
Console.WriteLine("Mexicano.Tipo : " + Mexicano.Tipo());
Console.WriteLine("--- Arreglo de Objetos ---");
Empleado[] aProgramadores = new Empleado[2];
aProgramadores[0] = new Empleado("Bill Gates", 50);
aProgramadores[1] = new Empleado("Eric S. Raymond", 60);
for(int i = 0; i < aProgramadores.Length; i++){
Console.WriteLine("aProgramadores["+i+"].sNombre : " +
aProgramadores[i].sNombre);
Console.WriteLine("aProgramadores[" + i + "].iEdad : "
+ aProgramadores[i].iEdad);
Console.WriteLine("aProgramadores[" + i + "].Tipo : " +
aProgramadores[i].Tipo());
}
}
}
Cuando una función es declarada con la palabra reservada override significa que es la misma
función que fue declarada en la clase base, si la palabra reservada override se omite el
compilador podría asumir que la función no está relacionada a la función de la clase base y no
despacha la función virtual (el compilador podría sugerir omitir override o agregar new) .
Cuando existe una función virtual el programador puede pasar una referencia a la clase abstracta
aunque la clase derivada y el compilador podrían escribir código para invocar la versión apropiada
de la función en tiempo de ejecución.
Por ejemplo, el objeto base object tiene una función virtual llamada ToString() que convierte
un objeto a string. Si se invoca la función ToString() en un objeto que que no la tiene como
versión propia, la versión de la función que es parte de la clase object podría ser invocada.
Encapsulación y Visibilidad
Encapsulación (también llamada information hiding), habilidad de un objeto para ocultar sus
datos internos o parte interna de sus usuarios y provee una interface que hace las partes
importantes del objeto accesible programaticamente.
La encapsulación provee los límites entre una interfaz externa y los detalles de su implementación
interna.
Al diseñar objetos el programador decide que objetos son visibles al usuario y que es privado
dentro del objeto, los detalles que no son visibles al usuario son señalados para ser encapsulados
en la clase.
Razones para encapsular y ocultar:
El usuario no puede cambiar lo que es privado en el objeto, lo cual reduce la oportunidad
de que el usuario cambie o dependa de los detalles en el código, si el usuario hace lode
detalles dependientes, los cambios realizados en el objeto quizá arruinen el código del
usuario.
Los cambios realizados en partes públicas de un objeto deben permanecer compatibles
con la versión previa. Las partes públicas pueden ser cambiadas sin arruinar el código
del usuario.
8. Los campos privados pueden sólo ser accesados desde la clase, los campos públicos
pueden ser accesados a través de cualquier instancia de la clase.
Abstracción
Una Abstracción se refiere a como un problema dado es representado en el espacio de programa.
Como desarrollador de clases es necesario pensar en terminos de hacer el mejor diseño de
abstracción para los clientes de clase y permitirles enfocarse a la tarea que deben realizar y no
escudriñar o indagar en los detalles de como funciona la clase, también es necesario determinar
cuales de los miembros de la clase deberían ser accesibles publicamente.
Los beneficios de una buena abstracción, es diseñarla de manera tal que las modificaciones son
minimas, si se conoce bien el problema a resolver facilita determinar que métodos necesitara el
usuario, también será un sistema fácil de entender y mantener.
La interfaz de clase es la implementación de la abstracción.
Plataforma .NET
La solución Microsoft .NET comprende cuatro componentes fundamentales:
Servicios de Bloques de Construcción .NET o acceso programático para ciertos servicios
tales como almacenamiento de archivos, calendario y pasaporte .NET el cual es un
servicio de verificación de identidad.
Dispositivos de Software .NET el cual podría correr sobre nuevos dispositivos Internet.
La Experiencia de Usuario .NET la cual incluye características tales como interfaz
natural, agentes de información y etiquetas inteligentes es decir una tecnología que
automatiza hiperligas para información relacionada a palabras y frases en documentos
creados por el usuario.
La Infraestructura .NET la cual comprende la plataforma .NET, Microsoft Studio .NET, los
Servidores Corporativos .NET y Microsoft Windows .NET
La Infraestructura .NET se refiere a todas las tecnologías que constituyen el nuevo ambiente para
crear y ejecutar aplicaciones robustas, escalables y distribuidas. La parte de .NET que permite
desarrollar estas aplicaciones es la plataforma .NET.
La Plataforma .NET consiste de un Lenguaje Común en Tiempo de Ejecución (CLR) y la Biblioteca
de Clases de la Plataforma .NET algunas veces llamada la Biblioteca de Clases Base (CBL).
El CLR es como una máquina virtual (el código que corre dentro del CLR en ejecución en un
ambiente encapsulado y controlado, separados de otros procesos en la máquina) en la cual
funcionan las aplicaciones .NET, todos los lenguajes .NET tienen la biblioteca de clases de la
Plataforma .NET a su disposición.
La biblioteca de clases de la Plataforma .NET incluyen soporte para cualquiera de los archivos de
entrada/salida y base de datos de entrada/salida para XML y SOAP.
La Plataforma .NET es una plataforma nueva que simplifica la aplicación del desarrollo en
ambientes altamente distribuidos de Internet. La Plataforma .NET está diseñada para cumplir los
siguientes objetivos:
Proveer un ambiente consistente de programación orientada a objetos, tanto o si el
código objeto es almacenado y ejecutado localmente, o si es ejecutado localmente pero
distribuido en Internet o si es ejecutado remotamente.
Proveer un ambiente de ejecución de código que minimice la distribución de software y
conflictos de versiones.
Proveer un ambiente de ejecución de código que garantice la ejecución de código
seguro, incluyendo el código creado por un desconocido o un tercero semiconfiable.
Proveer un ambiente de ejecución de código que elimine los problemas de desempeño
de ambientes de scripts o interpretes.
Hacer que la experiencia del desarrollador sea consistente a través de una amplia
variedad de aplicaciones, tal como aplicaciones basadas en Windows y aplicaciones
basadas en Web.
9. Construir toda la comunicación sobre estándares industriales para asegurar que el
código basado en la Plataforma .NET pueda integrarse con cualquier otro código.
La Plataforma .NET consiste de dos componentes principales:
El Lenguaje Común en Tiempo de Ejecución (CLR) el cual es el fundamento de la
Plataforma .NET
La Biblioteca de Clases de la Plataforma .NET, es una colección orientada a objetos de
tipos reusables que pueden utilizarse para desarrollar aplicaciones en el rango de
aplicaciones tradicionales desde la línea de comandos o interfaces de usuario gráficas
(GUI) hasta aplicaciones basadas en las últimas inovaciones que provee ASP.NET tales
como Web Forms y servicios web XML.
El runtime es una agente que administra el código en tiempo de ejecución al proveer de
serviciones principales como la administración de memoria, administración de hilos, también
implementa tipos estrictos de seguridad y otras formas de verificación de código que aseguren la
seguridad y robustez.
El concepto de administración de código es principio fundamental del runtime. El código que
manipulará el runtime es conocido como código administrado, mientras que el código que no será
manipulado por el runtime es conocido como un código no administrado.
La plataforma .NET puede almacenar componentes no administrados que son cargados por el CLR
en sus procesos e inicializados por la ejecución de código administrado de esta manera se crea
un ambiente de software que puede explotar tanto caracterísitcas de código administrado como
las del código no administrado.
.NET Common Language Runtime - CLR
El .NET Common Language Runtime (Lenguage común en tiempo de ejecución .NET) es un
ambiente basado en componentes y C# esta diseñado para facilitar la creación de componentes.
Todos los objetos son escritos como componentes y los componentes son el centro de acción, por
ello que reciba el nombre de lenguaje céntrico de componentes (component-centric).
Los componentes creados en C# son totalmente auto-describibles y pueden ser utilizados sin un
proceso de registro.
C# ayuda en la creación de componentes mediante el runtime y framework .NET, los cuales
proveen un sistema de tipo unificado en el cual todo puede ser tratado como un objeto.
Características del CLR
EL CLR no sólo soporta el compilador de C#, también el de Visual Basic y C++, el código que
generan estos compiladores para ser soportado por CLR es llamado managed code.
Algunos de los beneficios que las aplicaciones obtienen del CLR son:
Integración de lenguajes a través del Common Language Specification
Administración automatica de memoria, a través del recolector de basura.
Manejo de excepciones de lenguajes.
Seguridad type safety.
Soporte de versiones.
Modelo Simplificado para la interacción de componentes.
El CLR provee los beneficios anteriores, el compilador debe emitir los metadatos en el managed
code. Los metadatosdescriben los tipos en el código y son empaquetados en el código ejecutable.
El CLR administra la memoria, ejecución de hilos, ejecución de código, verificación de código
seguro, compilación y otros servicios. Estas características son intrínsecas a la administración de
código que corre sobre el CLR.
La seguridad y administración de componentes depende de un número de factores que se incluyen
en su origen como Internet red corporativa, computadora local, es decir, quizá o quizá no están
disponibles para desempeñar operaciones de acceso a archivos, acceso a registros o funciones
sensitivas, aún si comienzan a utilizarse en el misma aplicación activa.
El runtime forza el acceso a código seguro, no es posible acceder a datos personales, sistema de
archivos o red.
10. El runtime también forza la robustez del código implementando una infraestrucutra estricta de
verificación de código llamado Common Type System (CTS), el cual asegura que toda la
administración de código se describe así misma. La variedad de compiladores Microsoft y de
terceros genera código administrado que conforma el CTS, es decir, que la administración de
código puede consumir otros tipos e instancias administradas, mientras que se forza estrictamente
la fidelidad de tipo y seguridad de tipo.
La administración del ambiente del runtime elimina cuestiones de software comunes, liberando
por ejemplo recursos que ya no son utilizados.
El runtime también acelera la productividad del desarrollador, no importa el lenguaje que un
programador utilice, puede utilizar las ventajas del runtime, biblioteca de clases, y componentes
escritos por otros programadores, cualquier compilador que utilice el runtime puede hacer lo
mismo,
La interoperabilidad entre código administrado y no administrado permiten a los desarrolladores
continuar utilizando componentes COM y DLLs.
El runtime está diseñado para incrementar el desempeño, através del CLR que provee muchos
servicios estándar, el código administrado nunca es interpretado, una característica llamada just-in-
time (JIT) permite compilar todo el código administrado para correr en el lenguaje nativo de la
máquina del sistema o de cualquiera que se este ejecutando. El administrador de memoria elimina
las posibilidades de fragmentación de memoria e incrementa la referencia de localidad de memoria
para impulsar el incremento del desempeño.
El runtime soporta aplicaciones del lado del servidor como Microsoft® SQL Server™ e Internet
Information Services (IIS), esta infraestructura permite utilizar codigo administrado para escribir
la lógica del negocio.
.NET Runtime Environment
El Lenguaje Común en Tiempo de Ejecución provee los servicios de ejecución básica. Las clases
base proveen tipos de datos básicos, clases colección y otras clases generales. Las clases base
son clases para tratar datos y XML. En la parte superior de la arquitectura las clases exponen
servicios web y tratramiento de la intefaz de usuario. Una aplicación puede hacer invocaciones en
cualquier nivel y utilizar clases desde cualquier nivel.
Organización .NET Framework:
Servicios Web Interfaz de Usuario
Datos y XML
Clases Base
Lenguaje Común en Tiempo de Ejecución
Ambiente/Entorno de Ejecución
El ambiente o entorno provee un modelo de programación simple, seguro, soporta de
herramientas potentes y ayuda con la distribución, empaquetado y soporte:
Modelo de Programación Simple, todos los servicios son ofrecidos a través de un modelo
común que puede ser accedido desde cualquier lenguaje .NET y los servicios pueden ser
escritos en cualquier lenguaje .NET, el entorno o ambiente en gran parte es un lenguaje
agnóstico permitiendo la elección de lenguaje, esto hace el código fácil de reusar para
el programador y los proveedores de bibliotecas.
En el runtime .NET todos los errores son reportados via excepciones.
El entorno contiene las Bibliotecas de Clase Base (Base Class Libraries - BCL) las cuales
proveen las funciones tradicionales fundadas en bibliotecas en tiempo de ejecución, la
funcionalidad del BCL incluye:
Clases colección, tales como consultas, arreglos, pilas y tablas hash.
Clases de acceso a bases de datos
Clases IO (input-output)
Clases WinForms, para crear interfaces de usuario
11. Clases Network
Fuera de la clase base en tiempo de ejecución, existen muchos otros componentes que controlan
la interfaz de usuario (UI) y realizan otras operaciones sofisticadas.
Seguridad, el entorno del runtime .NET está diseñado para ser un entorno seguro.
El runtime .NET es un entorno administrado o controlado, lo cual significa que
el runtime administra la memoria por el programador a través del recolector de basura.
El runtime .NET es un entorno verificado, en tiempo de ejecución el entorno verifica que
la ejecución del código sea de tipo segura (type-safe).
El sistema de seguridad interactua con el verificador para asegurar que el código realice
sólo lo que tiene permitido hacer, esto es posible especificando un requerimiento de
seguridad para una pieza de código específica.
Soporte de herramientas potentes, Microsoft suministra cuatro lenguajes .NET: VB,
VC++, C# y JScript. La depuración en gran medida es enriquecida por el runtime .NET,
el modelo de ejecución común hace la depuración de lenguajes simple y directa.
Distribución, empaquetado y soporte, El runtime .NET ayuda simplificando la
distribución y en algunos casos no existe el paso tradicional de instalación, porque los
paquetes son distribuidos en un formato genérico, un paquete puede correr en cualquier
entorno que soporte .NET, el entorno separa los componentes de una aplicación por lo
que una aplicación sólo corre con los componentes que son enviados.
Ensamblaje
En el runtime .NET el mecanismo de empaquetado es el ensamble (assembly), cuando el código
es compilado por uno de los compiladores .NET, es convertido a una forma intermedia conocida
como IL.
El ensamble contiene todos los IL, metadatos y otros archivos requeridos para que un paquete
se ejecute en un paquete completo.
Cada ensamble contiene un manifiesto que enumera todos los archivos que están contenidos en
el ensamble, controla que tipos y recursos son expuestos fuera del ensamble y relaciona las
referencias de esos tipos y recursos a los archivos que contienen los tipos y recursos.
El manifiesto también lista otros ensambles que dependen de un ensamble.
Los ensambles se contienen a sí mismo, existe suficiente información en el ensamble para ser
auto-descrito.
Cuando se define un ensamble, el ensamble puede ser contenido en un solo archivo o puede ser
dividido entre varios archivos. Utilizando varios archivos podría hacer posible un escenario donde
las secciones del ensamble sean descargadas sólo como se necesiten.
Interoperabilidad de Lenguaje
Una de las metas del runtime .NET es ser un lenguaje agnóstico, permitiendo que el código sea
utilizado y escrito desde cualquier lenguaje, no sólo las clases pueden ser escritas en algún
lenguaje .NET como VB.NET y ser invocadas desde otro lenguaje .NET como C#, una clase que
fué escrita en VB.NET puede ser utilizada como una clase base escrita en C# y esa clase podría
ser utilizada desde una clase VC++ o JScript, es decir, no importaria en que clase sea escrita una
clase.
Para hacer lo anterior posible existen algunos obstaculos como las propias características del
lenguaje, ya que un lenguaje no podría soportar ciertas cosas que otro si las soporte, por ejemplo
la sobrecarga de operadores.
Para que una clase sea utilizada desde un lenguaje .NET, la clase debe adherir la Especificación
Común de Lenguaje(Common Language Specification - CLS) la cual describe que características
pueden ser visibles en la interfaz pública de la clase, por ejemplo el CLS prohibe exponer tipos de
datos sin signo, porque no todos los lenguajes pueden utilizarlos.
Atributos
El runtime .NET soporta atributos personalizables, los cuales son en cierto sentido un lugar para
colocar información descriptiva en los metadatos junto con un objeto y entonces recuper después
los datos. Los atributos proveen un mecanismo general para hacer esto y son utilizados en exceso
12. en todo el tiempo de ejecución para almacenar información que modifica como el runtime utiliza
las clases.
Los atributos son extensibles y permite a los programadores definir atributos y utilizarlos.
Los atributos se especifican encerrandolos entre corchetes:
[Version("14/09/2005", Comentario="1.0.1.0")]
Los atributos son anotaciones que se colocan en elementos de código fuente,
como clases, miembros, parámetros, etc.
Los atributos puede ser utilizados para: cambiar el comportamiento del runtime, proveer
información acerca de un objeto, llevar información organizacional al diseñador.
El atributo información es almacenado con los metadatos del elemento y pueden ser facilmente
recuperados en tiempo de ejecución a través de un proceso conocido como reflection.
C# utiliza un Atributo Condicional para controlar cuando las funciones miembro son invocadas.
Por convención los atributos se agregan al final del nombre de una clase, con la finalidad de
conocer cuales son clases atributo y cuales son clases normales. Todos los atributos derivan
de System.Attribute.
Procure que el atributo para el elemento sea específico, utilizando los identificadores siguientes:
Identificador Descripción
assembly ensamble
module módulo
type clase o estructura
method método
property porpiedad
event evento
field campo
param parámetro
return valor de regreso
Los atributos que son aplicados a ensambles o módulos deben colocarse después de cualquier
cláusula using y antes de cualquier código.
Biblioteca de Clases de la Plataforma .NET
La Biblioteca de Clases de la Plataforma .NET es una colección de tipos reutilizables integradas en
el CLR.
Los tipos de la Plataforma .NET permiten llevar a cabo tareas de programación comunes como
manipulación de strings, colecciones de datos, conectividad a bases de datos y acceso a archivos.
Es posible utilizar la Plataforma .NET para desarrollar los siguientes tipos de aplicaciones y
servicios:
Aplicaciones de consola
Windows Forms
Aplicaciones ASP.NET
Servicios Web XML
Servicios Windows
Requerimientos de Software
Todo lo que se necesita para desarrollar en C# es el Kit de desarrollo (SDK), del cual solo se
utilizará el CLR y el compilador de C#.
Lenguaje Intermedio y Metadatos
Microsoft desarrollo un lenguaje parecido al lenguaje ensamblador llamado Microsoft
Intermediate Language (MSIL).
Para compilar aplicaciones .NET, los compiladores toman el código fuente como entrada y
producen MSIL como salida.
13. MSIL en sí es un lenguaje completo con el cual es posible escribir aplicaciones.
El managed code generado por el compilador C# no es código nativo porque es un código de
Lenguaje Intermedio (IL). Este código IL se convierte en la entrada para la administración del
proceso de ejecución del CLR. La ventaja final del código IL es que el CPU es independiente, lo
cual significa que se necesita un compilador en la máquina destino para cambiar el código IL en
el código nativo.
El IL es generado por el compilador, pero no es lo único que se provee para el runtime, el
compilador también genera metadatos acerca del código, los cuales dicen más al runtime del
código, por ejemplo la definición de cada tipo. Los metadatos son bibliotecas de tipo, entrada de
datos al registry, etc. Los metadatos son empaquetados directamente con el código ejecutable y
no en localidades separadas.
El IL y los metadatos son colocados en los archivos que extienden el formato PE (Portable
Executable) utilizado para archivos .exe y .dll, cuando se carga el archivo PE el runtime coloca y
extrae los metadatos y el IL de estos.
Cuando se compila una aplicación C# o cualquier aplicación escrita en un CLS, la aplicación es
compilada dentro del MSIL, además se compila dentro de las instrucciones nativas de CPU cuando
la aplicación es ejecutada por vez primera por elCLR.
El proceso es el siguiente:
Código fuente escrito en C#
El código fuente es compilado usando el compilador de C# (csc.exe) dentro de un EXE.
El compilador C# produce como salida el código MSIL y un manifiesto en una parte de
sólo lectura del EXE que tiene un encabezado estándar PE (Win32-Portable Executable).
Cuando el compilador produce o crea la salida también importa una función
llamada _CorExeMain del runtime .NET.
Cuando la aplicación es ejecutada, el sistema operativo carga el PE como una DLL
dependiente tal como la única que exporta la función _CorExeMain (mscoree.dll) justo
como lo hace con cualquier PE válido.
El sistema operativo carga y entonces salta al punto dentro del PE el cual es puesto ahí
por el compilador C#.
El sistema operativo obviamente no puede ejecutar el código MSIL, el punto de entrada
es un pequeña parte que salta a la función _CorExeMain en mscoree.dll.
La función _CorExeMain comienza la ejecución del código MSIL que fue colocado en el
PE.
Dado que el código MSIL no puede ser ejecutado directamente (porque no está un un
formato de máquina ejecutable) el CLR compila el MSIL usando un compilador just-in-time
(JIT o JITter) dentro de instrucciones CPU nativas tal como procesa el MSIL.
JITers
El managed code generado por C# es el código IL, aunque el código IL es empaquetado en un
formato de archivo PE válido, no es posible ejecutarlo sin convertirlo a un managed code nativo.
Cuando un tipo es cargado, el laoder crea y agrega un stub (pieza pequeña) a cada método del
tipo, así cuando el método es invocado por vez primera, el stub pasa el control al JIT.
El JIT compila el IL a código nativo y cambia el stub para que apunte al código nativo que está en
cache, así las subsecuentes invocaciones podrían ejecutar el código nativo.
El CLR incluye tres diferentes JITers que pueden ser usados para convertir MSIL en código nativo,
dependiendo de las circunstancias:
PreJIT (Generación de código Install-time), opera como un compilador tradicional,
aunque está basado sobre el compilador JIT principal, se ejecuta cuando un componente
NGWS es intalado y compila el código IL a managed code nativo.
La generación de código en tiempo de instalación compilará un ensamble completo
dentro de un código binario de CPU-especifico, tal como lo hace el compilador C++. Un
ensamble el código empaquetado que es enviado al compilador. La compilación se hace
14. en tiempo de instalación, cuando el usuario final es menos probable para notificar que
el ensamble esta siendo compilado-JIT.
La ventaja de la generación de código en tiempo de instalación, es que permite compilar
el ensamble completo justo una vez antes de ser ejecutado. Al ser compilado el
ensamble entero no hay preocupación referente al desempeño intermitente cada vez
que un método en el código es ejecutado por primera vez.
Al usar esta utilidad depende del tamaño del sistema y del ambiente de distribución.
JIT, compilador por default utilizado por el CLR, es un compilador optimizado, el cual
realiza el análisis del flujo de datos, administra el código nativo como salida.
El JITter es invocado en tiempo de ejecución.
EconoJIT, realiza una conversión muy veloz del IL a managed code nativo
También es un JITter en tiempo de ejecución, esta especialmente diseñado para
sistemas que tienen recursos limitados como memoria. La principal diferencia con un
JIIter regular es la incorporación de algunas invocaciones code pitching, que permiten
al EconoJIT descartar el código generado o compilado si el sistema comienza a
ejecutarse fuera de memoria, siendo el beneficio el reclamo de memoria. La desventaja
es que si el código es pitched (lanzado) es invocado otra vez por lo que debe ser
compilado de nuevo.
Es posible determinar que tipo de JIT esta siendo utilizado y cuanta memoria utiliza a través de
una pequeña utilidad llamada JIT Compiler Manager (jitman.exe), que reside en el
directorio bin del directorio de instalación del NGWS SDK.
Sistema de Tipo Unificado
El runtime de .NET hace más que dar al desarrollador un simple sistema de tipo unificado que es
usado a través de todos los lenguajes, también deja a los lenguajes escribir extensiones de
sistema tipo, agregando nuevos tipos que parezcan y actuen como tipos de sistemas built.in.
El Sistema Virtual de Objetos - VOS
Las reglas que se siguen cuando se declaran, utilizan y administran tipos son modeladas en el
Sistema Virtual de Objetos (Virtual Object System - VOS).
El VOS establece una plataforma que permite la integración de lenguajes y type safety.
La base de la arquitectura del runtime es la plataforma que puede describir en cuatro áreas:
VOS Type System, provee un sistema de tipos que intenta soportar la implementación
completa de una rango amplio de lenguajes de programación.
Metadata, describe y hace referencia a los tipos definidos por el VOS.
Common Language Specification - CLS, define el subconjunto de tipos del VOS. Si una
biblioteca de clases es soportada por las reglas del CLS, garantiza que la biblioteca de
clases pueda ser utilizada por los demás lenguajes que implementen el CLS.
Virtual Execution System - VES, es responsable de la carga y ejecución de los programas
que fueron escritos por el CLR.
VOS Type System
El VOS define tipos que describen valores y especifican un contrato en donde todos los valores de
tipo deben soportar. Existen dos entidades: valores y objetos.
Para un valor el tipo describe la representación de almacenamiento y las operaciones que puede
realizar.
Los objetos son más poderosos porque el tipo es almacenado explicitamente en su representación,
cada objeto tiene una identidad que lo distingue de los demás objetos.
Metadata
El compilador CLS toma el código fuente como entrada y produce código MSIL para el runtime
para compilar a través de los JITters y ejecutar. Además se mapea el código fuente a secuencias
de instrucciones MSIL, el compilador CLS tiene otra tarea importante: envolver metadatos dentro
del EXE resultante.
Los Metadatos son datos que describen datos.
15. Los metadatos son la colección de elementos programáticos que constituyen el EXE, como los
tipos declarados y los métodos implementados.
Estos metadatos son similares a los tipos de bibliotecas generadas con componentes COM
(Component Object Model).
La razón para usar metadatos es simple ya que permiten al runtime .NET conocer en tiempo de
ejecución que tipos podrían ser almacenados y que métodos podrían ser invocados. Esto permite
al runtime configurar apropiadamente el ambiente para mayor eficiencia al ejecutar la aplicación.
El significado por el cual estos metadatos son consultados es llamado reflection.
Los metadatos por cada objeto .NET registran toda la información que es requerida para usar el
objeto, con esta información el runtime .NET es capaz de resolver como crear objetos, invocar
funciones miembro o acceder a los datos de un objeto y el compilador puede utilizar la información
para encontrar que objetos están disponibles y como es utilizado un objeto. La información incluye
lo siguiente:
El nombre del objeto
Los nombres de todos los campos del objeto y sus tipos
Los nombres de todas las funciones miembro, incluyendo tipos parámetro y nombres
Los metadatos también permiten a otras herramientas acceder a la información detallada acerca
del código
Existe un proceso llamado reflection donde el código en tiempo de ejecución puede consultar los
metadatos para encontrar que objetos están disponibles y que funciones y campos están
presentes en la clase. La reflection está disponible para usuarios finales para determinar como
son los objetos, búsqueda de atributos o ejecutar métodos en los que los nombres no son
conocidos hasta el tiempo de ejecución.
Los metadatos son utilizados para varias tareas:
Para representar la información que el CLR utiliza para localizar y cargar clases.
Para sacar las instancias de las clases en memoria.
Para resolver la invocación de métodos.
Para traducir IL a código nativo.
Para forzar la seguridad.
El encargado de generar los metadatos es el compilador C#, al pasar el código a IL, emitiendo la
información binaria de los metadatos en un archivo PE.
La principal ventaja de la combinación de los metadatos con el código ejecutable es que la
información acerca de los tipos es persistente.
Una herramienta que toma ventaja de reflection es el ILDASM (Microsoft .NET Framework IL
Disassembler), el cual analiza la aplicación de metadatos fuente y entonces presenta información
acerca de la aplicación en la jerarquía del árbol.
Seguridad
La faceta más importante de cualquier ambiente de desarrollo de aplicaciones distribuidas es como
manejar la seguridad.
La seguridad comienza tan pronto como una clase es caragada por el CLR porque la clase loader
es parte del esquema de seguridad .NET, la seguridad relacionada a factores tales como reglas de
accesibilidad y requerimientos de consistencia son verificados.
Deployment
La llave para el Deployment de aplicaciones .NET es el concepto de (ensambles). Los assemblies
son paquetes simples de comportamiento semanticamente relacionados que son construidos como
un archivo individual o entidades de archivos múltiples.
La especificación de como deploy una aplicación podría variar ya que se puede tratar de un
desarrollo web o aplicación tradicional de escritorio.
El runtime .NET mantiene el rastreo delos archivos y de las versiones de los archivos asociados
con una aplicación. Cualquier aplicación que es instalada es automáticamente asociada con los
archivos que son parte de ese ensamble.
16. Si una aplicación de instalación intenta sobre escribir un archivo necesario para otra aplicación, el
runtime .NET es lo bastante inteligente para permitir que la aplicación de instalación, instale los
archivos necesarios pero el CLR no elimina las versiones previas de el archivo porque todavía son
requeridas por la primer aplicación.
Interoperabilidad con código no administrado
El código no administrado no tiene las ventajas que tiene el código administrado, como recolección
de basura, sistema de tipo unificado y metadatos.
Código administrado invocando funciones DLL no administradas, cuando la aplicación
necesita una interfaz para una DLL en C y la empresa que escribe la DLL no adopta .NET
será necesario invocar esa DLL desde una aplicación .NET.
Código administrado usando componentes COM, es posible lograr esto creando un
wrapper .NET para el componente COM, así que el cliente administrado trabaja con
clases .NET
Código no administrado usando servicios .NET, cuando se desea acceder a .NET desde
código no administrado.
Common Language Specification - CLS
Es un conjunto de reglas que un lenguaje debe adherir para crear aplicaciones .NET que se
ejecutan en el CLR.
Un concepto importante relacionado a la CLR es el código administrado, el código administrado es
justo el código que esta ejecutandose bajo el auspicio de la CLR y por consiguiente comienza a
ser controlado por el CLR.
El CLS define un subconjunto de tipos del VOS, si una biblioteca de clases sigue las reglas del CLS
esta garantizando ser utilizada por clientes de otro lenguaje de programación que también se
adhieren a la CLS.
El CLS se refiere a la interoperabilidad entre lenguajes, por lo que es necesario seguir los tipos y
características del CLS, para ello es necesario conocer los tipos primitivos, arreglos, tipos,
miembros tipo, métodos, campos, propiedades, enumeraciones, excepciones, interfaces, eventos,
atributos personalizables, delegados, identificadores, etc. que la propia especicicación define.
Virtual Execution System - VES
El Sistema Virtual de Ejecución implementa la VOS y se crea implementando un motor de
ejecución (Execution EngineEE). Los componentes de la VES son:
Lenguaje Intermedio (Intermediate Language - IL), diseñado para ser facilmente
traducido a una amplia gama de lenguajes, por lo que el compilador C# es capaz de
generar el lenguaje intermedio.
Carga del Código Administrado (Loading Managed Code), resuelve nombres, obtiene
clases de la memoria, crea stubs que son necesarios para la compilación JIT. La class
loader forza la seguridad.
Conversión de IL a Código Nativo via JIT, el código del lenguaje intermedio no esta
diseñado como un interprete tradicional bytecode o árbol de código, la conversión del
lenguaje intermedio es realmente una compilación.
Carga de Metadatos, se encarga de checar la seguridad de tipos y la integridad de los
métodos.
Recolector de Basura y Manejo de Excepciones (Garbage Collection), el codigo
administrado premite rastrear el apilado en el runtime, para que el runtime entienda el
apilado individual de frames un código administrado tiene que ser proporcionado por el
JITer o por el compilador.
Servicios de debugging, estos servicios dependeran de la información producida por el
compilador del lenguaje fuente y se emiten dos mapas, un mapa del lenguaje fuente de
la construcción de direcciones en el flujo de instrucciones y un mapa de las direcciones
de las localidades en el apilado de frames.
Administración de Hilos, el VES proprorciona este servicio al código administrado.
17. Tipos de Datos
C# soporta el conjunto de tipos de datos usual, para cada tipo de dato que C# soporta, existe
una correspondencia tipo de lenguaje común en tiempo de ejecución .NET subyacente.
Todos los tipos runtime pueden encontrarse en el namespace System del lenguaje común en
tiempo de ejecución .NET.
Tipo Bytes Tipo runtime Descripción
byte 1 Byte Unsigned byte
sbyte 1 SByte Signed byte
short 2 Int16 Signed short
ushort 2 UInt16 Unsigned short
int 4 Int32 Signed integer
uint 4 UInt32 Unsigned int
long 8 Int64 Signed big integer
ulong 8 UInt64 Unsigned big integer
float 4 Single Floating point number
double 8 double Double-precision floating point number
decimal 8 Decimal Fixed-precision number
string String Unicode string
char Char Unicode character
bool Boolean Boolean value
Los tipos de datos son separados en value types y reference types. Los value types son asignados
en estructuras de pilas o en línea. Los reference types son asignados al aglomerado.
Las referencias y tipos de valores son derivados de la última clase base objet, de esta manera
en caso de que un tipo de valor necesite actuar como un object una envoltura hace que el tipo
de valor parezca una referencia asignandolo al aglomerado, y los tipos de valores son copiados a
estos. La envoltura es marcada por lo que el sistema conoce que contiene por ejemplo int, a
este proceso se le conoce como boxing y el proceso de reversa se le conoce como unboxing
La palabra reservada class es empleada para declarar un tipo referencia (heap allocated), y la
palabra reservada structes empleada para declarar un tipo valor, una estructura es utilizada
para objetos ligeros que necesitan actuar como tiposbuilt-in, las clases son utilizadas en cualquier
otro caso.
Por ejemplo un tipo int es un valor tipo y un tipo string es un tipo referencias, esto trabajaria
así:
int i = 2005;
string s = "Septiembre";
i 2005
s o-----
------------
>
Septiembre
Constantes y Campos Solo Lectura
En C# los valores pueden ser definidos como constantes y para que un valor sea constante su
valor debe ser algo que pueda ser escrito como una constante.
public const string sDominio = "informatique.com.mx";
La restricción de tipos constantes es que son conocibles en tiempo de compilación, en vez de ello
es posible utilizar el modificador readonly el cual está diseñado para aquellas situaciones en
donde las constantes tienen restricción.
18. Aplicando el modificador readonly un valor puede ser establecido en el constructor o en una
inicialización pero no puede ser modificado después.
Ejemplo Hello World!
El código C# puede ser escrito en cualquier editor, también puede escribirse con Visual Studio 7.
El código C# debe almacenarse en un archivo con extensión .cs
Para compilar el código C# es necesario tener instalado la Plataforma .NET que incluye el
compilador C#, puede buscar el ejecutable en la ruta:
C:WINDOWSMicrosoft.NETFrameworkv1.1.4322csc.exe
Asegurese de tener esta ruta en el path para poder ejecutar el compilador desde cualquier
ubicación.
Para compilar su archivo .cs es necesario abrir la consola (DOS) y escribir el comando cs seguido
del nombre de su archivo por ejemplo:
cd helloworld.cs
La salida exitosa de la compilación podría ser así:
Microsoft (R) Visual C# .NET Compiler version 7.10.6001.4
for Microsoft (R) .NET Framework version 1.1.4322
Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.
Si existe algún error el compilador lo notificará. El archivo es compilado y ligado
a helloworld.exe, (el archivo .exe generado tendrá el nombre del archivo fuente) para ejecutar
el programa sólo es necesario escribir el nombre del archivo ejecutable.
Algo interesante es que es posible especificar el nombre del archivo ejecutable a través de
un switch:
csc /out:nuevoNombre.exe nombreArchivoFuente.cs
El siguiente es el ejemplo típico de los lenguajes de programación:
class HelloWorld{
public static void Main(){
System.Console.WriteLine("Hello World");
}
}
El método Main debe estar contenido en la clase y escrito con la primer letra en mayúscula. El
tipo de este método puede ser void o int. También este método puede especificar argumentos:
public static void Main(string[] args)
System es el ámbito del namespace en el cual el objeto Console está contenido.
Es posible importar el namespace en las aplicaciones indicandolo al inicio del código con la palabra
reservada using que es una directiva para el namespace System. Existen más namespaces en
la Plataforma .NET
using System;
Ejemplo Args
Examinemos el siguiente ejemplo, el cual recibe los argumentos con los que el componente fue
invocado:
Ejemplo de Args con for:
using System;
class Args{
public static void Main(string[] args){
Console.WriteLine("Argumentos : {0}",
args.Length);
for(int itera = 0; itera < args.Length; itera++)
19. Console.WriteLine("Argumento {0} : {1}",
itera, args[itera]);
}
}
Ejemplo de Args con foreach:
using System;
class App{
public static void Main(string[] args){
foreach(string input in args){
Console.WriteLine(input);
}
}
}
using System;, define el namespace System, el cual contiene entre otras la
clase Console la cual es utilizada para comunicarse con la línea de comandos.
using permite al usuario omitir el namespace al utilizar el tipo al que es referenciado
en este caso System, por lo que en vez de escribir:
System.Console.WriteLine();
Solamente se escribe:
Console.WriteLine();
using no puede ser utilizado con un nombre de clase por lo que no es permitido
escribir using System.Console
class Args, Al no existir las funciones globales en C#, se declara una clase
llamada Args.
public static void Main(string[] args), La clase Args contiene una función o
método Main(), el cual sirve como punto de partida de la ejecución del componente,
este método puede o no ser declarado con argumentos, en este caso es fundamental
declarlos porque deseamos precisamente leer y escribir estos argumentos
proporcionados al invocar el componente.
Al ser un método de arranque debe ser declarado con el modificador static porque no
está asociado con una instancia de un objeto.
El método indica que recibe un arreglo de tipo string llamado args
Console.WriteLine("Argumentos : {0}", args.Length);, invoca el
método WriteLine de la clase Console para escribir en la línea de comando lo que se
indica entre los paréntesis.
La primer parte de lo que se encierra entre paréntesis es un string donde es necesario
destacar que{0}, es una notación que indica entre llaves un índice que hace referencia
a una variable asociada a este, en este caso asociada con args.Length
args.Length, Length es un método de la clase args el cual obtiene el número de
elementos que contiene este arreglo.
El ciclo for comienza una iteración desde 0 hasta el número de elementos que contiene
el arreglo args.Length, por cada elemento contenido en el arreglo escribe en la línea
de comandos lo que se indica en ("Argumento {0} : {1}", itera, args[itera]) que
como mencionamos anteriormente {0} hace referencia al orden en que las variables
serán escritas, en este caso corresponde al iterador y {1} corresponde a args[itera],
lo cual indica obtener el elemento en cuestión del arreglo args.
Para compilar el componente utilice csc Args.cs
Para ejecutar el componente sin parámetros escriba en la línea de comando: csc Args
Salida : Argumentos : 0
Para ejecutar el componente con parámetros escriba en la línea de comando:
20. csc Args p1 p2 p3 p4 p5 p6 ... pn
Por ejemplo: args http : www . informatique . com . mx
Salida :
Argumentos : 8
Argumento 0 : http
Argumento 1 : :
Argumento 2 : www
Argumento 3 : .
Argumento 4 : informatique
Argumento 5 : .
Argumento 6 : com
Argumento 7 : .
Argumento 8 : mx
Ejemplo Input/Output
Es posible leer datos de la consola utilizando el método ReadLine y es posible mostrarlos
utilizando el método Write oWriteLine del objeto Console:
using System;
class inOut{
public static void Main(){
Console.Write("Fecha de Nacimiento: ");
String strFecNac = Console.ReadLine();
Console.WriteLine("FecNac = " + strFecNac);
}
}
Note que importar la directiva System hace posible omitir escribir el namespace, de esta forma
sólo es necesario escribir el nombre del objeto seguido del nombre del método.
Ejemplo String Format
Es posible dar formato a la salida de datos a un tipo string, utilizando la sintaxis {número} donde
número es reemplazado por la variable correspondiente:
using System;
class strFormat{
public static void Main(){
Console.Write("Nombre: ");
String strNombre = Console.ReadLine();
Console.Write("Edad: ");
String strEdad = Console.ReadLine();
Console.Write("Teléfono: ");
String strTel = Console.ReadLine();
Console.Write("Dirección:
");
String strDir = Console.ReadLine();
Console.WriteLine("Datos: {0} {1} {2} {3}",
strNombre
, intEdad, strTel, strDir);
}
}
Función Main
21. Es posible incluir una función estática en la clase para poder probarla, en C# esta función estática
puede ser escrita comoMain() e indica el inicio de la ejecución de un programa:
using System;
class App{
public static void Main(){
Console.WriteLine("Hello world!");
}
}
El ejemplo anterior define a la función Main como void lo cual indica que no regresa un valor,
pero es posible indicar que si regrese un valor escribiendo el tipo de la función como int por
ejemplo, que indica que regresa un valor de tipo entero:
using System;
class App{
public static int Main(){
Console.WriteLine("Hello world!");
return(1);
}
}
También es posible que la función Main reciba parámetros de la línea de comandos, para ello es
necesario especificar unarreglo de strings como parámetro:
using System;
class App{
public static void Main(string[] args){
foreach(string input in args){
Console.WriteLine(input);
}
}
}
Múltiples Funciones Main
Es posible que existan en una aplicación varias clases que contengan la función Main() y por ello
al compilar se indicará un error.
Para evitar el error o indicar que función Main() de que clase deseamos que se ejecute, es
necesario utilizar el siguienteswitch al compilar:
/main:nombreClase
Por ejemplo
using System;
class ClaseA{
public static void Main(){
Console.WriteLine("Main de la clase A");
}
}
class ClaseB{
public static void Main(){
Console.WriteLine("Main de la clase B");
}
}
class ClaseC{
public static void Main(){
Console.WriteLine("Main de la clase C");
}
}
Al compilar utilice : csc multiplesmain.cs /main:ClaseB
22. Salida: Main de la clase B
Preprocesamiento
Lo más importante en este punto es que en C# no existe el preprocesador, el motivo por el cual
no existe es para simplificar la estructura de compilación además de que no hay necesidad de
escribir un archivo de encabezados por separado y mantener en sincronia la implementación,
cuando los archivos fuente C# son compilados el orden de la compilación de archivos individuales
no es importante y es equivalente a un archivo de gran tamaño.
Un identificador es el nombre que es usado para algún elemento de un programa como una
variable o función y deben tener una letra o guión bajo como primer caracter.
C# soporta las siguientes directivas de preprocesamiento:
Tamaño Valor
#define
Define un identificador, los identificadores también pueden ser
definidos via la línea de comando
#undef Elimina la definición de un identificador
#if El código de la sección es compilado si la expresión es verdadera
#elif
Constructor Else-if, si la directiva anterior no se cumplio y si la
expresión es verdadera el código de la sección es compilado
#else
Si la directiva anterior no se cumplio el código de la sección es
compilado
#endif Marca el final de la sección
Los identificadores deben preceder a cualquier código real.
Es posible utilizar los siguientes operadores en expresiones preprocesador:
!
==
!=
&&
||
Es posible utilizar paréntesis para agrupar expresiones.
Comentarios
Es posible comentar el código, para ello existen dos modalidades:
//, que se utiliza para comentar una línea, es decir, todo lo que sigue a // es ignorado.
/* */, que se utiliza para comentar segmentos de código.
Value Types
Una variable contiene un valor de cierto tipo, C# forza a inicializar las variables antes de utilizarlas
en una operación.
Cuando se asigna un valor a un value type el valor actual es copiado a diferencia de los reference
types lo que se copia es la referencia actual no el valor.
C# agrupa los value types en:
Tipos Simples
Tipos Estructura
Tipos Enumeración
Tipos Simples
Los Tipos Simples de C# comparten características como las de alias con los tipos de sistema
de .NET, expresiones constantes consisten de Tipos Simples evaluados solamente en tiempo de
compilación no en tiempo de ejecución y los Tipos Simples pueden ser inicializados con literales.
Los Tipos Simples de C# se agrupan en:
Integral
Representa valores enteros y existen nueve tipos integral en C#:
23. Tipo Tamaño Valor
sbyte Entero con signo 8 bit -128 a 127
byte Entero sin signo 8 bit 0 a 255
short Entero con signo 16 bit -32,768 a 32,767
ushort Entero sin signo 16 bit 0 a 65,535
int Entero con signo 32 bit -2,147,483,648 a 2,147,483,647
uint Entero sin signo 32 bit 0 a 4,294,967,295
long Entero con signo 64 bit -9,223,372,036,854,775,808 a -9,223,372,036,854,775,807
ulong Entero sin signo 64 bit 0 a 18,446,744,073,709,551,615
Bool
Representa valores booleanos verdadero y falso, por lo que es posible asignar a una
variable un valor booleano o el resultado de una expresión:
bool bContinuar = (a > b);
En C# el valor verdadero no es posible representarlo con algún valor diferente de cero,
no hay una conversión entre el tipo integral a bool que force esta conversión.
Char
Representa un caracter Unicode de 16 bit de tamaño, por ejemplo:
char cSexo = 'M';
También es posible asignar un valor hexadecimal utilizando la secuencia de escape x o
un valor Unicode con lasecuencia de escape u:
char cHexadecimal = 'x0068';
char cUnicode = 'u0068';
No existen conversiones implicitas de char a otro tipo de datos disponible, esto significa
por ejemplo que tratar de convertir una variable char a un tipo de dato integral no es
posible en C#, por lo que se tendrá que hacer un cast explicito:
char cCaracter = (char)65;
int nNumero = (int)'A';
Floating Point
Representan dos tipos de datos, flotantes (float) y dobles (double):
Tipo Valor
float 1.5x10-45 a 3.4x1038 con una precisión de 7 dígitos
double 5.0x10-324 a 1.7x10308 con una precisión de 15-16 dígitos
Al realizar operaciones con Floating Point pueden producirse los siguientes valores:
Cero positivo y negativo
Infinito positivo y negativo
NaN, Not-a-Number
Nota: Si una expresión un valor es de tipo Floating Point todos los otros valores son
convertidos a tiposFloating Point antes de realizar el cálculo.
Decimal
Representa un tipo de alta precisión de 128 bit el cual es posible utilizarlo para calculos
financieros y monetarios. Los valores posibles comprenden los rangos 1.0x10-28 a
7.9x1028 con una precisión de 28 a 29 dígitos.
24. No hay conversiones implicitas entre decimales y dobles, se podría generar
un overflow o perder precisión, por lo que es necesario una conversión explícita con
un cast.
Cuando se define una variable y se le asigna un valor se utiliza el sufijo m para denotar
que es un valor decimal:
decimal decDecimal = 1.0m
Si se omite la letra m la variable podría ser tratada como double por el compilador antes
de ser asignado.
Tipos Estructura
Un tipo struct puede declarar constructores, constantes, campos, métodos, propiedades,
índices, operadores y tipos anidados. Las estructuras actuan de manera similar a una clase y con
mayores restricciones, por ejemplo no pueden heredar de cualquier otro tipo, ni tampoco otra
clase puede heredar de una estructura.
Las estructuras deberían ser utilizadas sólo para tipos que son realmente una pieza de datos.
La diferencia entre struct y class en C# es que struct es un value type y class es
una reference type.
La principal idea de utilizar struct es para crear objetos ligeros como Point, FileInfo, etc.,
de esta manera se conserva memoria porque no hay referencias adicionales que son creadas como
se necesiten por objetos clase.
using System;
struct IP{
public byte b1,b2,b3,b4;
}
class ip{
public static void Main(){
IP miIP;
miIP.b1 = 192;
miIP.b2 = 168;
miIP.b3 = 1;
miIP.b4 = 101;
Console.Write("{0}.{1}.", miIP.b1,miIP.b2);
Console.Write("{0}.{1}", miIP.b3,miIP.b4);
}
}
Tipos Enumeración
Es posible establecer un conjunto de constantes relacionadas, por default los elementos de una
enumeración son de tipoint donde el primer elemento tiene el valor 0 y cada elemento
subsecuente se incrementa en 1. Es posible establecer el valor del primer elemento simplemente
asignando a este el valor deseado, así como es posible especificar el tipo de dato de los valores
contenidos especificandolo después del nombre de la enumeración aunque están restringidos a
los tipos:long, int, short y byte.
Sintaxis:
enum NombreEnumeraciones{
constante1,
constante2,
constante3,
.
.
constanteN
}
Ejemplo:
using System;
public class Enumeracion {
25. enum enumDias {Sabado, Domingo, Lunes, Martes, Miércoles,
Jueves, Viernes };
enum enumMeses
{Enero,Febrero,Marzo,Abril,Mayo,Junio,Julio,Agosto,Septiembre,_
Octubre,Noviembre,Diciembre};
enum enumFecha {Dia = 21, Mes = 9, Año = 1971};
public static void Main() {
Type eDias = typeof(enumDias);
Type eMeses = typeof(enumMeses);
Type eFecha = typeof(enumMeses);
Console.WriteLine("Los días de la semana, y su valor
correspondiente en la enumeración es:");
foreach ( string s in Enum.GetNames(eDias) )
Console.WriteLine( "{0,-11}= {1}", s, Enum.Format( eDias,
Enum.Parse(eDias, s), "d"));
Console.WriteLine();
Console.WriteLine("Los meses del año, y su valor correspondiente
en la enumeración es:");
foreach ( string s in Enum.GetNames(eMeses) )
Console.WriteLine( "{0,-11}= {1}", s, Enum.Format(eMeses,
Enum.Parse(eMeses, s), "d"));
}
}
Tipos Base
Los Tipos Base para las enumeraciones se especifican listando el tipo base después del nombre
de la enumeración:
enum eDias : int{
Lunes,
Martes,
Miércoles,
Jueves,
Viernes
};
Los tipos base válidos para las enumeraciones
son: byte, sbyte, short, ushort, int, uint, long y ulong.
Si el tipo base no es especificado, el tipo base por default es int.
Tipos Referencia
Es contraste a value types los reference types no almacenan el dato actual que representan,
porque almacenan una referencia al dato actual.
Los reference types que C# utiliza son:
Tipo Objeto
Tipo Clase
Interfaces
Delegados
Tipo string
Arreglos
Tipo Objeto
26. El Tipo Objeto es la Clase Base de todos los tipos, al ser la clase base de todos los tipos es posible
asignarle valores de cualquier tipo.
El Tipo Objeto es utilizado cuando el value type esta boxed, es decir, que está disponible como un
objeto.
Tipo Clase
El Tipo Clase contiene datos miembro, funciones miembro y tipos anidados. Los datos miembro
son constantes, campos y eventos. Las funciones miembro incluyen métodos, propiedades,
índices, operadores, constructores y destructores.
Interfaces
Una interface declara un tipo referencia que tiene sólo miembros abstractos. Sólo existe la firma
pero no tiene implementado todo el código, por lo que no es posible instanciar una interface, sólo
un objeto que deriva de la interface. Para crear una interface se emplea la palabra
reservada interface:
using System;
interface Iuno{
void AccionUno();
}
class Implementa : Iuno{
public void AccionUno(){
Console.WriteLine("Acción uno...");
}
}
class App{
public static void Main(){
Implementa I = new Implementa();
I.AccionUno();
}
}
Es posible definir métodos, propiedades e índices en una interface, cuando se define una Clase es
posible derivar de múltiples interfaces, mientras que al definir una interface sólo es posible derivar
de sólo una clase.
Las interfaces están estrechamente relacionadas a clases abstractas, se parecen a una clase
abstracta que tiene todos sus miembros abstractos.
Cuando un objeto implementa una interface, una referencia a la interface puede ser obtenida por
un cast de la interface.
Una clase puede implementar más de una interface.
class NombreClase : InterfaceA, InterfaceB{}
Existe una técnica llamada Implementación de la Interface Explícita y se utiliza para resolver
colisiones con nombres de métodos iguales entre interfaces:
using System;
interface Iuno{
void AccionUno();
}
interface Idos{
void AccionUno();
}
class Implementa : Iuno, Idos{
void Iuno.AccionUno(){
Console.WriteLine("Colisión resuelta con el nombre
del método Iuno");
}
void Idos.AccionUno(){
27. Console.WriteLine("Colisión resuelta con el nombre
del método Idos");
}
}
class App{
public static void Main(){
Implementa I = new Implementa();
Iuno uno = (Iuno) I;
uno.AccionUno();
Idos dos = (Idos) I;
dos.AccionUno();
}
}
Es posible ocultar al usuario de la clase la implementación de una interfaz, así como también es
posible crear interfaces basadas de otras interfaces.
Delegados
Los delegados son similares a las interfaces, especifican un contratado entre un caller y
un implementer (implementador).
Un delegado especifica la forma de una función en vez de especificar toda una interface.
Las interfaces se crean en tiempo de compilación y los delegados son creados en tiempo de
ejecución.
Un delegado encapsula un método con cierta firma, básicamente un delegado es un type-safe
y secure version. Un delegado es una implementación de function pointers orientada a objetos
y son utilizados en muchas situaciones donde un componente necesita volver a invocar el
componente que lo esta usando.
Es posible encapsular métodos estáticos e instancias en una instancia delegado.
El principal uso de los delegados es con los eventos no con las clases.
La especificación del delegado determina la forma de la función y crea una instancia del delegado,
se usa la función que coincide con la forma.
Los delegados al ser de naturaleza dinámica se utilizan cuando el usuario desea cambiar el
comportamiento, por ejemplo si deseamos que una clase Ordenamiento soporte diferentes
métodos de ordenación, la ordenación podría ser controlada en base a un delegado que defina la
función de comparación.
Nota los delegados siempre son creados aún si no son usados, pero los delegados podrían ser
creados al vuelo si se reemplazan las funciones estáticas por propiedades, entonces unicamente
se crea el delegado solo si se utiliza la propiedad.
Tipo string
El Tipo string se utiliza para manipular datos string. La clase string deriva directamente
de object y no es posible derivarla.
Todos los strings en C# son instancias del tipo System.String en el CLR.
string es un alias para la clase predefinida System.String y su uso es muy sencillo:
string sWebSite = "http://www.informatique.com.mx";
Para acceder a un caracter, simplemente acceda a su índice:
sWebSite[11];
Es posible hacer un barrido de los caracteres que componen el string utilizando la
propiedad Length que poseen los arreglos y porque es posible acceder a estos tratando
al string como un arreglo:
using System;
class App{
public static void Main(){
string sWebSite = "http://www.informatique.com.mx";
28. Console.WriteLine("sWebSite contiene : " + sWebSite.Length
+ " caracteres");
for(int iElemento = 0; iElemento < sWebSite.Length;
iElemento++){
Console.WriteLine("Elemento " + iElemento + " : "
+ sWebSite[iElemento]);
}
}
}
Es posible concatenar strings utilizando el operador +.
Si requiere comparar strings por igualdad utilice el operador de comparación ==
Aunque string es un reference type la comparación se realiza comparando los valores no las
referencias.
La clase String es un ejemplo de tipo inmutable, es decir, que los caracteres contenidos en el
string no puede ser modificados por los usuarios del string, todas las operaciones que son
realizadas por la clase String regresan una versión modificada del string en vez de modificar la
instancia en la cual se invoco el método.
La clase String soporta los sisguientes métodos de comparación y búsqueda:
Método Descripción
Compare() Compara dos strings.
CompareOrdinal() Compara dos regiones de strings utilizando una comparación ordinal
CompareTo() Compara la instancia actual con otra instancia.
EndsWith() Determina cuando un substring existe al final de un string
StartsWith() Determina cuando un substring existe al principio de un string.
IndexOf() Regresa la posición de la primer ocurrencia de un substring
LastIndexOf() Regresa la posición de la última ocurrencia de un substring
Concat()
Concatena dos o más strings u objetos, si se pasan objetos la
función ToString es invocada
CopyTo()
Copia un número específico de caracteres de una ubicación del string
dentro del arreglo
Insert()
Regresa un nuevo string con un substring insertado en la ubicación
específica
Join()
Une un arreglo de strings junto con un separador entre cada elemento
del arreglo
PadLeft() Alinea a la izquierda un string
PadRight() Alinea a la derecha un string
Remove() Elimina caracteres de un string
Replace()
Reemplaza todas las instancias de un caracter con caracteres
diferentes
Split()
Crea un arreglo de strings dividiendo un string en cualquier ocurrencia
de uno o más caracteres
Substring() Extrae un substring de un string
ToLower() regresa una versión de un string en minúsculas
ToUpper() regresa una versión de un string en mayúsculas
Trim() Elimina espacios en blanco de un string
TrimEnd() Elimina un string de caracteres al final de un string
TrimStart() Elimina un string de caracteres al inicio de un string
29. object.ToString(), convierte un objeto a una representación
string. String.Format() puede ser utilizada para crear un string basado en los valores de otro
string.
La clase StringBuilder soporta las siguientes propiedades y métodos:
Propiedad Descripción
Capacity
Recupera o establece el número de caracteres
que StringBuilder puede contener
[]
Índice StringBuilder utilizado para obtener o establecer un caracter en
la posición específica
Length Recupera o establece la longitud
MaxCapacity Recupera la capacidad máxima del StringBuilder
Método Descripción
Append() Agrega la representación string de un objeto
AppendFormat()
Agrega la representación string de un objeto, utilizando un formato
específico para el objeto
EnsureCapacity()
Asegura que StringBuilder tiene suficiente espacio para un
número de caracteres específico
Insert()
Inserta la representación string de un objeto específico en una
posición específica
Remove() Elimina los caracteres específicos
Replace()
Reemplaza todas las instancias de un caractes con un nuevo
caracter
Arreglos
Un arreglo contiene variables a las cuales se accede a través de índices, todas las variables
contenidas en el arreglo son referidos como elementos los cuales deben ser del mismo tipo, por
lo que el tipo del arreglo.
Los arreglos en C# son referencias a objetos. Un arreglo value type no contiene instancias boxed.
El valor inicial de un arreglo es null, un arreglo de objetos es creado utilizando new.
Cuando un arreglo es creado inicialmente contiene los valores por default para los tipos que este
contendrá.
Sintaxis:
tipo[] identificador;
Note que para definir un arreglo se utilizan los corchetes [] después del tipo del arreglo.
Ejemplo:
string[] aPersonas;
Es posible inicializar un arreglo al momento de crearlo:
string[] asPersonas = new string[] {"Tim Berners-Lee","Brendan
Eich","Dennis Ritchie","James Gosling"};
Durante la inicialización es posible omitir new tipo[x] y el compilador podría determinar el
tamaño de almacenamiento para el arreglo del número de items en la lista de inicialización:
string[] asPersonas = {"Tim Berners-Lee","Brendan Eich","Dennis
Ritchie","James Gosling"};
Cada elemento de un arreglo de ints es un int con el valor 0:
int[] aiNumeros = new int[5];
Cada elemento de un arreglo de strings es un string con el valor null:
string[] asNombres = new string[5];
30. La dimensión del arreglo puede ser simple o multidimensional, donde cada dimensión comienza
con el índice 0, si requiere hacer un barrido de todos los elementos del arreglo, comience a partir
del índice 0 hasta la longitud del arreglo menos uno (nombreArreglo.Length - 1 o nIndice
< nombreArreglo.Length);
using System;
class Arreglo{
static public void Main(){
string[] aNombres = {"Hugo","Paco","Luis"};
Console.WriteLine(aNombres[0]);
Console.WriteLine(aNombres[1]);
Console.WriteLine(aNombres[2]);
}
}
Otra alternativa al ejemplo anterior es:
int[] aiNumeros = new int[3];
aiNumeros[0] = 4;
aiNumeros[1] = 33;
aiNumeros[2] = 43;
Al declarar el arreglo especifique solamente el número de elementos que este contendrá. utilice
la palabre reservada newseguido del tipo y entre corchetes el número de elementos que
contendrá.
Es posible ordernar y buscar los elementos de un arreglo gracias a que los arreglos en C# están
basados en el tipoSystem.Array del runtime NET. El método Sort() podría ordenar los
elementos de un arreglo, los métodos IndexOf() yLastIndexOf() y BinarySearch podrían
buscar elementos en un arreglo. El método Reverse podría invertir el orden de los elementos de
un arreglo.
Arreglos Multidimensionales
Los Arreglos Multidimensionales son aquellos que tienen más de una dimensión.
Sintaxis:
tipo[,] identificador;
Ejemplo:
string[,] asBidimensional = new string[2, 4];
Para definir un arreglo multidimensional, simplemente defina arreglos como elementos del
arreglo:
string[,] asMulti = {{"a","1"},{"b","2"},{"c","3"}};
Ejemplo:
using System;
class App{
public static void Main(){
string[] asPersonas = {"Tim Berners-Lee", "Brendan Eich", "Dennis
M. Ritchie", "James Gosling"};
Console.WriteLine("Longitud del arreglo asPersonas : " +
asPersonas.Length);
int[] aiNumeros = new int[3] {1, 2, 3};
Console.WriteLine("Longitud del arreglo aiNumeros : " +
aiNumeros.Length);
//Define 4 arreglos de 2 dimensiones
int iRenglon = 4;
int iColumna = 2;
string[,] asBidimensional = new string[iRenglon, iColumna];
// 4 renglones * 2 columnas = 8 Elementos
31. asBidimensional[0,0] = "00";
asBidimensional[0,1] = "01";
asBidimensional[1,0] = "10";
asBidimensional[1,1] = "11";
asBidimensional[2,0] = "20";
asBidimensional[2,1] = "21";
asBidimensional[3,0] = "30";
asBidimensional[3,1] = "31";
Console.WriteLine("Longitud del arreglo asBidimensional : " +
asBidimensional.Length);
int[,] aiBidimensional = { {11,22}, {33,44}, {55,66}, {77,88} };
for(int i = 0; i < iRenglon; i++){
for(int j = 0; j < iColumna; j++){
Console.WriteLine("Dimensión " + i + " elemento " + j + "
: " + aiBidimensional[i,j]);
}
}
Console.WriteLine("Longitud del arreglo aiBidimensional : " +
aiBidimensional.Length);
}
}
Arreglo de Arreglos
Un Arreglo de Arreglos es también conocido como jagged array porque no tiene que ser rígido.
Por ejemplo:
int[][] aiIDs = new int[3][];
Este ejemplo define un arreglo de arreglo de tipo int donde su dimensión es 3 elementos, donde
estos elementos son arreglos.
Arreglos de Objetos
Un arreglo de objetos es creado utilizando new.
Es posible declarar y manipular arreglos de objetos de la siguiente manera:
using System;
public class Persona{
//Propiedades
public string sNombre;
public int iEdad;
//Constructor
public Persona(string sNombre, int iEdad){
this.sNombre = sNombre;
this.iEdad = iEdad;
}
//Métodos
public string Tipo(){
return "Persona";
}
}
//Herencia Simple
public class Empleado : Persona{
32. public Empleado(string sNombre, int iEdad):base(sNombre,
iEdad){}
public new string Tipo(){
return "Empleado";
}
}
class App{
//Aplicación
public static void Main(){
Persona Mexicano = new Persona("Gerado Ángeles Nava", 33);
Console.WriteLine("Mexicano.sNombre : " +
Mexicano.sNombre);
Console.WriteLine("Mexicano.iEdad : " + Mexicano.iEdad);
Console.WriteLine("Mexicano.Tipo : " + Mexicano.Tipo());
Console.WriteLine("--- Arreglo de Objetos ---");
Empleado[] aProgramadores = new Empleado[2];
aProgramadores[0] = new Empleado("Bill Gates", 50);
aProgramadores[1] = new Empleado("Eric S. Raymond", 60);
for(int i = 0; i < aProgramadores.Length; i++){
Console.WriteLine("aProgramadores["+i+"].sNombre : " +
aProgramadores[i].sNombre);
Console.WriteLine("aProgramadores[" + i + "].iEdad : "
+ aProgramadores[i].iEdad);
Console.WriteLine("aProgramadores[" + i + "].Tipo : " +
aProgramadores[i].Tipo());
}
}
}
Conversión de Arreglos
Una conversión implícita es posible si los arreglos tienen el mismo número de dimensiones, si los
elementos de un arreglo tienen una conversión de referencia implícita para los tipos de elementos
del otro arreglo y ambos arreglos son tipos referencia.
Una conversión explícita tiene los mismos requerimientos de una conversión implícita excepto que
los elementos de un arreglo deben ser convertibles explícitamente a los tipos de elementos del
otro arreglo.
Clase Array
La clase Array provee entre otras, funciones de búsqueda y ordenamiento.
En el siguiente ejemplo se muestra como es ordenado un arreglo de strings:
using System;
class App{
public static void Main(){
string[] aLenguajes = {"Java", "Pascal", "ActionScript",
"PHP", "C#", "SQL",
"JavaScript", "C", "Java", "Prolog", "Visual Basic",
"C++"};
Array.Sort(aLenguajes);
for(int elemento = 0; elemento < aLenguajes.Length;
elemento++)
Console.WriteLine("Elemento [" + elemento + "] = "
+ aLenguajes[elemento]);
}
}
Salida:
33. Elemento [0] = ActionScript
Elemento [1] = C
Elemento [2] = C#
Elemento [3] = C++
Elemento [4] = Java
Elemento [5] = Java
Elemento [6] = JavaScript
Elemento [7] = Pascal
Elemento [8] = PHP
Elemento [9] = Prolog
Elemento [10] = SQL
Elemento [11] = Visual Basic
La función Sort(), también se puede utilizar con números:
using System;
class App{
public static void Main(){
double[] aNumeros = {8.7, 6.9, -6.5, 4.2, -102.09, 1.9,
0.01, -0.002, 99.87};
Array.Sort(aNumeros);
for(int elemento = 0; elemento < aNumeros.Length;
elemento++)
Console.WriteLine("Elemento [" + elemento + "] = "
+ aNumeros[elemento]);
}
}
Salida:
Elemento [0] = -102.09
Elemento [1] = -6.5
Elemento [2] = -0.002
Elemento [3] = 0.01
Elemento [4] = 1.9
Elemento [5] = 4.2
Elemento [6] = 6.9
Elemento [7] = 8.7
Elemento [8] = 99.87
Interface IComparable
La función sort no trabaja con clases o estructuras porque no conoce su orden, pero si desea
ordenarlas utilice la interfaceIComparable, por ejemplo una ordenación utilizando una propiedad
numérica:
using System;
public class Lenguaje : IComparable{
string nombre;
int id;
public Lenguaje(string nombre, int id){
this.nombre = nombre;
this.id = id;
}
int IComparable.CompareTo(object o){
Lenguaje lenguajeB = (Lenguaje)o;
if(this.id > lenguajeB.id){return 1;}
if(this.id < lenguajeB.id){
return -1;
}else{
return 0;
}
34. }
public override string ToString(){
return nombre + " " + id;
}
}
class App{
public static void Main(){
Lenguaje[] aLenguaje = new Lenguaje[5];
aLenguaje[0] = new Lenguaje("C",3);
aLenguaje[1] = new Lenguaje("ActionScript",5);
aLenguaje[2] = new Lenguaje("JavaScript",2);
aLenguaje[3] = new Lenguaje("Java",8);
aLenguaje[4] = new Lenguaje("PHP",1);
Array.Sort(aLenguaje);
foreach(Lenguaje len in aLenguaje)
Console.WriteLine(len);
}
}
Salida:
PHP 1
JavaScript 2
C 3
ActionScript 5
Java 8
Interface IComparer
Es posible definir múltiples tipos de ordenamientos gracias a que el diseño del Framework provee
esta capacidad.
Cada clase sólo puede implementar una interface a la vez, por lo que solamente se podría permitir
un tipo de ordenamiento, entonces se requiere una clase separada para cada tipo de ordenamiento
que implementen IComparer y podría también implementar la función Comapare():
using System;
using System.Collections;
public class Lenguaje : IComparable{
string nombre;
int id;
public Lenguaje(string nombre, int id){
this.nombre = nombre;
this.id = id;
}
int IComparable.CompareTo(object o){
Lenguaje lenguajeB = (Lenguaje)o;
if(this.id > lenguajeB.id){return 1;}
if(this.id < lenguajeB.id){
return -1;
}else{
return 0;
}
}
public override string ToString(){
return nombre + " " + id;
}
public class OrdenaNombres : IComparer{
public int Compare(object oA, object oB){
Lenguaje lenA = (Lenguaje)oA;
35. Lenguaje lenB = (Lenguaje)oB;
return
String.Compare(lenA.nombre,lenB.nombre);
}
}
}
class App{
public static void Main(){
Lenguaje[] aLenguaje = new Lenguaje[5];
aLenguaje[0] = new Lenguaje("C",3);
aLenguaje[1] = new Lenguaje("ActionScript",5);
aLenguaje[2] = new Lenguaje("JavaScript",2);
aLenguaje[3] = new Lenguaje("Java",8);
aLenguaje[4] = new Lenguaje("PHP",1);
ArrayList aList = new ArrayList();
aList.Add(aLenguaje[0]);
aList.Add(aLenguaje[1]);
aList.Add(aLenguaje[2]);
aList.Add(aLenguaje[3]);
aList.Add(aLenguaje[4]);
aList.Sort((IComparer) new
Lenguaje.OrdenaNombres());
foreach(Lenguaje len in aList)
Console.WriteLine(len);
}
}
Salida:
ActionScript 5
C 3
Java 8
JavaScript 2
PHP 1
IComparer Como Propiedad
En el ejemplo anterior el usuario tiene que crear una instancia del ordenamiento deseado y hacer
un cast de IComparer, pero es posible simplificar esto utilizando una propiedad estática y hacerlo
por el usuario:
using System;
using System.Collections;
public class Lenguaje : IComparable{
string nombre;
int id;
public Lenguaje(string nombre, int id){
this.nombre = nombre;
this.id = id;
}
int IComparable.CompareTo(object o){
Lenguaje lenguajeB = (Lenguaje)o;
if(this.id > lenguajeB.id){return 1;}
if(this.id < lenguajeB.id){
return -1;
}else{
return 0;
}
}
36. public override string ToString(){
return nombre + " " + id;
}
public static IComparer Ordena{
get{
return (IComparer) new OrdenaNombres();
}
}
public class OrdenaNombres : IComparer{
public int Compare(object oA, object oB){
Lenguaje lenA = (Lenguaje)oA;
Lenguaje lenB = (Lenguaje)oB;
return
String.Compare(lenA.nombre,lenB.nombre);
}
}
}
class App{
public static void Main(){
Lenguaje[] aLenguaje = new Lenguaje[5];
aLenguaje[0] = new Lenguaje("C",3);
aLenguaje[1] = new Lenguaje("ActionScript",5);
aLenguaje[2] = new Lenguaje("JavaScript",2);
aLenguaje[3] = new Lenguaje("Java",8);
aLenguaje[4] = new Lenguaje("PHP",1);
Array.Sort(aLenguaje, Lenguaje.Ordena);
foreach(Lenguaje len in aLenguaje)
Console.WriteLine(len);
}
}
Salida:
ActionScript 5
C 3
Java 8
JavaScript 2
PHP 1
Expresiones Regulares
Las Expresiones Regulares proveen un método muy poderoso para hacer funciones de
busquedas y reemplazamiento.
Operador as
El Operador as checa el tipo del operador izquierdo y si puede ser convertido explicitamente a
el operador derecho, se obtiene como resultado el objeto convertido a el operador derecho, si no
puede ser convertido la operación falla y regresanull. Este operador sólo puede se utilizado con
clases.
Secuencias de Escape
Secuencia de Escape Descripción
' Comilla simple
" Comilla doble
Diagonal invertida
Nulo
37. a Alert
b Retroceso
f Form Feed
n Nueva línea
r Retorno de carro
t Tabulador
v Tabulador vertical
Boxing
Boxing es un mecanismo que crea una liga entre los tipos de valores y las tipos de
referencia permitiendo a un tipo de valor ser convertido a un tipo objeto y viceversa.
using System;
class App{
public static void Main(){
int iEdad = 33;
object oNumero = iEdad; //Box
int iNumero = (int)oNumero; //Unbox
//cast necesario porque oNumero podría contener
cualquier tipo de objeto
}
}
Nota, durante la conversión unboxing el tipo debe coincidir exactamente, un valor de
tipo boxed no puede ser unboxed(convertido) a un tipo compatible. Si requiere obtener otro tipo
de valor diferente al que contiene el boxed, en ese caso primero obtenga el valor correcto y
después realice un cast al tipo que requiera: (valorRequerido)
valorRequerido vr = (valorRequerido)(valorBoxed)objeto;
Otra forma de definir el concepto boxing es que este mecanismo permite que los value
types parezcan o tengan la apariencia de reference types.
Conversiones Boxing
Boxing un valor se refiere a la conversión implícita de cualquier tipo de valor al tipo objeto. Cuando
un tipo de valor esboxed se asigna espacio a una instancia de objeto y el valor del value type es
copiado al nuevo objeto.
Observe las siguientes líneas:
int iNumero = 2012;
object oNumero = iNumero; //invocación implicita a
una operación boxing
Al asignar el valor de la variable entera nNumero a una variable objeto se realiza internamente
una operación boxing, donde el valor de la variable nNumero es copiado al objeto oNumero,
entonces las variables entera y objeto existen en la pila pero los valores de los objetos residen en
el área o espacio asignado, lo que implica que los valores son independientes y no hay una liga
entre ellos:
using System;
class Box{
public static void Main(){
int iNumero = 2012;
object oNumero = iNumero; //invocación implicita a
una operación boxing
oNumero = 2005;
Console.WriteLine(iNumero);
Console.WriteLine(oNumero);
}
}
38. Al ejecutar el código notará que el valor de oNumero es 2005 y el valor de iNumero no cambio
permanece en 2012.
Conversiones Unboxing
Al contrario que Boxing, Unboxing es un mecanismo de una operación explícita, por lo que es
necesario indicar al compilador que tipo de valor deseamos extraer de un objeto, al realizar la
operación Unboxing C# checa que el value typeque se requiere este almacenado en la instancia
del objeto, si la verificación es exitosa el valor es Unboxing.
Suponga que tiene una variable de tipo int y asigna el valor de esta variable int a un objeto,
después declara una variable de tipo double y aplica un cast (double) al objeto para asignar
su valor a la variable double, el objeto contiene sólo un valor int y no puede ser asignado a la
variable double porque el CLR dispara una excepción (System.InvalidCastException):
int iNumero = 2012;
object oNumero = iNumero; //invocación implicita a una
operación boxing
double dNumero = (double)oNumero; //invocacion explícita
(cast)
//CLR dispara la excepción System.InvalidCastException
Constructores y Destructores
Antes de acceder a los métodos o propiedades de una clase, primero se ejecuta el constructor de
la clase el cual contiene código de inicialización, si no se escribe un constructor para la clase el
compilador provee automáticamente un constructor default.
En el runtime .NET el programador no puede controlar la destrucción de objetos.
Un constructor puede invocar un constructor del tipo base utilizando la sintaxis base.
Los constructores son invocados invocados automaticamente sólo cuando una instancia de un
objeto es creada con new.
class NombreClase{
public NombreClase() : base(){} //Constructor que provee
el compilador
}
Las características de un constructor son:
Siempre tiene el mismo nombre que la clase.
No tiene declarado un tipo de regreso.
Por lo general tienen el modificador público.
Son utilizados para inicializar varibles.
Si la clase sólo contiene miembros estáticos, es posible crear un constructor private,
lo cual significa que no podrá ser accesible fuera de la calse o que sólo se puede acceder
desde la clase.
No puede ser invocado desde la definición de la clase.
Un objeto no puede ser instanciado desde la definición de la clase.
Al codificar no se está limitado a los parámetros del constructor, es posible enviar argumentos
iniciales para inicializar ciertos miembros.
using System;
class Vehiculo{
//Propiedades:
private int iRueda;
private int iPuerta;
private int iVentana;
private int iHelice;
private int iMotor;
private int iAsiento;
39. private string sTipo;//Aereo, anfibio, terrestre,
espacial
//Constructor:
public Vehiculo(int Rueda, int Puerta, int Ventana, int
Helice, _
int Motor, int Asiento, string Tipo){
iRueda = Rueda;
iPuerta = Puerta;
iVentana = Ventana;
iHelice = Helice;
iMotor = Motor;
iAsiento = Asiento;
sTipo = Tipo;
}
//Lectura/escritura de propiedades:
public int Ruedas{
get{return iRueda;}
set{iRueda = value;}
}
public int Puertas{
get{return iPuerta;}
set{iPuerta = value;}
}
public int Ventanas{
get{return iVentana;}
set{iVentana = value;}
}
public int Helices{
get{return iHelice;}
set{iHelice = value;}
}
public int Motores{
get{return iMotor;}
set{iMotor = value;}
}
public int Asientos{
get{return iAsiento;}
set{iAsiento = value;}
}
public string Tipo{
get{return sTipo;}
set{sTipo = value;}
}
}
//Aplicación:
class AplicConstructor{
public static void Main(){
40. Vehiculo MiAvion = new
Vehiculo(2,1,100,0,3,200,"Aereo");
Console.WriteLine("Ruedas : " + MiAvion.Ruedas);
Console.WriteLine("Puertas : " +
MiAvion.Puertas);
Console.WriteLine("Ventanas : " +
MiAvion.Ventanas);
Console.WriteLine("Helices : " +
MiAvion.Helices);
Console.WriteLine("Motores : " +
MiAvion.Motores);
Console.WriteLine("Asientos : " +
MiAvion.Asientos);
Console.WriteLine("Tipo : " + MiAvion.Tipo);
}
}
En un sentido estricto en C# no se tienen destructores, pero el termino destructor se refiere a
la liberación de recursos.
Es posible escribir un método que libere recursos después de ser utilizados, pero porque escribir
un método para liberar recursos si existe un destructor:
public ~NombreClase(){
//liberar recursos
}
La razón por la cual se debería escribir un método adicional es por el recolector de basura, el cual
no es invocado inmediatamente después que las variables quedan fuera de ámbito, sólo se invoca
el recolector de basura en ciertos intervalos o condiciones de memoria.
Lo que podría suceder es que los recursos se agoten antes de ser utilizados, entonces es buena
idea proveer un método explícito Release, el cual también puede ser invocado por el destructor:
public void Release(){
//Liberar recursos
}
public ~NombreClase(){
Release();
}
La invocación del método Release en el destructor no es obligatoria, la colección garbage de
cualquier forma realiza la liberación de cualquier objeto, pero es una buena práctica no olvidar
liberar los recursos.
Constructor Estático
Un Constructor Estático podría ser invocado antes de ser creada la primer instancia de un
objeto, y es útil para configurar el trabajo que necesita hacerse una vez.
En el runtime .NET el usuario no tiene control sobre cuando el constructor estático es invocado,
ya que el runtime sólo garantiza que algo es invocado después del inicio del programa y antes de
ser creada la primer instancia de un objeto, lo que significa que no puede ser determinada la
instancia que es creada en el constructor estático.
Para declarar un constructor estático se utiliza el modificador static:
class NombreClase{
static NombreClase(){
.
.
}
}
Métodos
La mayor parte de la funcionalidad es implementada en los métodos, los métodos son parte del
Tipo (class), pero los métodos no son parte de la instancia (object).
41. Parámetros
De algún modo se deben pasar valores a un método y también se debe regresar el resultado de
un método, los valores son manipulados en:
Valores en Parámetros in
Se utilizan valores en parámetros para pasar una variable por valor a un método, la variable del
método es inicializada con una copia del valor del caller (quien realizó la invocación).
using System;
public class Fecha{
public string Mayor(int iDiaA,int iMesA,int iAñoA,int
iDiaB,int iMesB,int iAñoB){
int iA = (iDiaA * 10000) + (iMesA + 100) +
(iAñoA);
int iB = (iDiaB * 10000) + (iMesB + 100) +
(iAñoB);
Console.WriteLine(iA + " > " + iB); //Test Line
(Delete)
if(iA > iB){
return iDiaA + "/" + iMesA + "/" + iAñoA;
}else{
return iDiaB + "/" + iMesB + "/" + iAñoB;
}
}
public string Menor(int iDiaA,int iMesA,int iAñoA,int
iDiaB,int iMesB,int iAñoB){
int iA = (iDiaA * 10000) + (iMesA + 100) +
(iAñoA);
int iB = (iDiaB * 10000) + (iMesB + 100) +
(iAñoB);
Console.WriteLine(iA + " < " + iB); //Test Line
(Delete)
if(iA < iB){
return iDiaA + "/" + iMesA + "/" + iAñoA;
}else{
return iDiaB + "/" + iMesB + "/" + iAñoB;
}
}
}
class AplicFecha{
public static void Main(){
Fecha MiFecha = new Fecha();
Console.WriteLine("La fecha mayor es : " +
MiFecha.Mayor(21,9,1971, 21,10,2000));
Console.WriteLine("La fecha menor es : " +
MiFecha.Menor(21,9,1971, 21,10,2000));
Console.WriteLine("La fecha mayor es : " +
MiFecha.Mayor(21,10,2000, 21,9,1971));
Console.WriteLine("La fecha menor es : " +
MiFecha.Menor(21,10,2000, 21,9,1971));
Console.WriteLine("La fecha mayor es : " +
MiFecha.Mayor(21,10,2000, 21,9,2005));
Console.WriteLine("La fecha menor es : " +
MiFecha.Menor(21,10,2000, 21,9,2005));
}