¿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)
 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 
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; 
} 
//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 
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); 
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"; 
} 
} 
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. 
 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. 
 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. 
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 
 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 e sa 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 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 acces o 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. 
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 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. 
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. 
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. 
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. 
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++) 
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: 
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 
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 
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#: 
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. 
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 { 
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 
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(){ 
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"; 
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 
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]; 
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 
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{ 
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: 
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; 
} 
} 
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; 
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; 
} 
} 
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 
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); 
} 
} 
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; 
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(){ 
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). 
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)); 
} 
} 
Al pasar un valor y no una referencia a la variable, es posible utilizar expresiones constantes, el 
resultado de los métodosMayor y Menor es pasado a el caller como un valor de regreso y es 
manipulado sin ser almacenarlo en una variable intermedia. 
Si no hay modificadores los parámetros son siempre pasados por valor. 
Valores en Parámetros ref 
Es posible pasar un valor como parámetro a un método, modificar el valor y regresarlo como 
resultado del método, para ello se utiliza el modificador ref seguido del tipo y del nombre del 
parámetro. 
Al contrario de los valores en parámetros no se pasa una copia del valor, sino la referencia del 
valor y por ello al modificar el valor se hace la modificación directa, también es necesario 
inicializar el valor que se pasa como paramétro por medio de una variable intermedia y no 
directamente a través de una expresión constante: 
using System; 
public class Param{ 
public void ParametroRef(ref int RefParametro){//No 
regresa un valor explícito 
RefParametro *= RefParametro; //Se modifica el 
valor directamente 
//No se regresa un valor, porque se modifico de 
manera directa 
} 
} 
class Parametros{ 
public static void Main(){ 
Param MiParam = new Param(); 
int iValorRef = 5; //Se requiere inicializar el valor 
MiParam.ParametroRef(ref iValorRef);//Se invoca el método 
pasando la referencia del valor 
Console.WriteLine("ref : " + iValorRef); 
} 
}
Good Practice, se recomienda tener dos variables, una en el parámetro y otra en el parámetro 
ref. 
Recuerde, el compilador de C# no permite utilizar variables que no han sido inicializadas, por 
lo que antes de utilizar o establecer los valores de una variable debe ser inicializada, para ello 
existen dos formas de hacerlo: 
 Inicializar la variable al declararla. 
 
 using System; 
 
 class MiClase{ 
 private int MiPropiedad; 
 public void AsignarValor(ref int MiRefParam){ 
 this.MiPropiedad = MiRefParam; 
 } 
 public static void Main(){ 
 MiClase MiObjeto = new MiClase(); 
 int MiEdad = 33;//Se inicializa la variable al declararla 
 MiObjeto.AsignarValor(ref MiEdad); 
 Console.WriteLine("MiEdad : " + MiEdad); 
 Console.WriteLine("MiObjeto.MiPropiedad : " + 
MiObjeto.MiPropiedad); 
 } 
 } 
 Por ejemplo cambiar la definición de la función y utilizar un parámetro out en vez de 
un parámetro ref. 
 using System; 
 
 class MiClase{ 
 private int MiPropiedad; 
 public void AsignarValor(out int MiOutParam){//Se cambia la 
definición de la función 
 MiOutParam = 33; 
 this.MiPropiedad = MiOutParam; 
 } 
 public static void Main(){ 
 MiClase MiObjeto = new MiClase(); 
 int MiEdad;//No se inicializa la variable 
 MiObjeto.AsignarValor(out MiEdad); 
 Console.WriteLine("MiEdad : " + MiEdad); 
 Console.WriteLine("MiObjeto.MiPropiedad : " + 
MiObjeto.MiPropiedad); 
 } 
 } 
Los parámetros out son exactamente como los parámetros ref excepto que una variable sin 
inicializar puede ser pasada como parámetro y el caller define un parámetro out en vez de ref. 
Valores en Parámetros out 
Un parámetro out puede ser utilizado sólo para contener el resultado de un método, es 
necesario especificar el modificador out para indicar el tipo de parámetro, a diferencia de los 
parámetros ref el caller no necesita inicializar la variable antes de invocar el método: 
using System;
public class Param{ 
public void ParametroOut(out int OutParametro){ 
OutParametro = 4 * 4; 
//No se gregresa un valor, porque es regresado en 
el parámetro out 
} 
} 
class Parametros{ 
public static void Main(){ 
Param MiParam = new Param(); 
int iValorOut; //No se requiere inicilizar el valor 
MiParam.ParametroOut(out iValorOut); //Se invoca el método 
con un parámetro out 
Console.WriteLine("out : " + iValorOut);//Resultado de la 
invocación del método 
} 
} 
Ejemplo de Parámetros In, Ref y Out 
using System; 
public class Param{ 
public int ParametroIn(int InParametro){ 
return InParametro * InParametro; 
} 
public void ParametroRef(ref int RefParametro){//No 
regresa un valor explícito 
RefParametro *= RefParametro; //Se modifica el 
valor directamente 
//No se regresa un valor, porque se modifico de 
manera directa 
} 
public void ParametroOut(out int OutParametro){ 
OutParametro = 4 * 4; 
//No se gregresa un valor, porque es regresado en 
el parámetro out 
} 
} 
class Parametros{ 
public static void Main(){ 
Param MiParam = new Param(); 
Console.WriteLine(" in : " + MiParam.ParametroIn(3)); 
int iValorRef = 5; //Se requiere inicializar el valor 
MiParam.ParametroRef(ref iValorRef);//Se invoca el método 
pasando la referencia del valor 
Console.WriteLine("ref : " + iValorRef); 
int iValorOut; //No se requiere inicilizar el valor 
MiParam.ParametroOut(out iValorOut); //Se invoca el método 
con un parámetro out
Console.WriteLine("out : " + iValorOut);//Resultado de la 
invocación del método 
} 
} 
Redefinición de Métodos (Overriding) 
Uno de los principios básicos de la programación orientada a objetos es el polimorfismo, el 
cual hace posible que una clase derivada pueda redefinir (override) métodos de la clase base. 
Para indicar que se puede redefinir el método se emplea la palabra reservada virtual: 
virtual void NombreMetodo_PuedeSerRedefinido 
Después, al derivar de la clase base se agrega la palabra reservada override en el nuevo 
método: 
override void NombreMetodo_PuedeSerRedefinido() 
No es posible cambiar la accesibilidad de un método que es redefinido, es decir, no es posible 
cambiar los modificadores que definen al método. 
Cuando se invoca un método virtual se está derivando el método de clase que es invocado y 
no el método de la clase base: 
((ClaseBase)InstanciaClaseDerivada).NombreMetodo_PuedeSerRedefinido(); 
Para indicar que una clase deriva de otra se utiliza el operador : el cual denota esta acción: 
class ClaseDerivada : ClaseBase{} 
El siguiente ejemplo muestra como se redefine un método: 
using System; 
class ClaseBase{ 
public virtual int Calculo(int iA, int iB){ 
return iA + iB; 
} 
} 
class ClaseDerivada : ClaseBase{//Se deriva de la clase base 
public override int Calculo(int iA, int iB){//Se especifica que 
el método será redefinido 
return iA - iB;//Se redefine la funcionalidad del método 
} 
} 
class RedefinirMetodos{ 
public static void Main(){ 
ClaseBase ClsBase = new ClaseBase(); 
Console.WriteLine("Clase base : " + 
ClsBase.Calculo(5,3)); 
//Se crea una instancia de la clase derivada: 
ClaseDerivada ClsDer = new ClaseDerivada(); 
//Se invoca el método redefinido en la clase 
derivada: 
Console.WriteLine("Clase derivada : " + 
ClsDer.Calculo(5,3)); 
} 
} 
Ocultamiento de Métodos (Hiding) 
Es posible ocultar métodos de la clase base, esto se logra haciendo uso de una característica 
especial de la redefinición de métodos llamada ocultamiento de métodos y al derivar de la 
clase base: 
using System; 
class ClaseBase{
//Sin código 
} 
class ClaseDerivada : ClaseBase{//Clase derivada de la clase base 
public void MetodoOculto(){//Método Oculto 
Console.WriteLine("Hiding Methods"); 
} 
} 
class Hiding{ 
public static void Main(){ 
ClaseDerivada MiClaseDerivada = new 
ClaseDerivada(); 
MiClaseDerivada.MetodoOculto(); 
} 
} 
El código anterior demuestra que es posible derivar una clase que implementa un método que la 
clase base no contiene. 
Por otro lado si la clase base contiene el método y se trata de derivar una clase que trata de 
implemetar un método que si contiene la clase, se produce un error: 
using System; 
class ClaseBase{ 
public void MetodoOculto(){ 
Console.WriteLine("Hiding Methods"); 
} 
} 
class ClaseDerivada : ClaseBase{//Clase derivada de la clase base 
public void MetodoOculto(){//Se implementa un método que 
si existe 
Console.WriteLine("Hiding Methods"); 
} 
} 
class Hiding{ 
public static void Main(){ 
ClaseDerivada MiClaseDerivada = new 
ClaseDerivada(); 
MiClaseDerivada.MetodoOculto(); 
} 
} 
El compilador indicará un mensaje similar al siguiente: 
Hiding.cs(10,14): warning CS0108: The keyword new is required on 
'ClaseDerivada.MetodoOculto()' 
because it hides inherited member 
'ClaseBase.MetodoOculto()' 
Hiding.cs(4,14): (Location of symbol related to previous warning) 
El error principal es que no se hace uso del modificador new, ya que si es posible ocultar un 
método contenido en la clase base: 
using System; 
class ClaseBase{ 
public void MetodoOculto(){//Método Oculto 
Console.WriteLine("Hiding Methods"); 
}
} 
class ClaseDerivada : ClaseBase{//Clase derivada de la clase base 
new public void MetodoOculto(){//Método Oculto 
Console.WriteLine("Hiding Methods using new"); 
} 
} 
class HidingClassMet{ 
public static void Main(){ 
ClaseDerivada MiClaseDerivada = new 
ClaseDerivada(); 
MiClaseDerivada.MetodoOculto(); 
} 
} 
Al hacer uso del modificador new, se le indica al compilador que se está redefiniendo el método 
de la clase base y que debería ocultar este método. 
Se puede asegurar de invocar el método que redefine la clase derivada utilizando la siguiente 
sintaxis: 
((ClaseBase)MiClaseDerivada).MetodoOculto(); 
Propiedades 
Las propiedades son convenientes para separar la interfaz de un objeto de su implementación, 
en vez de permitir a un usuario acceder directamente a un campo o arreglo, una propiedad 
permite especificar a un conjunto de sentencias realizar el acceso mientras se permita utilizar el 
campo o arreglo. 
class NombreClase{ 
int iNombrePropiedad; //declaración de la propiedad 
//Especificación del acceso a la propiedad 
public int NombrePropiedad{ 
get{return iNombrePropiedad;} 
set{iNombrePropiedad = value;} 
} 
} 
Ejemplo: 
using System; 
class Propiedades{ 
private int iEdad; 
public int Edad{ 
get{return iEdad;} 
set{iEdad = value;} 
} 
private string sNombre; 
public string Nombre{ 
get{return sNombre;} 
set{sNombre = value;} 
} 
private bool bMexicano; 
public bool Mexicano{ 
get{return bMexicano;} 
set{bMexicano = value;}
} 
public static void Main(){ 
Propiedades Ciudadano = new Propiedades(); 
Ciudadano.Edad = 33; 
Console.WriteLine("Edad Ciudadano : " + 
Ciudadano.Edad); 
Ciudadano.Nombre = "Gerardo Ángeles Nava"; 
Console.WriteLine("Nombre Ciudadano : " + 
Ciudadano.Nombre); 
Ciudadano.Mexicano = true; 
Console.WriteLine("Mexicano Ciudadano : " + 
Ciudadano.Mexicano); 
} 
} 
Existen dos maneras de exponer el nombre de los atributos: 
 Campos (fields) 
 Propiedades (properties) 
Los atributos son implementados como variables miembro con acceso público 
via accessors (get o set). 
Los accessors (get o set) especifican las sentencias que son ejecutadas cuando se requiere 
leer o escribir el valor de una propiedad. 
Los accessors para la lectura del valor de una propiedad son marcados con la palabra 
reservada get y los accessors para modificar el valor de una propiedad son marcados con la 
palabra reservada set. 
El siguiente ejemplo muestra como se implentan los accessors para las propiedades: 
using System; 
class Persona{ 
private int iSueldo; 
public int Sueldo{ 
get{return iSueldo;} 
set{iSueldo = value;} 
} 
} 
class AplicPersona{ 
public static void Main(){ 
Persona Empleado = new Persona(); 
Empleado.Sueldo = 33; 
Console.WriteLine("Edad : " + Empleado.Sueldo); 
} 
} 
Note, que se utiliza el parámetro value, ya que el valor actual es almacenado en este que es 
accesible dentro de la clase. 
Si en vez de utilizar propiedades desea utilizar campos deberá dejar fuera los accessors y 
redefinir la variable como: 
public int Sueldo; 
Accessors
Es posible ocultar los detalles de la estructura de almacenamiento de la clase reordenando 
los accessors, en este caso elaccessors set es pasado en el nuevo valor para la propiedad en el 
parámetro value. 
Las operaciones que pueden realizarse con los atributos son: 
 Implementar get y set, es posible tener acceso al valor de la propiedad para leerlo y 
escribirlo. 
 get only, sólo es posible leer el valor de la propiedad. 
 set only, sólo es posible establecer el valor de la propiedad. 
Propiedades Estáticas 
Propiedades estáticas no pueden ser declaradas con los 
modificadores virtual, abstract u override. 
Las propiedades estáticas pueden ser inicializadas hasta que sea necesario hacerlo, el valor 
puede ser fabricado cuando se necesite sin almacenarlo. 
using System; 
class Persona{ 
int iPiernas; 
int iBrazos; 
int iOjos; 
public Persona(int piernas, int brazos, int ojos){ 
this.iPiernas = piernas; 
this.iBrazos = brazos; 
this.iOjos = ojos; 
} 
public static Persona Piernas{ 
get{ 
return(new Persona(2,0,0)); 
} 
} 
public static Persona Brazos{ 
get{ 
return(new Persona(0,4,0)); 
} 
} 
public static Persona Ojos{ 
get{ 
return(new Persona(0,0,8)); 
} 
} 
} 
class App{ 
public static void Main(){ 
Persona ET = Persona.Piernas; 
Console.WriteLine(ET); 
} 
} 
Índices 
Es posible incluir una forma de acceso indexado a la clase tal como si la clase se tratará de un 
arreglo, para ello se utiliza la característica de C# indexer, sintaxis: 
atributos modificadores declarador{instrucciones} 
Los índices o indexers regresan o establecen un string en un índice dado, los indexers no tienen 
atributos por lo que utilizan el modificador public.
La parte del declarador consiste del tipo string y la palabra reservada this para denotar 
el indexer de la clase: 
public string this[int iIndex]{ 
get{intrucciones} 
set{intrucciones} 
} 
Las reglas de implementación para get y set son las mismas reglas de las propiedades, la 
única diferencia es que la lista de parámetros se define libremente entre los corchetes, también 
existen restricciones como que es necesario especificar al menos un parámetro y los 
modificadores ref y out no están permitidos. 
Ejemplo: 
using System; 
using System.Net;//Directiva namespace para la clase DNS 
class ResolverDNS{ 
IPAddress[] aIPs; 
public void Resolver(string strHost){ 
IPHostEntry IPHE = Dns.GetHostByName(strHost); 
aIPs = IPHE.AddressList; 
} 
public IPAddress this[int iIndex]{ 
get{return aIPs[iIndex];} 
} 
public int Contador{ 
get{return aIPs.Length;} 
} 
} 
class AplicResolverDNS{ 
public static void Main(){ 
ResolverDNS MiDNS = new ResolverDNS(); 
MiDNS.Resolver("www.informatique.com.mx"); 
int iContador = MiDNS.Contador; 
Console.WriteLine("Se encontro {0} para el host ", 
iContador); 
for(int i = 0; i < iContador; i++){ 
Console.WriteLine(MiDNS[i]); 
} 
} 
} 
Si el namespace para la clase DNS no está contenida en la biblioteca central, al compilar incluya 
la referencia a la biblioteca que la contiene: 
csc /r:System.Net.dll /out:resolver.exe AplicResolverDNS.cs 
Los índices pueden tener más que un parámetro para simular un arreglo virtual 
multidimensional. 
Eventos 
Una clase puede usar un evento para notificar a otra clase o clases que algo ocurrio, los eventos 
usan el idioma "publish-subscribe", ya que una clase publica el evento que puede lanzar y las 
clases que están interesadas en un evento específico pueden subscribir al evento. 
La rutina o tarea que un evento podría invocar es definida por un delegado. 
Para tratar de manera fácil con eventos, la convención de diseño para eventos es emplear dos 
parámetros, donde el primer parámetro es el objeto que lanza el evento y el segundo parámetro 
es un objeto que contiene la información acerca del evento el cual siempre deriva de la 
clase EventArgs.
Los eventos pueden ser declarados como campos o propiedades de clase, ambos accesos 
comparten la comodidad de tipo que el evento debe tener delegate. 
Cada evento puede ser utilizado por cero o más clientes y un cliente puede utilizar un evento en 
cualquier momento. 
Los delegados pueden ser implementados como métodos o instancias estáticas. 
Modificadores 
Los modificadores pueden clasificarse en: 
 Modificadores de Clase 
 Modificadores Miembro 
 Modificadores de Acceso 
Modificadores de Clase 
Existen dos tipos de modificadores de clase: 
 abstract 
 Una clase abstracta no puede ser inicializada 
 Sólo clases derivadas que no son abstractas pueden ser inicializadas 
 Las clases derivadas deben implementar todos los miembros abstractos de la clase 
base abstracta 
 No puede aplicarse un modificador sealed a una clase abstracta 
 sealed 
 Clases sealed no pueden ser heredadas 
 Utilice este modificador para prevenir herencia accidental 
Ejemplo: 
using System; 
abstract class ClaseAbstracta{ 
abstract public void MiMetodo(); 
} 
sealed class ClaseDerivada:ClaseAbstracta{ 
public override void MiMetodo(){ 
Console.WriteLine("Clase sealed"); 
} 
} 
public class ModificadorClase{ 
public static void Main(){ 
ClaseDerivada CD = new ClaseDerivada(); 
CD.MiMetodo(); 
} 
} 
Modificadores Miembro 
Algunos modificadores miembro son: 
 abstract 
 Indica que un método o accessor no contiene una implementación, que son 
implicitamente virtual. 
 La clase que recibe la herencia debe proveer la palabra reservada override. 
 const 
 Este modificador aplica a campos y variables locales
 La expresión constante es evaluada en tiempo de compilación, por lo tanto no puede 
contener referencias de la clase. 
 event 
 Define un campo o propiedad como tipo event 
 Utilizado para ligar al código cliente a eventos de la clase 
 extern 
 Indica al compilador que el método es implemetado externamente 
 override 
 Utilizado para modificar un método o accessor que es definido virtual en cualquiera 
de las clases base 
 La firma de redefinición y método base debe ser el mismo 
 readonly 
 Un campo declarado con el modificador readonly puede ser cambiado sólo en su 
declaración o en el constructor de la clase contenedora 
 static 
 Miembros que son declarados static pertenecen a la clase y no a una instancia de la 
clase 
 Pueden utilizarse modificadores static con campos, métodos, propiedades, 
operadores y constructores. 
 virtual 
 Indican que un método o accessor pueden ser redefinidos por las clases que reciben la 
herencia. 
Modificadores de Acceso 
Los modificadores de acceso definen el nivel de acceso que cierto código tiene en los miembros 
de la clase como métodos y propiedades. Es necesario aplicar el modificador de acceso deseado 
a cada miembro, de otra forma el tipo de acceso por default es implícito. 
Los modificadores de acceso son: 
 public, el miembro es accesible desde cualquier parte, este modificador de acceso es 
el menos restrictivo. 
 protected, el miembro es accesible en la clase y todas las clases derivadas. No es 
permitido el acceso desde fuera. 
El acceso protected permite a otras clases depender de la implementación interna de 
la clase y por lo tanto deberían ser otorgados sólo cuando sea necesario. 
 private, sólo el código dentro de la misma clase puede acceder este miembro. Las 
clases derivadas no pueden acceder al código. 
 internal, este tipo de acceso es otorgado a todo el código que es parte del mismo 
componente (aplicación o biblioteca) .NET, es visto como público a nivel del 
componente .NET y privado fuera de este. Este modificador permite que un miembro 
sea accesible desde las clases en el mismo ensamblaje, pero no desde las clases fuera 
de este. 
El modificador internal protected provee mayor flexibilidad en como una clase es 
definida y se utiliza para indicar que un miembro pueder ser accedido desde una 
clase internal o protected, en otras palabras internal protected permite 
acceso internal o protected. 
Sentencias de Control 
Las Sentencias de Control se emplean para controlar la ejecución y flujo del código, las cuales 
se dividen en:
 Sentencias de Selección 
 Sentencias de Iteración 
Sentencias de Selección 
Las Sentencias de selección son aquellas que se utilizan para realizar operaciones basadas en 
el valor de una expresión. 
Las Sentencias de selección son aquellas que se utilizan para escribir diferentes flujos de 
acción en base a una condición dada, existen dos tipos de sentencias de selección: 
 Sentencia if 
 Sentencia switch 
Sentencia if 
Al escribir uno o varios flujos de acción el código contenido en estos se ejecutará siempre y 
cuando la evaluación de la expresión en la sentencia if se evalue como verdadera (tenga 
cuidado en C# if(0){} o if(1){} no es válido). 
if(expresión-booleana){la expresión se evaluo verdadera} 
Es posible indicar código alterno en caso de que la expresión booleana se evalue falsa: 
if(expresión-booleana){ 
la expresión se evaluo verdadera 
}else{ 
la expresión se evaluo falsa 
} 
Nota C# no puede convertir valores numéricos a booleanos, solo puede hacer comparaciones 
entre ellos para evaluar el resultado de la expresión el cual es un valor booleano. 
using System; 
class SeleccionIf{ 
public static void Main(){ 
if(1 == 1){ 
Console.WriteLine("se evaluo verdadero"); 
} 
/* No es soportado por C# 
if(0){ 
Console.WriteLine("?"); 
} 
*/ 
} 
} 
Nota el operador de igualdad en C# es ==, si está habituado a otra forma, sera cosa tiempo 
acostumbrarse a escribirlo correctamente, en la siguiente tabla se muestran los operadores 
válidos en C#: 
Operador Evalua 
== Verdadero, si ambos valores son los mismos 
!= Verdadero, si los valores son diferentes 
<, <=, >, >= Verdadero, si el valor cumple con la condición 
Los operadores de la tabla son implementados via la sobrecarga de operadores y la 
implementación es especifica para el tipo de dato, si se comparan dos variables de diferente tipo 
se realiza una conversión implícita que debe existir para que el compilador cree el código 
necesario automáticamente. Recuerde que siempre podrá realizar un cast explícito. 
Ejemplo 
using System; 
class Caracteres{ 
public static void Main(){
string sNombre = "Gerardo Angeles Nava"; 
char chLetra = sNombre[0];//Extrae el primer 
caracter del string 
if(Char.IsDigit(chLetra)){ 
Console.WriteLine(chLetra + " es un 
dígito"); 
}else{ 
EsMayuscula(chLetra); 
EsMinuscula(chLetra); 
} 
chLetra = sNombre[1];//Extrae el segundo caracter 
del string 
if(Char.IsDigit(chLetra)){ 
Console.WriteLine(chLetra + " es un 
dígito"); 
}else{ 
EsMayuscula(chLetra); 
EsMinuscula(chLetra); 
} 
sNombre = "123"; 
chLetra = sNombre[2];//Extrae el tercer caracter 
del string 
if(Char.IsDigit(chLetra)){ 
Console.WriteLine(chLetra + " es un 
dígito"); 
}else{ 
EsMayuscula(chLetra); 
EsMinuscula(chLetra); 
} 
} 
public static void EsMayuscula(char chCaracter){ 
if(chCaracter >= 'A' && chCaracter <= 'Z'){ 
Console.WriteLine(chCaracter + " 
mayúscula"); 
} 
} 
public static void EsMinuscula(char chCaracter){ 
if(chCaracter >= 'a' && chCaracter <= 'z'){ 
Console.WriteLine(chCaracter + " 
minúscula"); 
} 
} 
} 
En el ejemplo anterior se muestra la aplicación de la sentencia de selección if y el uso del 
método IsDigit de la claseChar, también se muestra como determinar si un caracter 
correponde a las letras mayúsculas o minúsculas. 
Good Practice: nunca asigne valores a variables dentro de una condición que utiliza operadores 
lógicos (&&,||,!), porque puede que nunca se le asigne el valor correspondiente a la variable en 
caso de que una expresión anterior se evalue verdadera: 
if(a == b || (c == (iValor = d))){}
En el ejemplo anterior, si la expresión a == b se evalua verdadera entonces la 
variable iValor nunca contendrá el valor d. 
Sentencia switch 
La sentencia de selección switch tiene una expresión de control y los flujos de código alternos 
son ejecutados dependiendo del valor constante asociado con esta expresión. 
switch(expresion-de-control){ 
case expresion-contante: 
sentencias; 
break; 
case expresion-contante: 
goto case 2; 
case expresion-contante: 
goto default; 
default: 
sentencias; 
} 
Los tipos de datos permitidos para la expresión de control 
son sbyte, byte, short, ushort, uint, long, ulong, char, string o un tipo enumeración 
(enumeration). 
¿Cómo funciona la sentencia de selección switch? 
 Se evalua la expresión de control 
 Si la expresión constante en las etiquetas case coincide con el valor evaluado en la 
expresión de control, entonces las sentencias contenidas para ese caso son ejecutadas 
 Si la expresión constante en las etiquetas case no coincide con el valor evaluado en la 
expresión de control, entonces el código contenido en el caso por default es 
ejecutado 
 Si la expresión constante en las etiquetas case no coincide con el valor evaluado en la 
expresión de control y no existe un caso por default, entonces el control es 
transferido al final del bloque switch 
Ejemplo: 
using System; 
class SentenciaSwitch{ 
public static void Main(){ 
for(int i = 0; i <= 12; i++){ 
Mes(i); 
} 
} 
public static void Mes(int iMes){ 
switch(iMes){ 
case 1: 
Console.WriteLine("Enero"); 
break; 
case 2: 
Console.WriteLine("Febrero"); 
break; 
case 3: 
Console.WriteLine("Marzo"); 
break; 
case 4: 
Console.WriteLine("Abril"); 
break; 
case 5: 
Console.WriteLine("Mayo");
break; 
case 6: 
Console.WriteLine("Junio"); 
break; 
case 7: 
Console.WriteLine("Julio"); 
break; 
case 8: 
Console.WriteLine("Agosto"); 
break; 
case 9: 
Console.WriteLine("Septiembre"); 
break; 
case 10: 
Console.WriteLine("Octubre"); 
break; 
case 11: 
Console.WriteLine("Noviembre"); 
break; 
case 12: 
Console.WriteLine("Diciembre"); 
break; 
default: 
Console.WriteLine("Mes no válido"); 
break; 
} 
} 
} 
Es posible utilizar sentencias goto dentro del switch de la siguiente manera: 
 goto case expresion-contante 
 goto default 
Ejemplo: 
using System; 
class SentenciaSwitch{ 
public static void Main(){ 
int iOpcion = 4; 
Opcion(iOpcion); 
iOpcion = 2; 
Opcion(iOpcion); 
iOpcion = 8; 
Opcion(iOpcion); 
iOpcion = 10; 
Opcion(iOpcion); 
} 
public static void Opcion(int iValor){ 
switch(iValor){ 
case 2: 
goto case 6; 
case 4: 
Console.WriteLine(" cuatro"); 
break; 
case 6: 
Console.WriteLine(" seis"); 
break; 
case 8:
goto default; 
case 10: 
Console.WriteLine(" diez"); 
break; 
default: 
Console.WriteLine(" por defecto"); 
break; 
} 
} 
} 
Sentencias de Iteración (repetición) 
Las Sentencias de Iteración (también conocidas como looping statements) son aquellas que 
nos permiten ejecutar un bloque de código repetidamente mientras una condicíon específica sea 
verdadera: 
 for 
 foreach 
 while 
 do 
Sentencia for 
La Sentencia for se utiliza cuando se conoce previamente cuantas veces ha de repetirse un 
bloque de código. Este bloque se repetira mientras la condición evalue una expresión booleana 
verdadera, no será posible evaluar otro tipo de expresión. 
Sintaxis: 
for(inicializador; condición; iterador) 
Los componentes de la sentencia for: inicializador, condición, iterador, no son obligatorios. 
Es posible salir de un ciclo for a través de las instrucciones: 
 break 
 goto 
Ejemplo: 
using System; 
class Factorial{ 
public static void Main(string[] aArgs){ 
if(aArgs.Length == 0){ 
Console.WriteLine("Debe proporcionar un argumento, 
Ejemplo: Factorial 5"); 
return; 
} 
long lFactorial = 1; 
long lCalcular = Int64.Parse(aArgs[0]); 
long lAux = 1; 
for(lAux = 1; lAux <= lCalcular; lAux++){ 
lFactorial *= lAux; 
//Test Line Console.WriteLine("{0}! * {1}", lAux, 
lFactorial); 
} 
Console.WriteLine("{0}! es {1}", lCalcular, lFactorial); 
} 
} 
Sentencia foreach 
La Sentencia foreach es un comando para enumerar los elementos de una colección.
foreach(Tipo indentificador in expresión){} 
La variable de iteración es declarada por el Tipo, indentificador y expresión correspondiente a la 
colección. 
La variable de iteración representa el elemento de la colección para cada iteración. 
El siguiente ejemplo muestra el uso de for: 
using System; 
class App{ 
public static void Main(string[] aArgs){ 
for(int i = 0; i < aArgs.Length; i++){ 
Console.WriteLine("Elemento " + i + " = " + 
aArgs[i]); 
} 
} 
} 
El ejemplo anterior implementado con foreach: 
using System; 
class App{ 
public static void Main(string[] aArgs){ 
foreach(String s in aArgs){ 
Console.WriteLine(s); 
} 
} 
} 
No es posible asignar un nuevo valor a la variable de iteración. No se puede pasar la variable de 
iteración como un parámetro ref o out. 
Para que una clase soporte la sentencia foreach, la clase debe soportar un método con la 
firma GetEnumerator() y la estructura, clase o interface que regresa debe tener un método 
público MoveNext y una propiedad pública Current. 
En el siguiente ejemplo el método GetEnvironmentVariables() regresa una interfaz de 
tipo IDictionary. Es posible acceder a las colecciones Keys y Values de la 
interfaz IDictionary: 
using System; 
using System.Collections; 
class SentenciaForEach{ 
public static void Main(){ 
IDictionary VarsAmb = 
Environment.GetEnvironmentVariables(); 
Console.WriteLine("Existen {0} variables de ambiente 
declaradas", VarsAmb.Keys.Count); 
foreach(String strIterador in VarsAmb.Keys){ 
Console.WriteLine("{0} = {1}", strIterador, 
VarsAmb[strIterador].ToString()); 
} 
} 
} 
Nota, es necesario tener una precaución extra al decidir el tipo de variable de iteración, porque 
un tipo equivocado no puede ser detectado por el compilador, pero si detectado en tiempo de 
ejecución y causar una excepción. 
Sentencia while 
La Sentencia while se utiliza cuando no se conoce previamente cuantas veces ha de repetirse 
un bloque de código, por lo que puede ejecutarse 0 o más veces. Este bloque se repetira 
mientras la condición evalue una expresión booleana verdadera, no será posible evaluar otro 
tipo de expresión.
while(condicional){} 
Ejemplo: 
using System; 
using System.IO; 
class SentenciaWhile{ 
public static void Main(){ 
if(!File.Exists("test.html")){ 
Console.WriteLine("El archivo test.html no 
existe"); 
return; 
} 
StreamReader SR = File.OpenText("test.html"); 
String strLinea = null; 
while(null != (strLinea = SR.ReadLine())){ 
Console.WriteLine(strLinea); 
} 
SR.Close(); 
} 
} 
Es posible utilizar la sentencia break para salir del ciclo o continue para saltar una iteración. 
Sentencia do 
La diferencia entre la sentencia while y do es que do se evalua después de su primer iteración, 
por lo que al menos siempre se ejecuta una vez: 
do{ 
sentencias; 
}while(condición); 
Es posible salir de un ciclo do a través de la sentencia break y es posible saltar una iteración 
utilizando la sentenciacontinue 
El siguiente ejemplo le la entrada de la consola toma el primer caracter leido, lo convierte en un 
Tipo double y suma su valor mientras la entrada sea 's' o hasta que la entrada sea 'n'. 
using System; 
class Consola{ 
public static void Main(){ 
Consola LeerDatos = new Consola(); 
LeerDatos.Run(); 
} 
public void Run(){ 
char chContinuar = 's'; 
string strDatos; 
double dSuma = 0; 
do{ 
Console.Write("Proporcione un número: "); 
strDatos = Console.ReadLine(); 
dSuma += Double.Parse(strDatos); 
Console.Write("¿Continuar s/n?"); 
strDatos = Console.ReadLine(); 
chContinuar = strDatos[0]; 
if(chContinuar == 'n') break; 
}while(chContinuar == 's'); 
Console.WriteLine("La suma de los números es: " + 
dSuma);
} 
} 
Sentencias de Salto 
Las Sentencias de Salto como break, continue, goto y return sirven para ir de una 
sentencia a otra 
break 
La Sentencia break es utilizada para salir de la iteración en curso o sentencia switch y 
continuar con la ejecución después de esa sentencia. 
continue 
La Sentencia continue salta todas las sentencias siguientes en la iteración en curso y 
entonces continua la ejecución en la sentencia de iteración (siguiente iteración). 
goto 
La Sentencia goto puede ser utilizada para saltar directamente a una etiqueta. Una 
sentencia goto no puede ser utilizada para saltar adentro de un bloque de sentencias. Su uso 
podría ser empleado en sentencias switch o para transferir el control fuera de un loop anidado. 
Nota, como buena práctica no se recomienda el uso de goto. 
return 
La Sentencia return regresa a la función invocadora y opcionalmente retorna un valor. 
Asignación Definitiva 
Las reglas de Asignación definitiva previenen la observación del valor de una variable no 
asignada, ya que C# no permite utilizar variables que no han sido inicializadas, así como 
también no pueden realizarse operaciones con variables de clase que no han sido inicializadas. 
Puede accederse al elemento de un arreglo aún si no ha sido inicializado, ya que el compilador 
no puede rastrear la asignación definitiva en todas las situcaciones. 
Precedencia de Operadores 
Cuando una expresión contiene múltiples operadores, la precedencia de operadores controla el 
orden en el cual los elementos de la expresión son evaluados. 
Categoría Operador 
Primary 
(x), x.y, f(x), a[x], x++, x--, new, typeof, sizeof, 
checked, unchecked 
Unary +, -, !, ~, ++x, --x, (T)x 
Multiplicative *, /, % 
Additive +, - 
Shift <<, >> 
Relational <, >, <=, >=, is 
Equality ==, != 
Logical AND & 
Logical XOR ^ 
Logical OR | 
Conditional 
AND 
&& 
Conditional OR || 
Conditional ?: 
Assignment =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= 
typeof 
El operador typeof regresa el tipo del objeto, el cual es una instancia de la clase System.Type
Una instancia ya existente puede obtener el tipo de objeto con el método de la 
instancia GetType(). 
is 
El operador is es utilizado para determinar cuando una referencia a un objeto puede ser 
converitda a un tipo específico ointerface. 
El operador as es muy similar al operador is, pero en vez de determinar cuando un objeto es 
un tipo específico ointerface, as también realiza la conversión explicita a ese tipo o interface y si 
no lo puede convertir el operador regresanull. 
Utilizar as es más eficiente que utilizar is, porque as sólo necesita checar el tipo del objeto una 
vez e is checa el tipo cuando el operador es utilizado y lo checa otra vez cuando la conversión 
se realiza. 
Utilizando is 
if(UnObjeto is UnTipo){ 
UnTipo ut = (UnTipo) UnObjeto; 
} 
Utilizando as 
UnTipo ut = UnObjeto as UnTipo; 
if(ut != null){ 
sentencias; 
} 
Conversiones 
En C# las conversiones se dividen en conversiones explícitas y conversiones implícitas que son 
aquellas que podrían siempre ocurrir: 
//conversiones implícitas 
sbyte a = 55; 
short b = a; 
int c = b; 
long d = c; 
//conversiones explícitas 
c = (int) d; 
b = (short) c; 
a = (sbyte) b; 
A continuación se presenta la jerarquía de conversión en C## 
Manejo de Excepciones 
Las excepciones son el método fundamental de manejo de condiciones de error. 
Ejemplo: 
using System; 
class DivisionCero{ 
public static void Main(){ 
int iA = 33; 
int iB = 0; 
try{ 
//Sentencia que puede lanzar una excepción 
Console.WriteLine("{0}/{1} = {2}", iA ,iB, iA/iB); 
}catch(Exception e){ 
//Manejo de la excepción 
Console.WriteLine("La operación {0}/{1} genero la 
excepcion : {2}", iA, iB, e); 
} 
Console.WriteLine("Continua la ejecución del código..."); 
}
} 
El ejemplo encierra el bloque de código que podría lanzar una excepción con try. En caso de 
generarse una excepción elruntime .NET detiene la ejecución del código y busca el 
bloque try en el cual la excepción tuvo lugar, entonces busca si este bloque tiene relacionado 
un bloque catch, puede ser que encuentre más de un bloque catch relacionado al 
bloque tryque genero la excepción, por lo que se determina que bloque catch es el que mejor 
y ejecuta el código que contiene. 
El compilador de C# puede manejar silenciosamente situaciones que podrían producir un error 
sin notificarnos explicitamente de ello, por ejemplo una situación como un overflow que es 
cuando el cálculo de una operación excede el rango válido de resultados posibles para el tipo de 
dato. 
El caso del código para calcular un factorial, el compilador no prodruce una advertencia, pero si 
trata de obtener elfactorial de 2000 dara por resultado 0, el compilador actuo en modo 
silencioso porque por default el compilador tiene deshabilitada la opción de chequeo 
de overflow. 
Es posible cambiar el comportamiento de chequeo de overflow utilizando un switch al compilar. 
Jerarquía de Excepciones 
Todas las excepciones derivan de la clase Exception, la cual es parte del lenguaje común en 
tiempo de ejecución (CLR), donde la propiedad catch determina por coincidencia el tipo de 
excepción a el nombre de la excepción generada. Un bloque catch con una coincidencia 
especifica hacen más general la excepción: 
using System; 
class ExceptionDivision0{ 
public static void Main(){ 
int iA = 33; 
int iB = 0; 
try{ 
Console.WriteLine("{0}/{1} = {2}", iA ,iB, 
iA/iB); 
}catch(DivideByZeroException){ 
Console.WriteLine("Se genero la excepcion : 
DivideByZeroException"); 
} 
Console.WriteLine("Continua la ejecución del 
código..."); 
} 
} 
En este ejemplo el bloque catch que atrapa la excepción DivideByZeroException es una 
coincidencia más específica, por lo que es la única que será ejecutada, pero si además de 
escribir el catch para DivideByZeroException escribe el catch paraException, el 
compilador le notificara que existe una excepción que atrapa todas las excepciones y esto es 
porqueException ocupa la cima de la jerarquía de todas las excepciones. 
Trabajar con Excepciones 
Existen tres formas básicas de trabajar con excepciones: 
Caller Beware 
La primer forma es no atrapar la excepción, lo cual provocará dejar al objeto en un estado 
incorrecto, y causará daños cuando el caller trate de utilizarla de nuevo. 
Caller Confuse 
La segunda forma es atrapar la excepción y tratar de hacer acciones que dejen la operación 
como estaba hasta antes de generarse la excepción y entonces relanzar la excepción, esto 
usualmente es lo menos que se esperaria del manejo de excepciones ya que un objeto debería 
siempre mantener un estado válido después de generarse una excepción.
Se llama Caller Confuse, porque después de generase una excepción, el caller con frecuencia 
tiene poca información respecto al entendimiento de los detalles de la excepción o como podría 
ser solucionada. 
Caller Inform 
Las tercer forma Caller Inform agrega información que es devuelta al usuario, la excepción 
atrapada es envuelta en una excepción que tiene información adicional: 
using System; 
class ExcDivZeroInf{ 
public static void Main(){ 
int iA = 33; 
int iB = 0; 
try{ 
Console.WriteLine("{0}/{1} = {2}", iA ,iB, 
iA/iB); 
}catch(DivideByZeroException e){ 
Console.WriteLine("Se genero la excepcion : 
DivideByZeroException"); 
throw(new 
DivideByZeroException("Información adicional...", e)); 
} 
Console.WriteLine("Continua la ejecución del 
código..."); 
} 
} 
Chequeo de Overflow 
Si requerimos controlar el chequeo de overflow para la aplicación completa, el compilador de C# 
debe establecerse comochecked. Por default el compilador tiene deshabilitada la opción de 
chequeo. 
Para indicar explicitamente que el compilador cheque el overflow escriba: 
csc Factorial.cs /checked+ 
Una vez que se compilo de con la opción de chequeo de overflow habilitado, al intentar obtener 
el factorial de 2000 de presenta la ventana Just-In-Time-debbuging notificandonos que 
ocurrio una excepción en Factorial.exe: 
System.OverflowException 
Este tipo de situaciones es posible atraparlas y manejarlas a través de las excepciones que se 
producen. 
Chequeo programático de Overflow 
Existe otra opción si es que no deseamos activar el Chequeo de Overflow para la aplicación 
completa y habilitar sólamente partes especificas de código, para ello se utiliza la 
sentencia checked: 
using System; 
class FactorialChecked{ 
public static void Main(string[] aArgs){ 
if(aArgs.Length == 0){ 
Console.WriteLine("Debe proporcionar un argumento, 
Ejemplo: Factorial 5"); 
return; 
} 
long lFactorial = 1; 
long lCalcular = Int64.Parse(aArgs[0]); 
long lAux = 1; 
for(lAux = 1; lAux <= lCalcular; lAux++){
checked{lFactorial *= lAux;} //Habilitar chequeo 
de overflow 
//Test Line Console.WriteLine("{0}! * {1}", lAux, 
lFactorial); 
} 
Console.WriteLine("{0}! es {1}", lCalcular, lFactorial); 
} 
} 
También es posible hacer el caso contrario, es decir, indicar que no se realice el chequeo 
de overflow para partes especificas de código, para ello se utiliza la sentencia: 
unchecked{sentencias;} 
Sentencias para el Manejo de Excepciones 
Es posible atrapar, manejar y limpiar las excepciones que se producen utilizando las sentencias 
siguientes: 
 try - catch 
 try - finally 
 try - catch - finally 
try - catch 
Para evitar que se muestre el mensaje que indica que una excepción ocurrio, es 
necesario atrapar la excepción y lo mejor de todo es que continue la ejecución del programa, 
para ello se utiliza try y catch. 
try contiene el código que quizá pueda lanzar una excepción y catch maneja la excepción si 
esta ocurre: 
try{ 
//sentencias que pueden lanzar una excepción 
}catch(nombreExcepción){ 
//manejo de la excepción 
} 
El siguiente ejemplo maneja la excepción FileNotFoundException que se produce cuando se 
intenta manipular un archivo que no existe, si esto ocurre se presenta un mensaje que muestra 
el nombre del archivo que se intento manipular y no se encontro a través de una propiedad 
pública de la excepción llamada FileName. 
using System; 
using System.IO; 
class SentenciaWhile{ 
public static void Main(){ 
try{ 
StreamReader SR = 
File.OpenText("test.html"); 
String strLinea = null; 
while(null != (strLinea = SR.ReadLine())){ 
Console.WriteLine(strLinea); 
} 
SR.Close(); 
}catch(FileNotFoundException e){//En caso de que 
el archivo no exista 
Console.WriteLine("No se encontro el 
archivo : " + e.FileName); 
return; 
} 
} 
} 
try - finally
Es posible limpiar el manejo de errores utilizando try y el constructor finally, sin eliminar el 
mensaje de error, pero el código contenido en el bloque finally es ejecutado aún después de 
ocurrir una excepción. 
El siguiente código maneja una variable booleana que indica si se produjo un error, simplemente 
poniendola dentro del bloque try, si el código contenido fué ejecutado la variable booleana 
es false lo cual indica que no ocurrieron excepciones, si el bloque no se ejecuto la variable 
booleana mantiene su valor inicial lo cual significa que si ocurrieron excepciones y entonces se 
ejecuta el bloque Finally el cual evalua el valor de la variable booleana y presenta la 
indicación correspondiente. 
using System; 
using System.IO; 
class SentenciaTryFinally{ 
public static void Main(){ 
bool bExcepcion = true; 
try{ 
StreamReader SR = 
File.OpenText("test.html"); 
String strLinea = null; 
while(null != (strLinea = SR.ReadLine())){ 
Console.WriteLine(strLinea); 
} 
SR.Close(); 
bExcepcion = false; 
} 
finally{ 
if(bExcepcion){ 
Console.WriteLine(">>> No se 
encontro el archivo"); 
}else{ 
Console.WriteLine(">>> No 
ocurrieron excepciones"); 
} 
} 
} 
} 
Note que en caso de no existir el archivo se produce una excepción y se presenta el mensaje 
que indica que ha ocurrido una excepción pero también fué ejecutado el bloque finally, el 
código que contiene el bloque finally siempre es ejecutado ocurra o no una excepción. 
Puede emplear la sentencia finally para reestablecer los valores previos a la generación de la 
excepción. 
try - catch - finally 
Combinar try para controlar el código que puede lanzar excepciones, atrapar la excepción 
con catch y llevar acabo instrucciones necesarias con finally hacen una mejor solución 
cuando ocurren las excepciones. 
Es posible utilizar una sentencia catch por cualquier excepción que pudiera ocurrir, es decir, 
tener más de un bloquecatch, pero es necesario conocer la jerarquía de las excepciones porque 
puede ocurrir que un bloque previo catch sea más general y contenga todas las excepciones lo 
cual produciria un error. 
using System; 
using System.IO; 
class SentenciaTryCatchFinally{ 
public static void Main(){ 
bool bExcepcion = true;
bool bModificacion = false; 
try{ 
bModificacion = true; 
StreamReader SR = File.OpenText("test.htmlX"); 
String strLinea = null; 
while(null != (strLinea = SR.ReadLine())){ 
Console.WriteLine(strLinea); 
} 
SR.Close(); 
bExcepcion = false; 
}catch(FileNotFoundException e){//En caso de que el 
archivo no exista 
Console.WriteLine("No se encontro el archivo : " + 
e.FileName); 
return; 
} 
finally{ 
if(bExcepcion){ 
bModificacion = false;//Valor antes de 
generarse la excepción 
if(!bModificacion){ 
Console.WriteLine("Entro en modo 
modificación, _ 
pero las modificaciones no se 
realizaron"); 
} 
Console.WriteLine("Causa : No se encontro 
el archivo"); 
}else{ 
Console.WriteLine("No ocurrieron 
excepciones"); 
} 
} 
} 
} 
Lanzamiento de Excepciones 
Para atrapar una excepción con la sentencia catch primero debe generarse la excepción, pero 
es posible que a través de codigo se lanze o invoque una excepción: 
throw new NombreExcepcion(excepcion); 
El poder lanzar o invocar una excepción es util cuando no se ha contemplado cierto escenario o 
para nuevos escenarios, al crear una clase podrian crearse también excepciones propias de esta 
clase. 
A continuación se presenta una tabla que contiene las excepciones estándar que provee 
el runtime: 
Tipo Descripción 
Exception Clase base para todas los objetos exception 
SystemException 
Clase base para todos los errores generados en 
tiempo de ejecución 
IndexOutRangeException 
Lanzada en tiempo de ejecución cuando el índice de 
un arreglo está fuera de rango 
NullreferenceException 
Disparada en tiempo de ejecución cuando un objeto 
null es referenciado 
InvalidOperationException 
Lanzada por ciertos métodos cuando invocan a 
métodos que son inválidos para el estado de los
objetos actuales 
ArgumentException 
Clase base de todos los argumentos de las 
excepciones 
ArgumentNullException 
Lanzada por un método, en caso de que un 
argumento sea null cuando no sea permitido 
ArgumentOutOfRangeException 
Lanzada por un método cuando un argumento no 
está en el rango permitido 
InteropException 
Clase base para excepciones que son originadas u 
ocurren en ambientes fuera del CLR 
ComException Excepción que contiene información HRESULT COM 
SEHException 
Excepción que encapsula información del manejo de 
excepciones destructurada Win32 
Relanzamiento de Excepciones 
El siguiente código muestra como es posible atrapar una excepción, manejarla y se volverla a 
invocar: 
using System; 
class FactorialCheckedReThrow{ 
public static void Main(string[] aArgs){ 
if(aArgs.Length == 0){ 
Console.WriteLine("Debe proporcionar un argumento, 
Ejemplo: Factorial 5"); 
return; 
} 
long lFactorial = 1; 
long lCalcular = Int64.Parse(aArgs[0]); 
long lAux = 1; 
try{ 
checked{ //Habilitar chequeo de overflow 
for(lAux = 1; lAux <= lCalcular; lAux++){ 
lFactorial *= lAux; 
//Test Line Console.WriteLine("{0}! * {1}", 
lAux, lFactorial); 
} 
} 
}catch(OverflowException){ 
Console.WriteLine("El factorial {0}! causo una excepción", 
lCalcular); 
throw; 
} 
Console.WriteLine("{0}! es {1}", lCalcular, lFactorial); 
} 
} 
Creación de Clases Exception 
Es recomendable utilizar las clases predefinidas para excepciones, para ciertos escenarios es 
posible crear clases de excepciones apropiadas, por ejemplo cuando creamos una clase, también 
podemos crear excepciones para esta clase, esto es conveniente cuando se utiliza la clase y 
manejar posibles escenarios de error con las clases de excepciones creadas. 
using System;
public class MiExcepcion:Exception{ 
public MiExcepcion(string str):base(str){} 
} 
public class AplicMisExcepciones{ 
public static void Probar(){ 
throw new MiExcepcion("ocurrio un error"); 
} 
public static void Main(){ 
try{ 
AplicMisExcepciones.Probar(); 
}catch(Exception e){ 
Console.WriteLine(e); 
} 
} 
} 
Se deben tomar ciertas consideraciones al crear excepciones: 
 Al lanzar una excepción se debe proporcionar un texto significativo 
 Lanzar excepciones solo cuando se presente una condición realmente excepcional 
 Lanzar un ArgumentException si el método o propiedad envio mal los parámetros 
 Lanzar un InvalidOperationException cuando la operación invocada no es apropiada 
para el estado actual de los objetos 
 Lanzar la excepción más apropiada 
 Usar excepciones encadenadas 
 No utilizar excepciones para errores esperados o normales 
 No usar excepciones para control o flujo normal 
 No lanzar NullReferenceException o IndexOutOfRangeException en métodos 
Componentes 
Así como es posible escribir clases y hacer uso de estas en un mismo archivo, también es 
posible escribir en un archivo (ejecutable) unicamente la clase lo cual es conocido 
como componente y en otro archivo (también ejecutable) el uso de la clase lo cual es conocido 
como cliente. 
Creación de Componentes 
Para crear un componente unicamente es necesario escribir la Clase con todos sus miembros 
(Constructores, Propiedades, Métodos), almacenarla en un archivo y compilar el componente. 
using System; 
public class ClsPersona{ 
private int iSueldo; 
public int Sueldo{ 
get{return iSueldo;} 
set{iSueldo = value;} 
} 
public string sNombre; 
public void AsignarNombre(string sValor){ 
sNombre = sValor; 
} 
public string ObtenerNombre(){ 
return sNombre; 
} 
} 
Compilación de Componentes
Al compilar un componente se crea una biblioteca y no se crea una aplicación, como es el caso 
cuando se trabaja en un sólo archivo la clase y la aplicación que hace uso de la clase: 
csc /t:library /out:ClsPersona.dll ClsPersona.cs 
Si utiliza nombres de espacio: 
csc /r:System.nombreBiblioteca.dll /t:library /out:NombreComponente.dll 
NombreComponente.cs 
Donde el switch /t:library indica al compilador de C# crear una biblioteca y no buscar el 
método estático Main. 
Si su clase requirio de nombres de espacio (namespace) es necesario hacer referencia a estas 
bibliotecas mediante elswitch /r:NombreBiblioteca.dll 
Recuerde que el switch /out no es obligatorio ya que se utiliza para especificar un nombre 
distinto al nombre del archivo fuente, aunque es buena práctica especificarlo ya que algunas 
aplicaciones no sólo tienen un archivo fuente, entonces el compilador podría llamar a la 
biblioteca con el primer nombre de archivo fuente en la lista. 
Una vez que ha compilado su componente puede escribir una Aplicación Cliente. 
Creación de una Aplicación Cliente 
Para hacer uso de los componentes creados es posible crear aplicaciones cliente donde se creen 
instancias de las clases creadas: 
using System; 
class AplicClsPersona{ 
public static void Main(){ 
ClsPersona Empleado = new ClsPersona(); 
Empleado.Sueldo = 33; 
Console.WriteLine("Edad : " + Empleado.Sueldo); 
Empleado.AsignarNombre("Gerardo Ángeles Nava"); 
Console.WriteLine("Nombre : " + 
Empleado.ObtenerNombre()); 
} 
} 
Compilación de Clientes 
Es necesario indicarle al compilador una referencia a la nueva biblioteca de componentes DLL: 
csc /r:NombreComponente.dll NombreCliente.cs 
Nota para hacer uso de una clase es necesario que tenga el modificador de acceso public. 
Namespaces 
Los nombres de espacio namespace en el runtime .NET son utilizados para organizar las clases 
y otros tipos en una estructura jerarquica. El propósito del uso de namespace hacen las clases 
fáciles de usar y prevenir colisiones con las clases escritas por otros programadores. 
Los namespace en C# se utilizan para organizar programas y la jerarquía natural de la 
organización facilita presentar los elementos de un programa a otros programas. 
Los namespace son utiles para la organización interna de aplicaciones. 
Un namespace contiene tipos que pueden ser utilizados en la construcción de programas: 
clases, estructuras, enumeraciones, delegados e interfaces, por ejemplo para poder escribir a la 
consola se utiliza el namespace System. 
No es obligatorio jerarquizar los namespace pero es una buena práctica organizar 
los namespace creados de manera jerarquica para dar claridad a la aplicación. 
Los nombres de espacio son definidos utilizando la sentencia: 
namespace 
Para múltiples niveles de organización los namespaces pueden ser anidados: 
namespace NombreNamespaceA{ 
namespace NombreNamespaceB{ 
class NombreClase{
public static void Function(){} 
} 
} 
} 
El código anterior puede ser simplificado de la siguiente manera: 
namespace NombreNamespaceA.NombreNamespaceB{ 
class NombreClase{ 
public static void Function(){} 
} 
} 
Las colisiones entre tipos o nombres de espacio que tienen el mismo nombre se pueden resolver 
utilizando una variante de la cláusula using que permite definir un alias para la clase: 
using Alias = System.Console; 
class NombreClase{ 
public static void Main(){ 
Alias.WriteLine("Alias de una clase"); 
} 
} 
Envolver Clases en Namespace 
Es posible envolver (wrapping) las clases en un namespace, para ello sólo es necesario utilizar la 
palabra reservadanamespace seguida de un nombre que lo identifique y encerrar entre llaves el 
código que deseamos pertenezca a este. 
using NombreOtrosEspacios; 
namespace NombreEspacio{ 
public class NombreClase{ 
//propiedades 
//métodos 
} 
} 
Ejemplo: 
using System; 
namespace informatique.com.mx{ 
public class iPersona{ 
private int iSueldo; 
public int Sueldo{ 
get{return iSueldo;} 
set{iSueldo = value;} 
} 
public string sNombre; 
public void AsignarNombre(string sValor){ 
sNombre = sValor; 
} 
public string ObtenerNombre(){ 
return sNombre; 
} 
} 
} 
Compilar: 
csc /t:library /out:iPersona.dll iPersona.cs 
Utilizar Namespace en Aplicaciones Cliente 
Al desarrollar componentes utilizando namespace la aplicación cliente debe importarlo: 
using NombreEspacio;
Otra posibilidad es hacer una referencia absoluta a los elementos del namespace, aunque para 
evitar conflictos es preferible utilizar la directiva: 
Ejemplo: 
using System; 
using informatique.com.mx; 
class iAplicClsPersona{ 
public static void Main(){ 
iPersona Empleado = new iPersona(); 
Empleado.Sueldo = 33; 
Console.WriteLine("Edad : " + Empleado.Sueldo); 
Empleado.AsignarNombre("Gerardo Ángeles Nava"); 
Console.WriteLine("Nombre : " + 
Empleado.ObtenerNombre()); 
} 
} 
Compilar: 
csc /r:iPersona.dll iAplicPersona.cs 
Especificar de manera absoluta el namespace: 
using System; 
class iAplicClsPersonaAbs{ 
public static void Main(){ 
informatique.com.mx.iPersona Empleado = new 
informatique.com.mx.iPersona(); 
Empleado.Sueldo = 33; 
Console.WriteLine("Edad : " + Empleado.Sueldo); 
Empleado.AsignarNombre("Gerardo Ángeles Nava"); 
Console.WriteLine("Nombre : " + 
Empleado.ObtenerNombre()); 
} 
} 
Compilar: 
csc /r:iPersonaAbs.dll iAplicPersonaAbs.cs 
Agregar Múltiples Clases a Un Namespace 
En el punto anterior envolvimos una clase a un namespace, pero es posible agregar y envolver 
más clases o componentes al mismo namespace, sin importar que esten contenidos en 
diferentes archivos, después de la compilación son parte del mismo namespace: 
csc /t:library /out:NombreComponente.dll archivoFuente1.cs 
archivoFuente2.cs 
Agregando otra clase al namespace informatique.com.mx: 
using System; 
namespace informatique.com.mx{ 
class iVehiculo{ 
private int iRueda; 
private int iPuerta; 
private int iVentana; 
private int iHelice; 
private int iMotor; 
private int iAsiento; 
private string sTipo;//Aereo, anfibio, terrestre, 
espacial
//Constructor 
public iVehiculo(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; 
} 
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;} 
} 
} 
} 
Compilar: 
csc /t:library /out:informatique.com.mx.dll iPersona.cs iVehiculo.cs 
Aplicación Cliente: 
using System; 
using informatique.com.mx; 
class iAplic{ 
public static void Main(){
iPersona Empleado = new iPersona(); 
Empleado.Sueldo = 33; 
Console.WriteLine("Edad : " + Empleado.Sueldo); 
Empleado.AsignarNombre("Gerardo Ángeles Nava"); 
Console.WriteLine("Nombre : " + 
Empleado.ObtenerNombre()); 
//Clase que está contenida en otro archivo 
iVehiculo MiAvion = new 
iVehiculo(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); 
} 
} 
Compilar: 
csc /r:informatique.com.mx.dll iAplic.cs 
Recuerde que sus clases deben tener el modificador de acceso public, de lo contrario el 
compilador notificara un error (error CS0122: is inaccessible due to its protection level), por lo 
que tendrá que agregar el modificador de acceso yvolver a compilar. 
Namespace y Ensambles 
Un objeto puede ser utilizado desde un archivo fuente C# sólo si ese objeto puede ser localizado 
por el compilador C#, por default el compilador sólo abre el ensamble conocido 
como mscorlib.dll, el cual contiene las funciones principales para elCLR. 
Para referenciar objetos localizados en otros ensambles, el nombre del archivo de ensamble 
debe ser pasado al compilador, esto es posible utilizando un switch al compilar: 
/r:nombreEnsamble 
Es así como se crea un correlación entre el namespace de un objeto y el nombre del ensamble 
en el cual reside, por ejemplo los tipos de namespace en el System.Net residen en el 
ensamble System.Net.dll 
Compilación Condicional 
La Compilación Condicional permite excluir o incluir código, en C# existen dos formas de 
hacer esto: 
 Uso del Preprocesador 
 Atributo conditional 
Uso del Preprocesador 
C# el compilador emula el preprocesador, ya que no hay un preprocesador por separado. 
El compilador de C# no soporta macros, en cambio soporta las siguientes directivas: 
 Definición de símbolos 
 Exclusión de código basado en símbolos 
 Lanzamiento de errores y advertencias 
Definición de símbolos
La Definición de símbolos es utilizada para excluir o incluir código dependiendo si son o no 
son definidos ciertos símbolos. 
Una forma para definir un símbolo es utilizando la directiva #define en un archivo fuente C#, 
está definición deberá realizarse antes de cualquier otra sentencia: 
#define DEBUG 
#define RELEASE 
En este caso #define DEBUG, define un símbolo DEBUG y su ámbito es el archivo donde es 
definido, al igual que el símbolo RELEASE. 
Otra forma utilizada para definir símbolos es usar el compilador y es de ámbito global para todos 
los archivos: 
csc /define:DEBUG nombreArchivo.cs 
Si se requiere definir múltiples símbolos utilizando el compilador, es necesario separar cada 
símbolo con una coma (,) 
csc /define:DEBUG,RELEASE,DEMOVERSION nombreArchivo.cs 
Si el código fuente incluye directivas de definición de símbolos, es posible deshabilitarlas 
utilizando la directiva #undef cuyo ámbito también corresponde al archivo donde es definida: 
#undef DEBUG 
Exclusión de código basado en símbolos 
El principal propósito de los símbolos es la inclusión o exclusión condicional del código, basado 
sobre si son o no son definidos los símbolos. 
El siguiente código no define símbolos en el archivo fuente: 
. 
sentencia; 
. 
#if NOMBRE_SIMBOLO 
sentencia; 
#else 
sentencia; 
#endif 
. 
Pero es posible definir o no los símbolos al compilar la aplicación: 
csc /define:NOMBRE_SIMBOLO NombreAplicacion.cs 
Las directivas del preprocesador emulado utilizadas para evaluar el símbolo 
son: #if, #else y #endif las cuales actuan como su contraparte, la sentencia 
condicional if C#, es posible utilizar &&, || y !: 
//#define SIMBOLO_A 
#define SIMBOLO_B 
#define SIMBOLO_C 
#if SIMBOLO_A 
#undef SIMBOLO_C 
#endif 
using System; 
class NombreClase{ 
public static void Main(){ 
#if SIMBOLO_A 
. 
#elif SIMBOLO_B && SIMBOLO_C 
. 
#else 
. 
#endif
} 
} 
Lanzamiento de errores y advertencias 
Existe otro uso de las directivas del preprocesador para lanzar errores del compilador o 
advertencias dependiendo de ciertos símbolos, para ello se utilizan las directivas: 
 #warning 
 #error 
Por ejemplo 
#if SIMBOLO_ERROR 
#error Presentar el mensaje de error correspondiente 
#endif 
. 
#if SIMBOLO_WARNING 
#error Presentar el mensaje de advertencia correspondiente 
#endif 
Atributo conditional 
Un atributo conditional evalua la invocación de una función cuando sierto símbolo es definido 
y evalua a nada cuando una versión liberada es construida. 
Un atributo conditional debe tener un tipo void de regreso, cualquier otro tipo de regreso 
no es permitido. 
. 
[conditional("NOMBRE_SIMBOLO")] 
método A 
[conditional("NOMBRE_SIMBOLO")] 
método B 
. 
Comentarios de Documentación en XML 
Es posible construir automáticamente la Documentación utilizando comentarios en el código. 
La salida que es generada por el compilador es XML puro y puede ser utilizada como entrada 
para la documentación de un componente. 
La documentación es una parte extremadamente importante del software y en especial de los 
componentes por ser utilizados por otros desarrolladores. 
Elementos XML 
Nota: todo comentario de documentación son tags XML (eXtensible Markup Language). 
Para describir un elemento se utiliza el tag <summary></summary>, el cual se escribe en el 
código fuente anteponiendo tres diagonales que son indicativo de un comentario de 
documentación: 
///<summary>Descripción...</summary> 
En caso de requerir más una línea para el comentario de documentación utilice el tag: 
///<para> 
///. 
///. 
///</para> 
En caso de requerir una referencia a otros elementos utilice el tag: 
///<see cref="NombreElemento"/> 
En caso de requerir una referencia a un tópico de interes utilice: 
///<seealso cref="System.Net"/> 
Un tag contrario a summary, para un volumen mayor de documentación es: 
///<remarks> 
Es posible incluir listas utilizando los tags : 
///<list type="bullet"> 
/// <item>Constructor
/// <see cref="Constructor()"/> 
/// <see cref="Constructor(string)"/> 
/// </item> 
///</list> 
Para describir parámetros se utiliza el tag: 
///<paramref name="nombreParametro"/> 
Es posible encerrar en un tag un ejemplo completo e incluir la descripción y código: 
///<example> 
/// . 
/// <code> 
/// . 
/// </code> 
/// . 
///</example> 
Para describir los parámetros de regreso utilice: 
///<returns> 
/// <para>true: valor obtenido</para> 
/// <para>false: valor no obtenido</para> 
///</returns> 
Para describir propiedades de clase se utiliza un tag especial: 
///<value>Propiedad...</value> 
Una vez que el proceso de documentación para un componente es terminado, es posible crear 
un archivo XML basado en la documentación descrita en el código fuente y hacerla disponible a 
quienes utilicen el componente, para ello simplemente se utiliza al compilar 
el switch /doc: seguido del documento XML. 
csc /doc:Persona.xml AplicPersona.cc 
Ejemplo: 
using System; 
/// 
/// Clase Persona 
/// Autor : Gerardo Ángeles Nava 
/// Fecha : 10/09/2005 
/// Descripción : Clase que define lo que es y puede hacer una 
persona 
/// 
/// 
class Persona{ 
///La propiedad iSueldo se emplea para obtener y asignar el valor 
del sueldo de una persona 
private int iSueldo; 
public int Sueldo{ 
get{return iSueldo;} 
set{iSueldo = value;} 
} 
public string sNombre; 
///Utilizado para asignar el valor de la propiedad sNombre 
public void AsignarNombre(string sValor){ 
sNombre = sValor; 
}
public string ObtenerNombre(){ 
return sNombre; 
} 
} 
class AplicPersona{ 
public static void Main(){ 
Persona Empleado = new Persona(); 
Empleado.Sueldo = 33; 
Console.WriteLine("Edad : " + Empleado.Sueldo); 
Empleado.AsignarNombre("Gerardo Ángeles Nava"); 
Console.WriteLine("Nombre : " +Empleado.ObtenerNombre()); 
} 
} 
Salida: para ver el documento XML generado de clic aquí. El compilador realizará validación 
sobre los tags XML en el archivo fuente, en caso de existir errores el compilador lo notificará y 
aún así el documento XML es generado pero tendrá un mensaje de error. 
El documento XML tendrá algunos identificadores seguidos de dos puntos (:) y la ruta 
del namespace, los cuales por ejemplo tienen los siguiente significados. 
 N, denota un namespace 
 T, identifica un tipo, el cual puede ser 
una clase, interface, estructura, enumeración o delegado 
 F, describe un campo o clase 
 P, se refiere a una propiedad la cual también puede ser un indíce o propiedad índice. 
 M, identifica un método, incluyendo constructores y operadores. 
 E, denota eventos 
 !, denota un error el cual provee información acerca de una liga que el compilador C# 
no pudo resolver. 
Si un elemento tiene períodos en su nombre, estos son reemplazados por el símbolo #. Los 
parámetros para métodos son encerrados entre paréntesis y separados por comas (,). 
Componentes .NET 
El compilador de C# por default siempre crea componentes .NET para los ejecutables. 
Un Componente .NET es la unidad fundamental reusable y compartida en el CLR, un 
componente .NET también es limitante para asegurar la seguridad, permite la distribución de 
clases y resolución de tipos. Una aplicación puede contener múltiples componentes .NET 
Un componente .NET contiene cuatro partes referentes al número de versión llamado versión 
compatible: 
major version.minor version.build number.revision 
La versión compatible es utilizada por el class loader para decidir cual es la versión del 
componente .NET que cargará, en caso de existir diferentes versiones. 
Se considera una versión incompatible cuando major version.minor version es 
diferente de la versión solicitada. 
Se podría considerar una versión compatible cuando el build number es diferente a la 
versión solicitada. 
Se considera una QFE (Quick Fix Engineering) compatible cuando revision es diferente. 
Además del número de versión (versión compatible) se almacena otro número en el 
componente llamado informational version, el cual es considerado sólo para propósitos de 
documentación y su contenido podría ser SuperControl Build 1880, el contenido 
representa algo humano y no para la máquina. 
Para indicar al compilador que agregue un version information al componente se utiliza 
el switch:
csc /a.version:1.0.1.0 /t:library /out:nombreArchivoFuente.dll 
nombreArchivoFuente.cs 
El switch /a.version crea una biblioteca con el version information 1.0.1.0, esto puede 
comprobarse en las propiedades del archivo.dll. 
Componentes .NET Privados 
Al ligar una aplicación a un componente .NET utilizando 
el switch /reference:nombreBiblioteca la información de dependencia registra las 
herramientas de desarrollo, incluyendo la versión de las bibliotecas ligadas, este registro se 
hace en un manifiesto y el CLR los números de versión contenidos para cargar la versión 
apropiada de un componente .NET dependiente en tiempo de ejecución. 
Cualquier componente .NET que reside en el directorio de la aplicación es 
considerado privado y no es version-checked 
Componentes .NET Compartidos 
Si se requiere construir software que se comparta con otras aplicaciones, el componente .NET 
deberá ser instalado comocompartido. 
Interoperabilidad COM 
COM es una técnica de interoperabilidad, por lo que los clientes .NET deberán ser capaces de 
invocar componentes COM y componentes COM deberán hacer uso de los nuevos componentes 
.NET, esta es una característica de iteroperabilidad proporcionada por la plataforma .NET para 
todos los lenguajes de programación que emiten código administrado. 
Uso de Componentes .NET en Componentes COM 
La interoperabilidad permite a clientes COM utilizar componentes .NET, para hacer esto posible 
en COM primero es necesario registrar un objeto antes de poder ser utilizado, para registrar un 
objeto COM se utiliza la aplicación regsvr32 y para registrar un componente .NET se 
utiliza regasm.exe, esta herramienta permite registrar un componente .NET en elRegistry y 
también crear un archivo Registry. 
Si requiere examinar las entradas agregadas al Registry escriba en la línea de comandos: 
regasm nombreComponente.dll /regfile:nombreArchivoComponente.reg 
Ahora puede examinar el archivo generado nombreArchivoComponente.reg. 
Nota asegurese de que el directorio en el que se encuentra exista el archivo .dll o escriba la 
ruta completa de su ubicación. 
Ejemplo: 
REGEDIT4 
[HKEY_CLASSES_ROOTinformatique.com.mx.iPersona] 
@="informatique.com.mx.iPersona" 
[HKEY_CLASSES_ROOTinformatique.com.mx.iPersonaCLSID] 
@="{37504224-213A-3943-845A-E572758E4174}" 
[HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174}] 
@="informatique.com.mx.iPersona" 
[HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} 
InprocServer32] 
@="mscoree.dll" 
"ThreadingModel"="Both" 
"Class"="informatique.com.mx.iPersona" 
"Assembly"="iPersona, Version=0.0.0.0, Culture=neutral, 
PublicKeyToken=null" 
"RuntimeVersion"="v1.1.4322" 
[HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} 
InprocServer32.0.0.0]
"Class"="informatique.com.mx.iPersona" 
"Assembly"="iPersona, Version=0.0.0.0, Culture=neutral, 
PublicKeyToken=null" 
"RuntimeVersion"="v1.1.4322" 
[HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} 
ProgId] 
@="informatique.com.mx.iPersona" 
[HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} 
Implemented Categories_ 
{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}] 
El motor de ejecución mscoree.dll es invocado cuando una instancia del objeto (componente 
registrado) es requerida, más no la biblioteca por si sóla. 
El motor de ejecución es responsable de proveer la CCW (COM Callable Wrapper) al objeto. 
Una vez registrado el componente puede ser utilizado por lenguajes de programación que 
soporten esta vinculación, también es posible emplear la utileria tlbexp la cual permite generar 
una biblioteca tipo para el componente .NET 
tlbexp nombreComponente.dll /out:nombreBiblioteca.tlb 
Esta biblioteca tipo puede ser utilizada en lenguajes de programación que soporten esta 
vinculación. 
El Componente .NET y todas las clases ya están registradas y se tiene una biblioteca tipo para 
ambientes 
Uso de Componentes COM en Componentes .NET 
Los Clientes .NET pueden interoperar con objetos clásicos COM, para que un componente utilise 
COM debe tener una biblioteca tipo para el CLR esto se traduce a los metadatos que son 
almacenados con los tipos. 
Para que sea posible invocar un componente COM desde un objeto .NET, es necesario envolver 
el código unsafe, cuando la envoltura o wrapper es invocada, un RCW (Runtime Callable 
Wrapper) es construido desde la información de la biblioteca tipo. Una herramienta genera el 
código wrapper basado en la información obtenida de la biblioteca tipo. 
La herramienta a utilizar es tlimp (type library import): 
tlbimp nombreComponente.dll /out:nombreBiblioteca.dll 
Esta herramienta importa el tipo de biblioteca COM, crea y almacena un RCW que puede ser 
utilizado en el CLR en el archivo nombreBiblioteca.dll. 
Para ver los metadatos para el RCW utilice ildasm.exe, así podrá distinguir el nombre de la 
clase que fué generada para el objeto COM original, esta información es una ayuda para poder 
escribir el objeto .NET que utiliza el componente COM. 
Invocación de Servicios 
Se se requiere invocar una función provista por el WIN32 o alguna DLL unmanaged, se utilizan 
los Servicios de invocación de plataforma (PInvoke). 
PInvoke se encarga de ejecutar la función correcta y también de la colocación de los 
argumentos para y desde sus contrapartes unmanaged. 
Simplemente utilice el atributo sysimport al definir un método externo: 
[sysimport( 
dll = nombreDLL, 
name = puntoEntrada, 
cgarset = conjuntoCaracteres 
] 
Unicamente el argumento dll es obligatorio y los demás opcionales, aunque si se omite el 
atributo name, el nombre de la función de implementación externa debe coincidir con el nombre 
del método estático interno. 
Código No Seguro
Si requiere de escribir código no seguro, deberá utilizar dos palabras reservadas: 
 unsafe, denota un contexto no seguro, cuando requiera realizar acciones no seguras 
se debe envolver el código correspondiente con este modificador, el cual puede ser 
aplicado a constructores, métodos y propiedades. 
 fixed, al declarar una variable con este modificador previene al recolector de basura 
de reacomodarlo. 
Debugging 
El SDK .NET incorpora dos herramientas de depuración de errores: 
 CORDBG, depurador de línea de comando 
 SDK, depurador UI 
 El depurador SDK no soporta la depuración de código nativo, sólo es posible depurar 
código administrado. 
 No es posible la depuración de una máquina remota 
 Window register y disassembly aunque se implementan no son funcionales 
Antes de depurar el código de una aplicación es necesario crear una versión depurada, la cual 
contiene información de depuración no optimizada y un archivo adicional PDB (program 
database) para depuración y un estado de información del proyecto es creado. 
Para crear una versión depurada son necesarios dos switches al compilar: 
csc /optimize- /debug+ nombreArchivoFuente.cs 
Estos comandos utilizados al compilar crea dos archivos: 
nombreArchivoFuente.exe y 
nombreArchivoFuente.pdb 
Para configurar la sesión de depuración es necesario seleccionar la aplicación que se desea 
depurar y comenzar el depurador SDK ejecutando dbgurt.exe, el cual esta almacenado en el 
directorio ProgramFilesNGWSSDKGuiDebug 
Una vez que la aplicación depuradora comienza se selecciona el programa que se desea depurar 
en donde será también posible especificar argumentos en la línea de comandos, mismos que son 
pasados a la aplicación cuando la sesión de depuración inicia. 
Es posible establecer diferentes tipos de breakpoint: 
 File, interrumpe la ejecución cuando una ubicación específica en el archivo fuente se 
alcanza 
 Data, interrumpe la ejecución cuando una variable cambia a un valor especifico 
 Function, interrumpe la ejecución en una ubicación específica dentro de una función 
específica 
 Address, interrumpe la ejecución cuando una dirección de memoria específica se 
alcanza 
Una vez que se interrumpe la ejecución, es posible continuarla utilizando los comandos: 
 Step Over 
 Step Into 
 Step Out 
 Run to Cursor 
Es posible modificar valores de variables simplemente dando doble clic en la columna valor de 
aquella variable que se desea modificar, así como también es posible observar las variables, 
para ello es necesario dar clic en la columna nombre y escribir el nombre de las variables que se 
desean observar. 
Las excepciones son un punto excelente de comienzo para una sesión de depuración, cuando 
una excepción no es controlada apropiadamente por el código se muestra la ventana de 
depuración llamada JIT just in time.
Seguridad 
Hoy en día el código viene de distintas fuentes, no solo el que es instalado via un setup por el 
servidor, también puede ser instalado via una página web o correo electrónico. 
.NET plantea dos posibles soluciones para la seguridad: 
 Seguridad de acceso al código 
 Seguridad basada en roles 
Seguridad de acceso al código 
La Seguridad de acceso al código controla el acceso protegiendo los recursos y operaciones. 
El código es confiable en distintos grados, dependiendo su identidad y de donde viene. 
Funciones de la seguridad de acceso al código: 
 El administrador puede definir las políticas de seguridad asignando ciertos permisos 
para definir grupos de código. 
 El código puede requerir que quien invoca (caller) debe tener permisos especificos 
 La ejecución de código está restringido en tiempo de ejecución, realizando chequeos 
para verificar que los permisos otorgados a quien invoca (caller) coincidan con el 
permiso requerido para la operación. 
 El código puede requerir los permisos necesarios de ejecución y los permisos que 
podrían ser utilizados, es decir, verificar los permisos indispensables. 
 Los permisos son definidos para representar ciertos derechos para acceder a distintos 
recursos del sistema. 
 La seguridad de acceso al código otorga permisos cuando un componente es cargado, 
este otorgamiento esta basado en el requerimiento del código, definiendo operaciones 
permitidas por las políticas de seguridad. 
Existen dos puntos importantes de la seguridad de acceso al código, donde el requerimiento 
mínimo para beneficiarse de la seguridad de acceso al código es para generar un código de tipo 
seguro. 
 Verificar el tipo de seguridad del código administrado, el runtime forza la restricción 
de seguridad del código administrado, para determinar cuando el código es seguro. Es 
importante que el runtime sea capaz de checar los permisos de quien invoca de 
manera confiable, evadiendo hoyos de seguridad que son creados cuando código 
menos confiable invoca código altamente confiable, para ello el código administrado 
debe ser verificado como tipo seguro. Cada acceso a tipos se realiza sólo en un sentido 
permitido. 
El código C# es de tipo no seguro, pero el IL y los metadatos son inspeccionados antes 
de dar el visto bueno del tipo de seguridad del código. 
 Permisos que son requeridos por el código, el beneficio de activar el requerimiento de 
permisos es conocer cuando se tiene el permiso apropiado para realizar acciones y 
cuando no. Es posible prevenir al código de el otorgamiento de permisos adicionales 
que no son necesarios. Los permisos mínimos garantizan que el código se ejecute con 
los recursos justos cuando el código requiere de muchos permisos sin que falle. 
La categoria de los permisos es: 
 Required, permiso que el código necesita para ejecutarse correctamente. 
 Optional, permisos que no son obligatorios para la ejecución correcta del código, pero 
que podría ser bueno tenerlos. 
 Refused, permiso que se necesita para que el código nunca se otorgue, aunque la 
política de seguridad lo permita, se utiliza para restringir vulnerabilidades potenciales. 
Permisos Estándar 
Los Permisos Estándar son:
 EnvironmentPermission, clase que define permisos de acceso a variables de 
ambiente, donde son posibles dos tipos de acceso, de sólo lectura y escritura al valor 
de una variable de ambiente. El tipo de acceso escritura incluye permisos para crear y 
eliminar variables de ambiente. 
 FileDialogPermission, controla el acceso a archivos basado en el sistema de 
archivos de diálogo. El usuario debe autorizar el acceso al archivo via el cuadro de 
diálogo. 
 FileIOPermission, es posible especificar tres tipos de acceso a archivos de entrada y 
salida: lectura, escrituray adición, el acceso lectura incluye acceder a la información 
del archivo, el tipo escritura incluye eliminar y sobreescribir, el acceso adición no 
permite leer otros bits. 
 IsolatedStoragePermission, controla el acceso a almacenamientos aislados, este 
acceso permite utilización, tamaño de almacenamiento, tiempo de expiración y 
almacenamiento de datos. 
 ReflectionPermission, controla la capacidad de leer el tipo de información de tipos 
miembro no públicos y controla el uso de Reflection.Emit 
 RegistryPermission, Control de lectura, creación y escritura en el registry 
 SecurityPermission, colección de permisos simples que son utilizados por el sistema 
de seguridad, es posible controlar la ejecución de código, sobreescritura de chequeos 
de seguridad, invocación de código no administrado, serialización, etc. 
 UIPermission, define el acceso a varios aspectos de la interfaz de usuario, incluyendo 
el uso de windows, acceso a eventos y uso del portapapeles. 
Permisos Identidad 
Los Permisos Identidad son: 
 PubilsherIdentityPermission, la firma de componentes .NET provee resistencia de 
software publisher 
 StrongNameIdentityPermission, define el nombre del componente 
criptograficamente, ya que el nombre compromete la identidad. 
 ZoneIdentityPermission, define la zona de donde el código tiene origen, un URL 
puede pertenecer a sólo una zona 
 SiteIdentityPermission, permisos derivados basados en el sitio web de donde el 
código tiene origen 
 URLIdentityPermission, permisos derivados basados en URL de donde el código tiene 
origen 
Seguridad basada en roles 
La Seguridad basada en roles representa a un usuario o agente que actua en representación 
de un usuario dado. Las aplicaciones .NET hacen decisiones basadas en la identidad principal o 
su role como miembro. 
Un role es un nombre para un conjunto de usuarios quienes comparten los mismos privilegios. 
Un principal puede ser un miembro de múltiples roles y de esta manera se puede utilizar 
un role para determinar si ciertas acciones requeridas quizá sean realizadas por un principal. 
Un principal no necesariamente es un usuario, también puede ser un agente. 
Existen tres tipos de principal: 
 Generic principals, representa usuarios no autentificados. 
 Windows principals, relación de usuarios windows y sus roles, el acceso a recursos de 
otro usuario es permitido.
 Custom principals, definido por una aplicación. Pueden extender la noción básica de la 
identidad y los roles del principal. La restricción es que la aplicación debe proveer un 
módulo de autentificación de los tipos que elprincipal puede implementar. 
La clase Principalpermission provee consistencia con la seguridad de acceso, permitiendo 
al runtime realizar la autorización en un sentido similar al chequeo de la seguridad de acceso al 
código, pero es posible acceder directamente a la información de identidad principal y realizar 
chequeos de role e identidad en el código cuando sea necesario. 
Función ToString() 
Analice el siguiente ejemplo: 
using System; 
class Empleado{ 
string usr; 
string pwd; 
public Empleado(string login, string pwd){ 
this.usr = login; 
this.pwd = pwd; 
} 
} 
class App{ 
public static void Main(){ 
Empleado empleado = new 
Empleado("gangeles","123"); 
Console.WriteLine("Empleado : " + empleado); 
} 
} 
Salida: Empleado : Empleado 
La salida fue el nombre de la clase Empleado ya que es la representación más cercana que 
encontro. 
Es posible especificar algo con mayor sentido para ello se necesita redefinir la 
función ToString(): 
using System; 
class Empleado{ 
string usr; 
string pwd; 
public Empleado(string login, string pwd){ 
this.usr = login; 
this.pwd = pwd; 
} 
public override string ToString(){ 
return("Usuario : " + usr + ", Password : " + 
pwd); 
} 
} 
class App{ 
public static void Main(){ 
Empleado empleado = new 
Empleado("gangeles","123"); 
Console.WriteLine(empleado); 
} 
} 
Salida: Usuario : gangeles, Password : 123 
Función Equals() 
La función Equals() es utilizada para determinar cuando dos objetos tienen el mismo 
contenido.
En el siguiente ejemplo se redefinen las funciones operator==() y operator!=(), para 
permitir la sintaxis del operador, estos operadores deben ser redefinidos en pares, no pueden 
ser redefinidos separadamente. 
Ejemplo: 
using System; 
class Empleado{ 
string usr; 
string pwd; 
public Empleado(string login, string pwd){ 
this.usr = login; 
this.pwd = pwd; 
} 
public override string ToString(){ 
return("Usuario : " + usr + ", Password : " + 
pwd); 
} 
public override bool Equals(object o){ 
Empleado empB = (Empleado)o; 
if(usr != empB.usr){return false;} 
if(pwd != empB.pwd){return false;} 
return true; 
} 
public static bool operator==(Empleado empA, Empleado 
empB){ 
return empA.Equals(empB); 
} 
public static bool operator!=(Empleado empA, Empleado 
empB){ 
return !empA.Equals(empB); 
} 
} 
class App{ 
public static void Main(){ 
Empleado empleado1 = new Empleado("gangeles","123"); 
Empleado empleado2 = new Empleado("gangeles","123"); 
Console.WriteLine("El empleado1 es igual al empleado2: 
"+empleado1.Equals(empleado2)); 
Console.Write("empleado1 == empleado2 : "); 
Console.Write(empleado1 == empleado2); 
} 
} 
Salida: 
El empleado1 es igual al empleado2 : True 
empleado1 == empleado2 : True 
Al compilar el ejemplo anterior se presentara un warning indicando que no fue redefinada la 
función GetHashCode() ya que los valores que regresa son requeridos para ser relacionados al 
valor de regreso de Equals(). Cuando se invoca la funciónEquals() y dos objetos son iguales 
siempre se debe regresar el mismo código hash. 
Si no es redefinido el código hash podría ser sólo identico para la misma instancia de un objeto 
y una búsqueda para un objeto que es igual pero no la misma instancia podría fallar. 
Es posible utilizar un miembro que es único para el código hash, pero si no existe un valor único 
el código hash debería ser creado fuera de los valores contenidos en la función. 
Si la clase no tiene un identificador único pero tiene tiene otros campos, podrías ser utilizados 
por la función hash: 
using System; 
class Empleado{
string usr; 
string pwd; 
public Empleado(string login, string pwd){ 
this.usr = login; 
this.pwd = pwd; 
} 
public override string ToString(){ 
return("Usuario : " + usr + ", Password : " + 
pwd); 
} 
public override bool Equals(object o){ 
Empleado empB = (Empleado)o; 
if(usr != empB.usr){return false;} 
if(pwd != empB.pwd){return false;} 
return true; 
} 
public static bool operator==(Empleado empA, Empleado 
empB){ 
return empA.Equals(empB); 
} 
public static bool operator!=(Empleado empA, Empleado 
empB){ 
return !empA.Equals(empB); 
} 
public override int GetHashCode(){ 
return usr.GetHashCode() + pwd.GetHashCode(); 
} 
} 
class App{ 
public static void Main(){ 
Empleado empleado1 = new Empleado("gangeles","123"); 
Empleado empleado2 = new Empleado("gangeles","123"); 
Console.WriteLine("El empleado1 es igual al empleado2: 
"+empleado1.Equals(empleado2)); 
Console.Write("empleado1 == empleado2 : "); 
Console.Write(empleado1 == empleado2); 
} 
} 
La implementación del código GetHashCode anterior agrega los elementos y los regresa. 
Clase Hashtable 
La clase Hashtable es muy utilizada para realizar una búsqueda de objetos por una llave. Una 
tabla hash trabaja utilizando una función hash, la cual produce un entero llave para una 
instancia específica de una clase, donde esta llave es una versión condensada de la instancia. 
Una tabla hash utiliza esta llave para limitar drasticamente el número de objetos que deben ser 
buscados para encontrar un objeto específico en una colección de objetos. 
Interface IHashCodeProvider 
Si requiere definir diferentes códigos hash para un objeto específico, podría hacer esto 
implementado la InterfaceIHashCodeProvider para proveer una función alterna hash y 
además de que se requiere una coincidencia de la implementación de IComparer, estas nuevas 
implementaciones son pasadas al contructor de la Hashtable: 
using System; 
using System.Collections; 
public class Lenguaje : IComparable{ 
public 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 override int GetHashCode(){ 
return id; 
} 
public static IComparer OrdenaPorNombre{ 
get{ 
return (IComparer) new OrdenaNombres(); 
} 
} 
public static IHashCodeProvider HashNombre{ 
get{ 
return (IHashCodeProvider) new 
HashNombreCls(); 
} 
} 
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 HashNombreCls : IHashCodeProvider{ 
public int GetHashCode(object o){ 
Lenguaje len = (Lenguaje)o; 
return len.nombre.GetHashCode(); 
} 
} 
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); 
Hashtable lenguajes = new Hashtable(Lenguaje.HashNombre, 
Lenguaje.OrdenaPorNombre); 
lenguajes.Add(aLenguaje[0], "zxc"); 
lenguajes.Add(aLenguaje[1], "bnm"); 
lenguajes.Add(aLenguaje[2], "sdf"); 
lenguajes.Add(aLenguaje[3], "wer"); 
lenguajes.Add(aLenguaje[4], "tgh"); 
Lenguaje clone = new Lenguaje("MiLenguaje", 12345); 
string s = (string) lenguajes[clone]; 
Console.WriteLine(clone.ToString(), s); 
} 
} 
Función IClonable 
La función object.MemberWiseClone() puede ser utilizada para crear un clon de un objeto. 
La implementación por default de esta función produce una copia de un objeto, los campos de 
un objeto son copiados exactamente en lugar de ser duplicados: 
using System; 
class Saludo{ 
public string s; 
public Saludo(string s){ 
this.s = s; 
} 
} 
class MiClase{ 
public Saludo saludo; 
public MiClase(string s){this.saludo = new Saludo(s);} 
public MiClase Clon(){return (MiClase)MemberwiseClone();} 
} 
class App{ 
public static void Main(){ 
MiClase miClase = new MiClase("Hello World!"); 
MiClase miClon = miClase.Clon(); 
Console.WriteLine("miClase : " + miClase.saludo.s); 
Console.WriteLine("miClon : " + miClon.saludo.s); 
miClon.saludo.s = "Hola Mundo"; 
Console.WriteLine("miClase : " + miClase.saludo.s); 
Console.WriteLine("miClon : " + miClon.saludo.s); 
} 
} 
Salida: 
miClase : Hello World! 
miClon : Hello World! 
miClase : Hola Mundo 
miClon : Hola Mundo 
El resultado anterior es porque la copia hecha por la función MemberWiseClonre() es una 
copia, el valor de saludo es el mismo en ambos objetos por lo que se se cambia un valor 
dentro del objeto Saludo afecta ambas instancias de MiClase.
Interface ICloneable 
Para crear una copia deep, donde una nueva instancia de Saludo es creada para para la nueva 
instancia de MiClase, para ello se hace una implementación de la interface ICloneable: 
using System; 
class Saludo{ 
public string s; 
public Saludo(string s){ 
this.s = s; 
} 
} 
class MiClase : ICloneable{ 
public Saludo saludo; 
public MiClase(string s){this.saludo = new Saludo(s);} 
public object Clone(){return (new 
MiClase(this.saludo.s));} 
} 
class App{ 
public static void Main(){ 
MiClase miClase = new MiClase("Hello World!"); 
MiClase miClon = (MiClase) miClase.Clone(); 
Console.WriteLine("miClase : " + 
miClase.saludo.s); 
Console.WriteLine("miClon : " + miClon.saludo.s); 
miClon.saludo.s = "Hola Mundo!"; 
Console.WriteLine("miClase : " + 
miClase.saludo.s); 
Console.WriteLine("miClon : " + miClon.saludo.s); 
} 
} 
Salida: 
miClase : Hello World! 
miClon : Hello World! 
miClase : Hello World! 
miClon : Hola Mundo 
La invocación a Memberwiseclone() regresa una nueva instancia de Saludo y su contenido 
puede ser modificado sin afectar el contenido de miClase. 
Note que en este caso ICloneable requiere implementar la función Clone(). 
Formato Numérico 
Los tipos numéricos son formateados a través de la función miembro Format() del tipo de 
dato, la cual puede ser invocada directamente a través de String.Format() la cual invoca a 
la función Format() de cada tipo de dato o Console.WriteLine()la cual invoca 
a String.Format(). 
Existen dos tipos de métodos para el formateo específico numérico: 
Formato Estándar String 
Formato Estándar String, el cual puede ser utilizado para convertir un tipo numérico a una 
representación específica string. 
Este formato consiste del formato específico del caracter seguido de la secuencia de precisión 
específica de digitos, los formatos soportados son: 
Formato Descripción Ejemplo Salida
C, c Currency 
Console.WriteLine("0:C", 
123.8977); 
$123,345.90 
D, d Decimal 
Console.WriteLine("0:D7", 
12345); 
0012345 
E, e 
Scientific 
(exponential) 
Console.WriteLine("0:E", 
33345.8977); 
3.334590E+004 
E, f Fixed-point 
Console.WriteLine("0:F", 
33345.8977); 
3.334590E+004 
G, g General 
Console.WriteLine("0:G", 
33345.8977); 
33345.8977 
N, n Number 
Console.WriteLine("0:N", 
33345.8977); 
33,345.90 
X, x Hexadecimal 
Console.WriteLine("0:X", 
255); 
FF 
Formato Personalizado String 
El Formato Personalizado String, son utilizados para obtener más control, sobre la 
conversación que está disponible a través del formato estándar de strings. 
Reemplazo de Cero o Digito 
Console.WriteLine("{0:000}",12); Salida: 012 
Reemplazo de Espacio o Digito 
Console.WriteLine("{0:#####}",123); Salida: 123 
Punto Decimal 
Console.WriteLine("{0:#####.000}",12345.2); Salida: 12345.200 
Separador de Grupo 
Console.WriteLine("{0:##,###}",1123456789); Salida: 1,123,456,789 
Separador de Grupo 
Console.WriteLine("{0:000,.##}",12394); Salida: 123.95 
Porcentaje 
Console.WriteLine("{0:##.000%}",98144); Salida: 98.144% 
Notación Exponencial 
Console.WriteLine("{0:###.000E-00}",3.1415533E+04); Salida: 314.155E-02 
Separador de Sección 
Console.WriteLine("{0:###.00;0;(###.00)}",-456.55); Salida: 457 
Escape y Literales 
Console.WriteLine("{0:####}",255); Salida: 255# 
Parseo Numérico 
Los números pueden ser parseados utilizando el método Parse(), esto es posible ya que lo 
provee el tipo de dato. 
int iValue = Int32.Parse("123"); 
double dValue = Double.Parse("1.23"); 
Input/Output 
El lenguaje Común en Tiempo de Ejecución .NET provee funciones de entrada/salida en 
el namespace System.IO. 
La lectura y escritura la realiza la clase Stream, la cual describe como los bytes pueden ser 
escritos o leidos. Stream es una clase abstracta que en la práctica las clases derivan 
de Stream para ser utilizadas. 
Clases disponibles:
 FileStream, flujo en un archivo de disco 
 MemoryStream, flujo que es almacenado en memoria 
 NetworkStream, flujo en una conexión de red 
 BufferedStream, implementa un buffer en la cima de otro stream. 
Lectura y Escritura de Archivos 
Existen dos formas de obtener flujos (streams) que conectan a un archivo, uno utilizando la 
clase FileStream, la cual provee un control total sobre el acceso de archivos, incluyendo modo 
de acceso, compartido y buffering. 
A continuación se muestra un ejemplo donde, cada vez que el programa es ejecutado se 
sobreescribe el contenido del archivo en caso de que este exista, si no existe se crea: 
using System; 
using System.IO; 
class App{ 
public static void Main(){ 
FileStream f = new FileStream("nuevo.txt", 
FileMode.Create); 
StreamWriter s = new StreamWriter(f); 
for(int iNumberLine = 1; iNumberLine <= 10; 
iNumberLine++){ 
s.WriteLine("Linea " + iNumberLine); 
} 
s.Close(); 
f.Close(); 
} 
} 
Salida, Archivo Nuevo.txt cuyo contenido es: 
Linea 1 
Linea 2 
Linea 3 
Linea 4 
Linea 5 
Linea 6 
Linea 7 
Linea 8 
Linea 9 
Linea 10 
Serialización 
La Serialización es el proceso utilizado por el runtime para objetos persistentes en algún orden 
de almacenamiento o para transferirlos de un lugar a otro. 
La información de los metadatos en un objeto contiene información suficiente para que 
el runtime serialice los campos, pero es necesario indicar al runtime hacerlo correctamente a 
través de dos atributos [Serializable] el cual es utilizado para marcar un objeto que es 
posible serializar y [NonSerialized] que es aplicado para un campo o propiedad para indicar 
que no debería ser serializado. 
Threading (Hilos) 
El namespace System.Threading contiene clases utilizadas para threading y sincronización. El 
tipo apropiado de sincronización y/o exclusión depende del diseño del programa, pero C# 
soporta exclusión simple utilizando la sentencialock. 
lock utiliza la clase System.Threading.Monitor y provee funcionalidad similar a la 
invocación de CriticalSection en Win32

Manual c# 2

  • 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 defaultel 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.
  • 3.
    Una instancia deuna 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 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;
  • 4.
    //Constructor public Persona(stringsNombre, int iEdad){ this.sNombre = sNombre; this.iEdad = iEdad; } //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)
  • 5.
    El error esporque ClaseDerivada no puede utilizar ClaseBase como una clase base porque ClaseBase es sealed, es decir, no permite derivaciones. Overloading/Sobrecarga 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
  • 6.
    public static voidMain(){ Persona Mexicano = new Persona("Gerado Ángeles Nava", 33); 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{
  • 7.
    public Empleado(string sNombre,int iEdad):base(sNombre, iEdad){} override public 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()); } } } 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
  • 8.
    usuario hace lodedetalles 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.  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.
  • 9.
     Proveer unambiente 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.  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.
  • 10.
    El CLR administrala 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. 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.
  • 11.
    El entorno contienelas 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  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 e sa clase podría ser utilizada desde una clase VC++ o JScript, es decir, no importaria en que clase sea escrita una clase.
  • 12.
    Para hacer loanterior 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 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 acces o a archivos. Es posible utilizar la Plataforma .NET para desarrollar los siguientes tipos de aplicaciones y servicios:  Aplicaciones de consola  Windows Forms
  • 13.
     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. 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
  • 14.
    El managed codegenerado 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 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.
  • 15.
     Virtual ExecutionSystem - 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. 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.
  • 16.
    Seguridad La facetamá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. 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.
  • 17.
     Conversión deIL 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. 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í:
  • 18.
    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. 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.
  • 19.
    Es posible importarel 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++) 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.
  • 20.
    La primer partede 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: 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(){
  • 21.
    Console.Write("Nombre: "); StringstrNombre = 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 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
  • 22.
    using System; classClaseA{ 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 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
  • 23.
    Una variable contieneun 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#: 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):
  • 24.
    Tipo Valor float1.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. 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
  • 25.
    simplemente asignando aeste 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 { 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.
  • 26.
    Si el tipobase 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 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{}
  • 27.
    Existe una técnicallamada 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(){ 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
  • 28.
    El Tipo stringse 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"; 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
  • 29.
    PadRight() Alinea ala 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 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.
  • 30.
    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]; 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(){
  • 31.
    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 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;
  • 32.
    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{ 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.
  • 33.
    En el siguienteejemplo 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: 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:
  • 34.
    using System; publicclass 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; } } 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){
  • 35.
    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; 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:
  • 36.
    using System; usingSystem.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 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
  • 37.
    PHP 1 ExpresionesRegulares 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 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
  • 38.
    Al asignar elvalor 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); } } 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.
  • 39.
     Si laclase 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; 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;}
  • 40.
    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(){ 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.
  • 41.
    Constructor Estático UnConstructor 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). 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; } } }
  • 42.
    class AplicFecha{ publicstatic 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)); } } Al pasar un valor y no una referencia a la variable, es posible utilizar expresiones constantes, el resultado de los métodosMayor y Menor es pasado a el caller como un valor de regreso y es manipulado sin ser almacenarlo en una variable intermedia. Si no hay modificadores los parámetros son siempre pasados por valor. Valores en Parámetros ref Es posible pasar un valor como parámetro a un método, modificar el valor y regresarlo como resultado del método, para ello se utiliza el modificador ref seguido del tipo y del nombre del parámetro. Al contrario de los valores en parámetros no se pasa una copia del valor, sino la referencia del valor y por ello al modificar el valor se hace la modificación directa, también es necesario inicializar el valor que se pasa como paramétro por medio de una variable intermedia y no directamente a través de una expresión constante: using System; public class Param{ public void ParametroRef(ref int RefParametro){//No regresa un valor explícito RefParametro *= RefParametro; //Se modifica el valor directamente //No se regresa un valor, porque se modifico de manera directa } } class Parametros{ public static void Main(){ Param MiParam = new Param(); int iValorRef = 5; //Se requiere inicializar el valor MiParam.ParametroRef(ref iValorRef);//Se invoca el método pasando la referencia del valor Console.WriteLine("ref : " + iValorRef); } }
  • 43.
    Good Practice, serecomienda tener dos variables, una en el parámetro y otra en el parámetro ref. Recuerde, el compilador de C# no permite utilizar variables que no han sido inicializadas, por lo que antes de utilizar o establecer los valores de una variable debe ser inicializada, para ello existen dos formas de hacerlo:  Inicializar la variable al declararla.   using System;   class MiClase{  private int MiPropiedad;  public void AsignarValor(ref int MiRefParam){  this.MiPropiedad = MiRefParam;  }  public static void Main(){  MiClase MiObjeto = new MiClase();  int MiEdad = 33;//Se inicializa la variable al declararla  MiObjeto.AsignarValor(ref MiEdad);  Console.WriteLine("MiEdad : " + MiEdad);  Console.WriteLine("MiObjeto.MiPropiedad : " + MiObjeto.MiPropiedad);  }  }  Por ejemplo cambiar la definición de la función y utilizar un parámetro out en vez de un parámetro ref.  using System;   class MiClase{  private int MiPropiedad;  public void AsignarValor(out int MiOutParam){//Se cambia la definición de la función  MiOutParam = 33;  this.MiPropiedad = MiOutParam;  }  public static void Main(){  MiClase MiObjeto = new MiClase();  int MiEdad;//No se inicializa la variable  MiObjeto.AsignarValor(out MiEdad);  Console.WriteLine("MiEdad : " + MiEdad);  Console.WriteLine("MiObjeto.MiPropiedad : " + MiObjeto.MiPropiedad);  }  } Los parámetros out son exactamente como los parámetros ref excepto que una variable sin inicializar puede ser pasada como parámetro y el caller define un parámetro out en vez de ref. Valores en Parámetros out Un parámetro out puede ser utilizado sólo para contener el resultado de un método, es necesario especificar el modificador out para indicar el tipo de parámetro, a diferencia de los parámetros ref el caller no necesita inicializar la variable antes de invocar el método: using System;
  • 44.
    public class Param{ public void ParametroOut(out int OutParametro){ OutParametro = 4 * 4; //No se gregresa un valor, porque es regresado en el parámetro out } } class Parametros{ public static void Main(){ Param MiParam = new Param(); int iValorOut; //No se requiere inicilizar el valor MiParam.ParametroOut(out iValorOut); //Se invoca el método con un parámetro out Console.WriteLine("out : " + iValorOut);//Resultado de la invocación del método } } Ejemplo de Parámetros In, Ref y Out using System; public class Param{ public int ParametroIn(int InParametro){ return InParametro * InParametro; } public void ParametroRef(ref int RefParametro){//No regresa un valor explícito RefParametro *= RefParametro; //Se modifica el valor directamente //No se regresa un valor, porque se modifico de manera directa } public void ParametroOut(out int OutParametro){ OutParametro = 4 * 4; //No se gregresa un valor, porque es regresado en el parámetro out } } class Parametros{ public static void Main(){ Param MiParam = new Param(); Console.WriteLine(" in : " + MiParam.ParametroIn(3)); int iValorRef = 5; //Se requiere inicializar el valor MiParam.ParametroRef(ref iValorRef);//Se invoca el método pasando la referencia del valor Console.WriteLine("ref : " + iValorRef); int iValorOut; //No se requiere inicilizar el valor MiParam.ParametroOut(out iValorOut); //Se invoca el método con un parámetro out
  • 45.
    Console.WriteLine("out : "+ iValorOut);//Resultado de la invocación del método } } Redefinición de Métodos (Overriding) Uno de los principios básicos de la programación orientada a objetos es el polimorfismo, el cual hace posible que una clase derivada pueda redefinir (override) métodos de la clase base. Para indicar que se puede redefinir el método se emplea la palabra reservada virtual: virtual void NombreMetodo_PuedeSerRedefinido Después, al derivar de la clase base se agrega la palabra reservada override en el nuevo método: override void NombreMetodo_PuedeSerRedefinido() No es posible cambiar la accesibilidad de un método que es redefinido, es decir, no es posible cambiar los modificadores que definen al método. Cuando se invoca un método virtual se está derivando el método de clase que es invocado y no el método de la clase base: ((ClaseBase)InstanciaClaseDerivada).NombreMetodo_PuedeSerRedefinido(); Para indicar que una clase deriva de otra se utiliza el operador : el cual denota esta acción: class ClaseDerivada : ClaseBase{} El siguiente ejemplo muestra como se redefine un método: using System; class ClaseBase{ public virtual int Calculo(int iA, int iB){ return iA + iB; } } class ClaseDerivada : ClaseBase{//Se deriva de la clase base public override int Calculo(int iA, int iB){//Se especifica que el método será redefinido return iA - iB;//Se redefine la funcionalidad del método } } class RedefinirMetodos{ public static void Main(){ ClaseBase ClsBase = new ClaseBase(); Console.WriteLine("Clase base : " + ClsBase.Calculo(5,3)); //Se crea una instancia de la clase derivada: ClaseDerivada ClsDer = new ClaseDerivada(); //Se invoca el método redefinido en la clase derivada: Console.WriteLine("Clase derivada : " + ClsDer.Calculo(5,3)); } } Ocultamiento de Métodos (Hiding) Es posible ocultar métodos de la clase base, esto se logra haciendo uso de una característica especial de la redefinición de métodos llamada ocultamiento de métodos y al derivar de la clase base: using System; class ClaseBase{
  • 46.
    //Sin código } class ClaseDerivada : ClaseBase{//Clase derivada de la clase base public void MetodoOculto(){//Método Oculto Console.WriteLine("Hiding Methods"); } } class Hiding{ public static void Main(){ ClaseDerivada MiClaseDerivada = new ClaseDerivada(); MiClaseDerivada.MetodoOculto(); } } El código anterior demuestra que es posible derivar una clase que implementa un método que la clase base no contiene. Por otro lado si la clase base contiene el método y se trata de derivar una clase que trata de implemetar un método que si contiene la clase, se produce un error: using System; class ClaseBase{ public void MetodoOculto(){ Console.WriteLine("Hiding Methods"); } } class ClaseDerivada : ClaseBase{//Clase derivada de la clase base public void MetodoOculto(){//Se implementa un método que si existe Console.WriteLine("Hiding Methods"); } } class Hiding{ public static void Main(){ ClaseDerivada MiClaseDerivada = new ClaseDerivada(); MiClaseDerivada.MetodoOculto(); } } El compilador indicará un mensaje similar al siguiente: Hiding.cs(10,14): warning CS0108: The keyword new is required on 'ClaseDerivada.MetodoOculto()' because it hides inherited member 'ClaseBase.MetodoOculto()' Hiding.cs(4,14): (Location of symbol related to previous warning) El error principal es que no se hace uso del modificador new, ya que si es posible ocultar un método contenido en la clase base: using System; class ClaseBase{ public void MetodoOculto(){//Método Oculto Console.WriteLine("Hiding Methods"); }
  • 47.
    } class ClaseDerivada: ClaseBase{//Clase derivada de la clase base new public void MetodoOculto(){//Método Oculto Console.WriteLine("Hiding Methods using new"); } } class HidingClassMet{ public static void Main(){ ClaseDerivada MiClaseDerivada = new ClaseDerivada(); MiClaseDerivada.MetodoOculto(); } } Al hacer uso del modificador new, se le indica al compilador que se está redefiniendo el método de la clase base y que debería ocultar este método. Se puede asegurar de invocar el método que redefine la clase derivada utilizando la siguiente sintaxis: ((ClaseBase)MiClaseDerivada).MetodoOculto(); Propiedades Las propiedades son convenientes para separar la interfaz de un objeto de su implementación, en vez de permitir a un usuario acceder directamente a un campo o arreglo, una propiedad permite especificar a un conjunto de sentencias realizar el acceso mientras se permita utilizar el campo o arreglo. class NombreClase{ int iNombrePropiedad; //declaración de la propiedad //Especificación del acceso a la propiedad public int NombrePropiedad{ get{return iNombrePropiedad;} set{iNombrePropiedad = value;} } } Ejemplo: using System; class Propiedades{ private int iEdad; public int Edad{ get{return iEdad;} set{iEdad = value;} } private string sNombre; public string Nombre{ get{return sNombre;} set{sNombre = value;} } private bool bMexicano; public bool Mexicano{ get{return bMexicano;} set{bMexicano = value;}
  • 48.
    } public staticvoid Main(){ Propiedades Ciudadano = new Propiedades(); Ciudadano.Edad = 33; Console.WriteLine("Edad Ciudadano : " + Ciudadano.Edad); Ciudadano.Nombre = "Gerardo Ángeles Nava"; Console.WriteLine("Nombre Ciudadano : " + Ciudadano.Nombre); Ciudadano.Mexicano = true; Console.WriteLine("Mexicano Ciudadano : " + Ciudadano.Mexicano); } } Existen dos maneras de exponer el nombre de los atributos:  Campos (fields)  Propiedades (properties) Los atributos son implementados como variables miembro con acceso público via accessors (get o set). Los accessors (get o set) especifican las sentencias que son ejecutadas cuando se requiere leer o escribir el valor de una propiedad. Los accessors para la lectura del valor de una propiedad son marcados con la palabra reservada get y los accessors para modificar el valor de una propiedad son marcados con la palabra reservada set. El siguiente ejemplo muestra como se implentan los accessors para las propiedades: using System; class Persona{ private int iSueldo; public int Sueldo{ get{return iSueldo;} set{iSueldo = value;} } } class AplicPersona{ public static void Main(){ Persona Empleado = new Persona(); Empleado.Sueldo = 33; Console.WriteLine("Edad : " + Empleado.Sueldo); } } Note, que se utiliza el parámetro value, ya que el valor actual es almacenado en este que es accesible dentro de la clase. Si en vez de utilizar propiedades desea utilizar campos deberá dejar fuera los accessors y redefinir la variable como: public int Sueldo; Accessors
  • 49.
    Es posible ocultarlos detalles de la estructura de almacenamiento de la clase reordenando los accessors, en este caso elaccessors set es pasado en el nuevo valor para la propiedad en el parámetro value. Las operaciones que pueden realizarse con los atributos son:  Implementar get y set, es posible tener acceso al valor de la propiedad para leerlo y escribirlo.  get only, sólo es posible leer el valor de la propiedad.  set only, sólo es posible establecer el valor de la propiedad. Propiedades Estáticas Propiedades estáticas no pueden ser declaradas con los modificadores virtual, abstract u override. Las propiedades estáticas pueden ser inicializadas hasta que sea necesario hacerlo, el valor puede ser fabricado cuando se necesite sin almacenarlo. using System; class Persona{ int iPiernas; int iBrazos; int iOjos; public Persona(int piernas, int brazos, int ojos){ this.iPiernas = piernas; this.iBrazos = brazos; this.iOjos = ojos; } public static Persona Piernas{ get{ return(new Persona(2,0,0)); } } public static Persona Brazos{ get{ return(new Persona(0,4,0)); } } public static Persona Ojos{ get{ return(new Persona(0,0,8)); } } } class App{ public static void Main(){ Persona ET = Persona.Piernas; Console.WriteLine(ET); } } Índices Es posible incluir una forma de acceso indexado a la clase tal como si la clase se tratará de un arreglo, para ello se utiliza la característica de C# indexer, sintaxis: atributos modificadores declarador{instrucciones} Los índices o indexers regresan o establecen un string en un índice dado, los indexers no tienen atributos por lo que utilizan el modificador public.
  • 50.
    La parte deldeclarador consiste del tipo string y la palabra reservada this para denotar el indexer de la clase: public string this[int iIndex]{ get{intrucciones} set{intrucciones} } Las reglas de implementación para get y set son las mismas reglas de las propiedades, la única diferencia es que la lista de parámetros se define libremente entre los corchetes, también existen restricciones como que es necesario especificar al menos un parámetro y los modificadores ref y out no están permitidos. Ejemplo: using System; using System.Net;//Directiva namespace para la clase DNS class ResolverDNS{ IPAddress[] aIPs; public void Resolver(string strHost){ IPHostEntry IPHE = Dns.GetHostByName(strHost); aIPs = IPHE.AddressList; } public IPAddress this[int iIndex]{ get{return aIPs[iIndex];} } public int Contador{ get{return aIPs.Length;} } } class AplicResolverDNS{ public static void Main(){ ResolverDNS MiDNS = new ResolverDNS(); MiDNS.Resolver("www.informatique.com.mx"); int iContador = MiDNS.Contador; Console.WriteLine("Se encontro {0} para el host ", iContador); for(int i = 0; i < iContador; i++){ Console.WriteLine(MiDNS[i]); } } } Si el namespace para la clase DNS no está contenida en la biblioteca central, al compilar incluya la referencia a la biblioteca que la contiene: csc /r:System.Net.dll /out:resolver.exe AplicResolverDNS.cs Los índices pueden tener más que un parámetro para simular un arreglo virtual multidimensional. Eventos Una clase puede usar un evento para notificar a otra clase o clases que algo ocurrio, los eventos usan el idioma "publish-subscribe", ya que una clase publica el evento que puede lanzar y las clases que están interesadas en un evento específico pueden subscribir al evento. La rutina o tarea que un evento podría invocar es definida por un delegado. Para tratar de manera fácil con eventos, la convención de diseño para eventos es emplear dos parámetros, donde el primer parámetro es el objeto que lanza el evento y el segundo parámetro es un objeto que contiene la información acerca del evento el cual siempre deriva de la clase EventArgs.
  • 51.
    Los eventos puedenser declarados como campos o propiedades de clase, ambos accesos comparten la comodidad de tipo que el evento debe tener delegate. Cada evento puede ser utilizado por cero o más clientes y un cliente puede utilizar un evento en cualquier momento. Los delegados pueden ser implementados como métodos o instancias estáticas. Modificadores Los modificadores pueden clasificarse en:  Modificadores de Clase  Modificadores Miembro  Modificadores de Acceso Modificadores de Clase Existen dos tipos de modificadores de clase:  abstract  Una clase abstracta no puede ser inicializada  Sólo clases derivadas que no son abstractas pueden ser inicializadas  Las clases derivadas deben implementar todos los miembros abstractos de la clase base abstracta  No puede aplicarse un modificador sealed a una clase abstracta  sealed  Clases sealed no pueden ser heredadas  Utilice este modificador para prevenir herencia accidental Ejemplo: using System; abstract class ClaseAbstracta{ abstract public void MiMetodo(); } sealed class ClaseDerivada:ClaseAbstracta{ public override void MiMetodo(){ Console.WriteLine("Clase sealed"); } } public class ModificadorClase{ public static void Main(){ ClaseDerivada CD = new ClaseDerivada(); CD.MiMetodo(); } } Modificadores Miembro Algunos modificadores miembro son:  abstract  Indica que un método o accessor no contiene una implementación, que son implicitamente virtual.  La clase que recibe la herencia debe proveer la palabra reservada override.  const  Este modificador aplica a campos y variables locales
  • 52.
     La expresiónconstante es evaluada en tiempo de compilación, por lo tanto no puede contener referencias de la clase.  event  Define un campo o propiedad como tipo event  Utilizado para ligar al código cliente a eventos de la clase  extern  Indica al compilador que el método es implemetado externamente  override  Utilizado para modificar un método o accessor que es definido virtual en cualquiera de las clases base  La firma de redefinición y método base debe ser el mismo  readonly  Un campo declarado con el modificador readonly puede ser cambiado sólo en su declaración o en el constructor de la clase contenedora  static  Miembros que son declarados static pertenecen a la clase y no a una instancia de la clase  Pueden utilizarse modificadores static con campos, métodos, propiedades, operadores y constructores.  virtual  Indican que un método o accessor pueden ser redefinidos por las clases que reciben la herencia. Modificadores de Acceso Los modificadores de acceso definen el nivel de acceso que cierto código tiene en los miembros de la clase como métodos y propiedades. Es necesario aplicar el modificador de acceso deseado a cada miembro, de otra forma el tipo de acceso por default es implícito. Los modificadores de acceso son:  public, el miembro es accesible desde cualquier parte, este modificador de acceso es el menos restrictivo.  protected, el miembro es accesible en la clase y todas las clases derivadas. No es permitido el acceso desde fuera. El acceso protected permite a otras clases depender de la implementación interna de la clase y por lo tanto deberían ser otorgados sólo cuando sea necesario.  private, sólo el código dentro de la misma clase puede acceder este miembro. Las clases derivadas no pueden acceder al código.  internal, este tipo de acceso es otorgado a todo el código que es parte del mismo componente (aplicación o biblioteca) .NET, es visto como público a nivel del componente .NET y privado fuera de este. Este modificador permite que un miembro sea accesible desde las clases en el mismo ensamblaje, pero no desde las clases fuera de este. El modificador internal protected provee mayor flexibilidad en como una clase es definida y se utiliza para indicar que un miembro pueder ser accedido desde una clase internal o protected, en otras palabras internal protected permite acceso internal o protected. Sentencias de Control Las Sentencias de Control se emplean para controlar la ejecución y flujo del código, las cuales se dividen en:
  • 53.
     Sentencias deSelección  Sentencias de Iteración Sentencias de Selección Las Sentencias de selección son aquellas que se utilizan para realizar operaciones basadas en el valor de una expresión. Las Sentencias de selección son aquellas que se utilizan para escribir diferentes flujos de acción en base a una condición dada, existen dos tipos de sentencias de selección:  Sentencia if  Sentencia switch Sentencia if Al escribir uno o varios flujos de acción el código contenido en estos se ejecutará siempre y cuando la evaluación de la expresión en la sentencia if se evalue como verdadera (tenga cuidado en C# if(0){} o if(1){} no es válido). if(expresión-booleana){la expresión se evaluo verdadera} Es posible indicar código alterno en caso de que la expresión booleana se evalue falsa: if(expresión-booleana){ la expresión se evaluo verdadera }else{ la expresión se evaluo falsa } Nota C# no puede convertir valores numéricos a booleanos, solo puede hacer comparaciones entre ellos para evaluar el resultado de la expresión el cual es un valor booleano. using System; class SeleccionIf{ public static void Main(){ if(1 == 1){ Console.WriteLine("se evaluo verdadero"); } /* No es soportado por C# if(0){ Console.WriteLine("?"); } */ } } Nota el operador de igualdad en C# es ==, si está habituado a otra forma, sera cosa tiempo acostumbrarse a escribirlo correctamente, en la siguiente tabla se muestran los operadores válidos en C#: Operador Evalua == Verdadero, si ambos valores son los mismos != Verdadero, si los valores son diferentes <, <=, >, >= Verdadero, si el valor cumple con la condición Los operadores de la tabla son implementados via la sobrecarga de operadores y la implementación es especifica para el tipo de dato, si se comparan dos variables de diferente tipo se realiza una conversión implícita que debe existir para que el compilador cree el código necesario automáticamente. Recuerde que siempre podrá realizar un cast explícito. Ejemplo using System; class Caracteres{ public static void Main(){
  • 54.
    string sNombre ="Gerardo Angeles Nava"; char chLetra = sNombre[0];//Extrae el primer caracter del string if(Char.IsDigit(chLetra)){ Console.WriteLine(chLetra + " es un dígito"); }else{ EsMayuscula(chLetra); EsMinuscula(chLetra); } chLetra = sNombre[1];//Extrae el segundo caracter del string if(Char.IsDigit(chLetra)){ Console.WriteLine(chLetra + " es un dígito"); }else{ EsMayuscula(chLetra); EsMinuscula(chLetra); } sNombre = "123"; chLetra = sNombre[2];//Extrae el tercer caracter del string if(Char.IsDigit(chLetra)){ Console.WriteLine(chLetra + " es un dígito"); }else{ EsMayuscula(chLetra); EsMinuscula(chLetra); } } public static void EsMayuscula(char chCaracter){ if(chCaracter >= 'A' && chCaracter <= 'Z'){ Console.WriteLine(chCaracter + " mayúscula"); } } public static void EsMinuscula(char chCaracter){ if(chCaracter >= 'a' && chCaracter <= 'z'){ Console.WriteLine(chCaracter + " minúscula"); } } } En el ejemplo anterior se muestra la aplicación de la sentencia de selección if y el uso del método IsDigit de la claseChar, también se muestra como determinar si un caracter correponde a las letras mayúsculas o minúsculas. Good Practice: nunca asigne valores a variables dentro de una condición que utiliza operadores lógicos (&&,||,!), porque puede que nunca se le asigne el valor correspondiente a la variable en caso de que una expresión anterior se evalue verdadera: if(a == b || (c == (iValor = d))){}
  • 55.
    En el ejemploanterior, si la expresión a == b se evalua verdadera entonces la variable iValor nunca contendrá el valor d. Sentencia switch La sentencia de selección switch tiene una expresión de control y los flujos de código alternos son ejecutados dependiendo del valor constante asociado con esta expresión. switch(expresion-de-control){ case expresion-contante: sentencias; break; case expresion-contante: goto case 2; case expresion-contante: goto default; default: sentencias; } Los tipos de datos permitidos para la expresión de control son sbyte, byte, short, ushort, uint, long, ulong, char, string o un tipo enumeración (enumeration). ¿Cómo funciona la sentencia de selección switch?  Se evalua la expresión de control  Si la expresión constante en las etiquetas case coincide con el valor evaluado en la expresión de control, entonces las sentencias contenidas para ese caso son ejecutadas  Si la expresión constante en las etiquetas case no coincide con el valor evaluado en la expresión de control, entonces el código contenido en el caso por default es ejecutado  Si la expresión constante en las etiquetas case no coincide con el valor evaluado en la expresión de control y no existe un caso por default, entonces el control es transferido al final del bloque switch Ejemplo: using System; class SentenciaSwitch{ public static void Main(){ for(int i = 0; i <= 12; i++){ Mes(i); } } public static void Mes(int iMes){ switch(iMes){ case 1: Console.WriteLine("Enero"); break; case 2: Console.WriteLine("Febrero"); break; case 3: Console.WriteLine("Marzo"); break; case 4: Console.WriteLine("Abril"); break; case 5: Console.WriteLine("Mayo");
  • 56.
    break; case 6: Console.WriteLine("Junio"); break; case 7: Console.WriteLine("Julio"); break; case 8: Console.WriteLine("Agosto"); break; case 9: Console.WriteLine("Septiembre"); break; case 10: Console.WriteLine("Octubre"); break; case 11: Console.WriteLine("Noviembre"); break; case 12: Console.WriteLine("Diciembre"); break; default: Console.WriteLine("Mes no válido"); break; } } } Es posible utilizar sentencias goto dentro del switch de la siguiente manera:  goto case expresion-contante  goto default Ejemplo: using System; class SentenciaSwitch{ public static void Main(){ int iOpcion = 4; Opcion(iOpcion); iOpcion = 2; Opcion(iOpcion); iOpcion = 8; Opcion(iOpcion); iOpcion = 10; Opcion(iOpcion); } public static void Opcion(int iValor){ switch(iValor){ case 2: goto case 6; case 4: Console.WriteLine(" cuatro"); break; case 6: Console.WriteLine(" seis"); break; case 8:
  • 57.
    goto default; case10: Console.WriteLine(" diez"); break; default: Console.WriteLine(" por defecto"); break; } } } Sentencias de Iteración (repetición) Las Sentencias de Iteración (también conocidas como looping statements) son aquellas que nos permiten ejecutar un bloque de código repetidamente mientras una condicíon específica sea verdadera:  for  foreach  while  do Sentencia for La Sentencia for se utiliza cuando se conoce previamente cuantas veces ha de repetirse un bloque de código. Este bloque se repetira mientras la condición evalue una expresión booleana verdadera, no será posible evaluar otro tipo de expresión. Sintaxis: for(inicializador; condición; iterador) Los componentes de la sentencia for: inicializador, condición, iterador, no son obligatorios. Es posible salir de un ciclo for a través de las instrucciones:  break  goto Ejemplo: using System; class Factorial{ public static void Main(string[] aArgs){ if(aArgs.Length == 0){ Console.WriteLine("Debe proporcionar un argumento, Ejemplo: Factorial 5"); return; } long lFactorial = 1; long lCalcular = Int64.Parse(aArgs[0]); long lAux = 1; for(lAux = 1; lAux <= lCalcular; lAux++){ lFactorial *= lAux; //Test Line Console.WriteLine("{0}! * {1}", lAux, lFactorial); } Console.WriteLine("{0}! es {1}", lCalcular, lFactorial); } } Sentencia foreach La Sentencia foreach es un comando para enumerar los elementos de una colección.
  • 58.
    foreach(Tipo indentificador inexpresión){} La variable de iteración es declarada por el Tipo, indentificador y expresión correspondiente a la colección. La variable de iteración representa el elemento de la colección para cada iteración. El siguiente ejemplo muestra el uso de for: using System; class App{ public static void Main(string[] aArgs){ for(int i = 0; i < aArgs.Length; i++){ Console.WriteLine("Elemento " + i + " = " + aArgs[i]); } } } El ejemplo anterior implementado con foreach: using System; class App{ public static void Main(string[] aArgs){ foreach(String s in aArgs){ Console.WriteLine(s); } } } No es posible asignar un nuevo valor a la variable de iteración. No se puede pasar la variable de iteración como un parámetro ref o out. Para que una clase soporte la sentencia foreach, la clase debe soportar un método con la firma GetEnumerator() y la estructura, clase o interface que regresa debe tener un método público MoveNext y una propiedad pública Current. En el siguiente ejemplo el método GetEnvironmentVariables() regresa una interfaz de tipo IDictionary. Es posible acceder a las colecciones Keys y Values de la interfaz IDictionary: using System; using System.Collections; class SentenciaForEach{ public static void Main(){ IDictionary VarsAmb = Environment.GetEnvironmentVariables(); Console.WriteLine("Existen {0} variables de ambiente declaradas", VarsAmb.Keys.Count); foreach(String strIterador in VarsAmb.Keys){ Console.WriteLine("{0} = {1}", strIterador, VarsAmb[strIterador].ToString()); } } } Nota, es necesario tener una precaución extra al decidir el tipo de variable de iteración, porque un tipo equivocado no puede ser detectado por el compilador, pero si detectado en tiempo de ejecución y causar una excepción. Sentencia while La Sentencia while se utiliza cuando no se conoce previamente cuantas veces ha de repetirse un bloque de código, por lo que puede ejecutarse 0 o más veces. Este bloque se repetira mientras la condición evalue una expresión booleana verdadera, no será posible evaluar otro tipo de expresión.
  • 59.
    while(condicional){} Ejemplo: usingSystem; using System.IO; class SentenciaWhile{ public static void Main(){ if(!File.Exists("test.html")){ Console.WriteLine("El archivo test.html no existe"); return; } StreamReader SR = File.OpenText("test.html"); String strLinea = null; while(null != (strLinea = SR.ReadLine())){ Console.WriteLine(strLinea); } SR.Close(); } } Es posible utilizar la sentencia break para salir del ciclo o continue para saltar una iteración. Sentencia do La diferencia entre la sentencia while y do es que do se evalua después de su primer iteración, por lo que al menos siempre se ejecuta una vez: do{ sentencias; }while(condición); Es posible salir de un ciclo do a través de la sentencia break y es posible saltar una iteración utilizando la sentenciacontinue El siguiente ejemplo le la entrada de la consola toma el primer caracter leido, lo convierte en un Tipo double y suma su valor mientras la entrada sea 's' o hasta que la entrada sea 'n'. using System; class Consola{ public static void Main(){ Consola LeerDatos = new Consola(); LeerDatos.Run(); } public void Run(){ char chContinuar = 's'; string strDatos; double dSuma = 0; do{ Console.Write("Proporcione un número: "); strDatos = Console.ReadLine(); dSuma += Double.Parse(strDatos); Console.Write("¿Continuar s/n?"); strDatos = Console.ReadLine(); chContinuar = strDatos[0]; if(chContinuar == 'n') break; }while(chContinuar == 's'); Console.WriteLine("La suma de los números es: " + dSuma);
  • 60.
    } } Sentenciasde Salto Las Sentencias de Salto como break, continue, goto y return sirven para ir de una sentencia a otra break La Sentencia break es utilizada para salir de la iteración en curso o sentencia switch y continuar con la ejecución después de esa sentencia. continue La Sentencia continue salta todas las sentencias siguientes en la iteración en curso y entonces continua la ejecución en la sentencia de iteración (siguiente iteración). goto La Sentencia goto puede ser utilizada para saltar directamente a una etiqueta. Una sentencia goto no puede ser utilizada para saltar adentro de un bloque de sentencias. Su uso podría ser empleado en sentencias switch o para transferir el control fuera de un loop anidado. Nota, como buena práctica no se recomienda el uso de goto. return La Sentencia return regresa a la función invocadora y opcionalmente retorna un valor. Asignación Definitiva Las reglas de Asignación definitiva previenen la observación del valor de una variable no asignada, ya que C# no permite utilizar variables que no han sido inicializadas, así como también no pueden realizarse operaciones con variables de clase que no han sido inicializadas. Puede accederse al elemento de un arreglo aún si no ha sido inicializado, ya que el compilador no puede rastrear la asignación definitiva en todas las situcaciones. Precedencia de Operadores Cuando una expresión contiene múltiples operadores, la precedencia de operadores controla el orden en el cual los elementos de la expresión son evaluados. Categoría Operador Primary (x), x.y, f(x), a[x], x++, x--, new, typeof, sizeof, checked, unchecked Unary +, -, !, ~, ++x, --x, (T)x Multiplicative *, /, % Additive +, - Shift <<, >> Relational <, >, <=, >=, is Equality ==, != Logical AND & Logical XOR ^ Logical OR | Conditional AND && Conditional OR || Conditional ?: Assignment =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= typeof El operador typeof regresa el tipo del objeto, el cual es una instancia de la clase System.Type
  • 61.
    Una instancia yaexistente puede obtener el tipo de objeto con el método de la instancia GetType(). is El operador is es utilizado para determinar cuando una referencia a un objeto puede ser converitda a un tipo específico ointerface. El operador as es muy similar al operador is, pero en vez de determinar cuando un objeto es un tipo específico ointerface, as también realiza la conversión explicita a ese tipo o interface y si no lo puede convertir el operador regresanull. Utilizar as es más eficiente que utilizar is, porque as sólo necesita checar el tipo del objeto una vez e is checa el tipo cuando el operador es utilizado y lo checa otra vez cuando la conversión se realiza. Utilizando is if(UnObjeto is UnTipo){ UnTipo ut = (UnTipo) UnObjeto; } Utilizando as UnTipo ut = UnObjeto as UnTipo; if(ut != null){ sentencias; } Conversiones En C# las conversiones se dividen en conversiones explícitas y conversiones implícitas que son aquellas que podrían siempre ocurrir: //conversiones implícitas sbyte a = 55; short b = a; int c = b; long d = c; //conversiones explícitas c = (int) d; b = (short) c; a = (sbyte) b; A continuación se presenta la jerarquía de conversión en C## Manejo de Excepciones Las excepciones son el método fundamental de manejo de condiciones de error. Ejemplo: using System; class DivisionCero{ public static void Main(){ int iA = 33; int iB = 0; try{ //Sentencia que puede lanzar una excepción Console.WriteLine("{0}/{1} = {2}", iA ,iB, iA/iB); }catch(Exception e){ //Manejo de la excepción Console.WriteLine("La operación {0}/{1} genero la excepcion : {2}", iA, iB, e); } Console.WriteLine("Continua la ejecución del código..."); }
  • 62.
    } El ejemploencierra el bloque de código que podría lanzar una excepción con try. En caso de generarse una excepción elruntime .NET detiene la ejecución del código y busca el bloque try en el cual la excepción tuvo lugar, entonces busca si este bloque tiene relacionado un bloque catch, puede ser que encuentre más de un bloque catch relacionado al bloque tryque genero la excepción, por lo que se determina que bloque catch es el que mejor y ejecuta el código que contiene. El compilador de C# puede manejar silenciosamente situaciones que podrían producir un error sin notificarnos explicitamente de ello, por ejemplo una situación como un overflow que es cuando el cálculo de una operación excede el rango válido de resultados posibles para el tipo de dato. El caso del código para calcular un factorial, el compilador no prodruce una advertencia, pero si trata de obtener elfactorial de 2000 dara por resultado 0, el compilador actuo en modo silencioso porque por default el compilador tiene deshabilitada la opción de chequeo de overflow. Es posible cambiar el comportamiento de chequeo de overflow utilizando un switch al compilar. Jerarquía de Excepciones Todas las excepciones derivan de la clase Exception, la cual es parte del lenguaje común en tiempo de ejecución (CLR), donde la propiedad catch determina por coincidencia el tipo de excepción a el nombre de la excepción generada. Un bloque catch con una coincidencia especifica hacen más general la excepción: using System; class ExceptionDivision0{ public static void Main(){ int iA = 33; int iB = 0; try{ Console.WriteLine("{0}/{1} = {2}", iA ,iB, iA/iB); }catch(DivideByZeroException){ Console.WriteLine("Se genero la excepcion : DivideByZeroException"); } Console.WriteLine("Continua la ejecución del código..."); } } En este ejemplo el bloque catch que atrapa la excepción DivideByZeroException es una coincidencia más específica, por lo que es la única que será ejecutada, pero si además de escribir el catch para DivideByZeroException escribe el catch paraException, el compilador le notificara que existe una excepción que atrapa todas las excepciones y esto es porqueException ocupa la cima de la jerarquía de todas las excepciones. Trabajar con Excepciones Existen tres formas básicas de trabajar con excepciones: Caller Beware La primer forma es no atrapar la excepción, lo cual provocará dejar al objeto en un estado incorrecto, y causará daños cuando el caller trate de utilizarla de nuevo. Caller Confuse La segunda forma es atrapar la excepción y tratar de hacer acciones que dejen la operación como estaba hasta antes de generarse la excepción y entonces relanzar la excepción, esto usualmente es lo menos que se esperaria del manejo de excepciones ya que un objeto debería siempre mantener un estado válido después de generarse una excepción.
  • 63.
    Se llama CallerConfuse, porque después de generase una excepción, el caller con frecuencia tiene poca información respecto al entendimiento de los detalles de la excepción o como podría ser solucionada. Caller Inform Las tercer forma Caller Inform agrega información que es devuelta al usuario, la excepción atrapada es envuelta en una excepción que tiene información adicional: using System; class ExcDivZeroInf{ public static void Main(){ int iA = 33; int iB = 0; try{ Console.WriteLine("{0}/{1} = {2}", iA ,iB, iA/iB); }catch(DivideByZeroException e){ Console.WriteLine("Se genero la excepcion : DivideByZeroException"); throw(new DivideByZeroException("Información adicional...", e)); } Console.WriteLine("Continua la ejecución del código..."); } } Chequeo de Overflow Si requerimos controlar el chequeo de overflow para la aplicación completa, el compilador de C# debe establecerse comochecked. Por default el compilador tiene deshabilitada la opción de chequeo. Para indicar explicitamente que el compilador cheque el overflow escriba: csc Factorial.cs /checked+ Una vez que se compilo de con la opción de chequeo de overflow habilitado, al intentar obtener el factorial de 2000 de presenta la ventana Just-In-Time-debbuging notificandonos que ocurrio una excepción en Factorial.exe: System.OverflowException Este tipo de situaciones es posible atraparlas y manejarlas a través de las excepciones que se producen. Chequeo programático de Overflow Existe otra opción si es que no deseamos activar el Chequeo de Overflow para la aplicación completa y habilitar sólamente partes especificas de código, para ello se utiliza la sentencia checked: using System; class FactorialChecked{ public static void Main(string[] aArgs){ if(aArgs.Length == 0){ Console.WriteLine("Debe proporcionar un argumento, Ejemplo: Factorial 5"); return; } long lFactorial = 1; long lCalcular = Int64.Parse(aArgs[0]); long lAux = 1; for(lAux = 1; lAux <= lCalcular; lAux++){
  • 64.
    checked{lFactorial *= lAux;}//Habilitar chequeo de overflow //Test Line Console.WriteLine("{0}! * {1}", lAux, lFactorial); } Console.WriteLine("{0}! es {1}", lCalcular, lFactorial); } } También es posible hacer el caso contrario, es decir, indicar que no se realice el chequeo de overflow para partes especificas de código, para ello se utiliza la sentencia: unchecked{sentencias;} Sentencias para el Manejo de Excepciones Es posible atrapar, manejar y limpiar las excepciones que se producen utilizando las sentencias siguientes:  try - catch  try - finally  try - catch - finally try - catch Para evitar que se muestre el mensaje que indica que una excepción ocurrio, es necesario atrapar la excepción y lo mejor de todo es que continue la ejecución del programa, para ello se utiliza try y catch. try contiene el código que quizá pueda lanzar una excepción y catch maneja la excepción si esta ocurre: try{ //sentencias que pueden lanzar una excepción }catch(nombreExcepción){ //manejo de la excepción } El siguiente ejemplo maneja la excepción FileNotFoundException que se produce cuando se intenta manipular un archivo que no existe, si esto ocurre se presenta un mensaje que muestra el nombre del archivo que se intento manipular y no se encontro a través de una propiedad pública de la excepción llamada FileName. using System; using System.IO; class SentenciaWhile{ public static void Main(){ try{ StreamReader SR = File.OpenText("test.html"); String strLinea = null; while(null != (strLinea = SR.ReadLine())){ Console.WriteLine(strLinea); } SR.Close(); }catch(FileNotFoundException e){//En caso de que el archivo no exista Console.WriteLine("No se encontro el archivo : " + e.FileName); return; } } } try - finally
  • 65.
    Es posible limpiarel manejo de errores utilizando try y el constructor finally, sin eliminar el mensaje de error, pero el código contenido en el bloque finally es ejecutado aún después de ocurrir una excepción. El siguiente código maneja una variable booleana que indica si se produjo un error, simplemente poniendola dentro del bloque try, si el código contenido fué ejecutado la variable booleana es false lo cual indica que no ocurrieron excepciones, si el bloque no se ejecuto la variable booleana mantiene su valor inicial lo cual significa que si ocurrieron excepciones y entonces se ejecuta el bloque Finally el cual evalua el valor de la variable booleana y presenta la indicación correspondiente. using System; using System.IO; class SentenciaTryFinally{ public static void Main(){ bool bExcepcion = true; try{ StreamReader SR = File.OpenText("test.html"); String strLinea = null; while(null != (strLinea = SR.ReadLine())){ Console.WriteLine(strLinea); } SR.Close(); bExcepcion = false; } finally{ if(bExcepcion){ Console.WriteLine(">>> No se encontro el archivo"); }else{ Console.WriteLine(">>> No ocurrieron excepciones"); } } } } Note que en caso de no existir el archivo se produce una excepción y se presenta el mensaje que indica que ha ocurrido una excepción pero también fué ejecutado el bloque finally, el código que contiene el bloque finally siempre es ejecutado ocurra o no una excepción. Puede emplear la sentencia finally para reestablecer los valores previos a la generación de la excepción. try - catch - finally Combinar try para controlar el código que puede lanzar excepciones, atrapar la excepción con catch y llevar acabo instrucciones necesarias con finally hacen una mejor solución cuando ocurren las excepciones. Es posible utilizar una sentencia catch por cualquier excepción que pudiera ocurrir, es decir, tener más de un bloquecatch, pero es necesario conocer la jerarquía de las excepciones porque puede ocurrir que un bloque previo catch sea más general y contenga todas las excepciones lo cual produciria un error. using System; using System.IO; class SentenciaTryCatchFinally{ public static void Main(){ bool bExcepcion = true;
  • 66.
    bool bModificacion =false; try{ bModificacion = true; StreamReader SR = File.OpenText("test.htmlX"); String strLinea = null; while(null != (strLinea = SR.ReadLine())){ Console.WriteLine(strLinea); } SR.Close(); bExcepcion = false; }catch(FileNotFoundException e){//En caso de que el archivo no exista Console.WriteLine("No se encontro el archivo : " + e.FileName); return; } finally{ if(bExcepcion){ bModificacion = false;//Valor antes de generarse la excepción if(!bModificacion){ Console.WriteLine("Entro en modo modificación, _ pero las modificaciones no se realizaron"); } Console.WriteLine("Causa : No se encontro el archivo"); }else{ Console.WriteLine("No ocurrieron excepciones"); } } } } Lanzamiento de Excepciones Para atrapar una excepción con la sentencia catch primero debe generarse la excepción, pero es posible que a través de codigo se lanze o invoque una excepción: throw new NombreExcepcion(excepcion); El poder lanzar o invocar una excepción es util cuando no se ha contemplado cierto escenario o para nuevos escenarios, al crear una clase podrian crearse también excepciones propias de esta clase. A continuación se presenta una tabla que contiene las excepciones estándar que provee el runtime: Tipo Descripción Exception Clase base para todas los objetos exception SystemException Clase base para todos los errores generados en tiempo de ejecución IndexOutRangeException Lanzada en tiempo de ejecución cuando el índice de un arreglo está fuera de rango NullreferenceException Disparada en tiempo de ejecución cuando un objeto null es referenciado InvalidOperationException Lanzada por ciertos métodos cuando invocan a métodos que son inválidos para el estado de los
  • 67.
    objetos actuales ArgumentException Clase base de todos los argumentos de las excepciones ArgumentNullException Lanzada por un método, en caso de que un argumento sea null cuando no sea permitido ArgumentOutOfRangeException Lanzada por un método cuando un argumento no está en el rango permitido InteropException Clase base para excepciones que son originadas u ocurren en ambientes fuera del CLR ComException Excepción que contiene información HRESULT COM SEHException Excepción que encapsula información del manejo de excepciones destructurada Win32 Relanzamiento de Excepciones El siguiente código muestra como es posible atrapar una excepción, manejarla y se volverla a invocar: using System; class FactorialCheckedReThrow{ public static void Main(string[] aArgs){ if(aArgs.Length == 0){ Console.WriteLine("Debe proporcionar un argumento, Ejemplo: Factorial 5"); return; } long lFactorial = 1; long lCalcular = Int64.Parse(aArgs[0]); long lAux = 1; try{ checked{ //Habilitar chequeo de overflow for(lAux = 1; lAux <= lCalcular; lAux++){ lFactorial *= lAux; //Test Line Console.WriteLine("{0}! * {1}", lAux, lFactorial); } } }catch(OverflowException){ Console.WriteLine("El factorial {0}! causo una excepción", lCalcular); throw; } Console.WriteLine("{0}! es {1}", lCalcular, lFactorial); } } Creación de Clases Exception Es recomendable utilizar las clases predefinidas para excepciones, para ciertos escenarios es posible crear clases de excepciones apropiadas, por ejemplo cuando creamos una clase, también podemos crear excepciones para esta clase, esto es conveniente cuando se utiliza la clase y manejar posibles escenarios de error con las clases de excepciones creadas. using System;
  • 68.
    public class MiExcepcion:Exception{ public MiExcepcion(string str):base(str){} } public class AplicMisExcepciones{ public static void Probar(){ throw new MiExcepcion("ocurrio un error"); } public static void Main(){ try{ AplicMisExcepciones.Probar(); }catch(Exception e){ Console.WriteLine(e); } } } Se deben tomar ciertas consideraciones al crear excepciones:  Al lanzar una excepción se debe proporcionar un texto significativo  Lanzar excepciones solo cuando se presente una condición realmente excepcional  Lanzar un ArgumentException si el método o propiedad envio mal los parámetros  Lanzar un InvalidOperationException cuando la operación invocada no es apropiada para el estado actual de los objetos  Lanzar la excepción más apropiada  Usar excepciones encadenadas  No utilizar excepciones para errores esperados o normales  No usar excepciones para control o flujo normal  No lanzar NullReferenceException o IndexOutOfRangeException en métodos Componentes Así como es posible escribir clases y hacer uso de estas en un mismo archivo, también es posible escribir en un archivo (ejecutable) unicamente la clase lo cual es conocido como componente y en otro archivo (también ejecutable) el uso de la clase lo cual es conocido como cliente. Creación de Componentes Para crear un componente unicamente es necesario escribir la Clase con todos sus miembros (Constructores, Propiedades, Métodos), almacenarla en un archivo y compilar el componente. using System; public class ClsPersona{ private int iSueldo; public int Sueldo{ get{return iSueldo;} set{iSueldo = value;} } public string sNombre; public void AsignarNombre(string sValor){ sNombre = sValor; } public string ObtenerNombre(){ return sNombre; } } Compilación de Componentes
  • 69.
    Al compilar uncomponente se crea una biblioteca y no se crea una aplicación, como es el caso cuando se trabaja en un sólo archivo la clase y la aplicación que hace uso de la clase: csc /t:library /out:ClsPersona.dll ClsPersona.cs Si utiliza nombres de espacio: csc /r:System.nombreBiblioteca.dll /t:library /out:NombreComponente.dll NombreComponente.cs Donde el switch /t:library indica al compilador de C# crear una biblioteca y no buscar el método estático Main. Si su clase requirio de nombres de espacio (namespace) es necesario hacer referencia a estas bibliotecas mediante elswitch /r:NombreBiblioteca.dll Recuerde que el switch /out no es obligatorio ya que se utiliza para especificar un nombre distinto al nombre del archivo fuente, aunque es buena práctica especificarlo ya que algunas aplicaciones no sólo tienen un archivo fuente, entonces el compilador podría llamar a la biblioteca con el primer nombre de archivo fuente en la lista. Una vez que ha compilado su componente puede escribir una Aplicación Cliente. Creación de una Aplicación Cliente Para hacer uso de los componentes creados es posible crear aplicaciones cliente donde se creen instancias de las clases creadas: using System; class AplicClsPersona{ public static void Main(){ ClsPersona Empleado = new ClsPersona(); Empleado.Sueldo = 33; Console.WriteLine("Edad : " + Empleado.Sueldo); Empleado.AsignarNombre("Gerardo Ángeles Nava"); Console.WriteLine("Nombre : " + Empleado.ObtenerNombre()); } } Compilación de Clientes Es necesario indicarle al compilador una referencia a la nueva biblioteca de componentes DLL: csc /r:NombreComponente.dll NombreCliente.cs Nota para hacer uso de una clase es necesario que tenga el modificador de acceso public. Namespaces Los nombres de espacio namespace en el runtime .NET son utilizados para organizar las clases y otros tipos en una estructura jerarquica. El propósito del uso de namespace hacen las clases fáciles de usar y prevenir colisiones con las clases escritas por otros programadores. Los namespace en C# se utilizan para organizar programas y la jerarquía natural de la organización facilita presentar los elementos de un programa a otros programas. Los namespace son utiles para la organización interna de aplicaciones. Un namespace contiene tipos que pueden ser utilizados en la construcción de programas: clases, estructuras, enumeraciones, delegados e interfaces, por ejemplo para poder escribir a la consola se utiliza el namespace System. No es obligatorio jerarquizar los namespace pero es una buena práctica organizar los namespace creados de manera jerarquica para dar claridad a la aplicación. Los nombres de espacio son definidos utilizando la sentencia: namespace Para múltiples niveles de organización los namespaces pueden ser anidados: namespace NombreNamespaceA{ namespace NombreNamespaceB{ class NombreClase{
  • 70.
    public static voidFunction(){} } } } El código anterior puede ser simplificado de la siguiente manera: namespace NombreNamespaceA.NombreNamespaceB{ class NombreClase{ public static void Function(){} } } Las colisiones entre tipos o nombres de espacio que tienen el mismo nombre se pueden resolver utilizando una variante de la cláusula using que permite definir un alias para la clase: using Alias = System.Console; class NombreClase{ public static void Main(){ Alias.WriteLine("Alias de una clase"); } } Envolver Clases en Namespace Es posible envolver (wrapping) las clases en un namespace, para ello sólo es necesario utilizar la palabra reservadanamespace seguida de un nombre que lo identifique y encerrar entre llaves el código que deseamos pertenezca a este. using NombreOtrosEspacios; namespace NombreEspacio{ public class NombreClase{ //propiedades //métodos } } Ejemplo: using System; namespace informatique.com.mx{ public class iPersona{ private int iSueldo; public int Sueldo{ get{return iSueldo;} set{iSueldo = value;} } public string sNombre; public void AsignarNombre(string sValor){ sNombre = sValor; } public string ObtenerNombre(){ return sNombre; } } } Compilar: csc /t:library /out:iPersona.dll iPersona.cs Utilizar Namespace en Aplicaciones Cliente Al desarrollar componentes utilizando namespace la aplicación cliente debe importarlo: using NombreEspacio;
  • 71.
    Otra posibilidad eshacer una referencia absoluta a los elementos del namespace, aunque para evitar conflictos es preferible utilizar la directiva: Ejemplo: using System; using informatique.com.mx; class iAplicClsPersona{ public static void Main(){ iPersona Empleado = new iPersona(); Empleado.Sueldo = 33; Console.WriteLine("Edad : " + Empleado.Sueldo); Empleado.AsignarNombre("Gerardo Ángeles Nava"); Console.WriteLine("Nombre : " + Empleado.ObtenerNombre()); } } Compilar: csc /r:iPersona.dll iAplicPersona.cs Especificar de manera absoluta el namespace: using System; class iAplicClsPersonaAbs{ public static void Main(){ informatique.com.mx.iPersona Empleado = new informatique.com.mx.iPersona(); Empleado.Sueldo = 33; Console.WriteLine("Edad : " + Empleado.Sueldo); Empleado.AsignarNombre("Gerardo Ángeles Nava"); Console.WriteLine("Nombre : " + Empleado.ObtenerNombre()); } } Compilar: csc /r:iPersonaAbs.dll iAplicPersonaAbs.cs Agregar Múltiples Clases a Un Namespace En el punto anterior envolvimos una clase a un namespace, pero es posible agregar y envolver más clases o componentes al mismo namespace, sin importar que esten contenidos en diferentes archivos, después de la compilación son parte del mismo namespace: csc /t:library /out:NombreComponente.dll archivoFuente1.cs archivoFuente2.cs Agregando otra clase al namespace informatique.com.mx: using System; namespace informatique.com.mx{ class iVehiculo{ private int iRueda; private int iPuerta; private int iVentana; private int iHelice; private int iMotor; private int iAsiento; private string sTipo;//Aereo, anfibio, terrestre, espacial
  • 72.
    //Constructor public iVehiculo(intRueda, 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; } 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;} } } } Compilar: csc /t:library /out:informatique.com.mx.dll iPersona.cs iVehiculo.cs Aplicación Cliente: using System; using informatique.com.mx; class iAplic{ public static void Main(){
  • 73.
    iPersona Empleado =new iPersona(); Empleado.Sueldo = 33; Console.WriteLine("Edad : " + Empleado.Sueldo); Empleado.AsignarNombre("Gerardo Ángeles Nava"); Console.WriteLine("Nombre : " + Empleado.ObtenerNombre()); //Clase que está contenida en otro archivo iVehiculo MiAvion = new iVehiculo(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); } } Compilar: csc /r:informatique.com.mx.dll iAplic.cs Recuerde que sus clases deben tener el modificador de acceso public, de lo contrario el compilador notificara un error (error CS0122: is inaccessible due to its protection level), por lo que tendrá que agregar el modificador de acceso yvolver a compilar. Namespace y Ensambles Un objeto puede ser utilizado desde un archivo fuente C# sólo si ese objeto puede ser localizado por el compilador C#, por default el compilador sólo abre el ensamble conocido como mscorlib.dll, el cual contiene las funciones principales para elCLR. Para referenciar objetos localizados en otros ensambles, el nombre del archivo de ensamble debe ser pasado al compilador, esto es posible utilizando un switch al compilar: /r:nombreEnsamble Es así como se crea un correlación entre el namespace de un objeto y el nombre del ensamble en el cual reside, por ejemplo los tipos de namespace en el System.Net residen en el ensamble System.Net.dll Compilación Condicional La Compilación Condicional permite excluir o incluir código, en C# existen dos formas de hacer esto:  Uso del Preprocesador  Atributo conditional Uso del Preprocesador C# el compilador emula el preprocesador, ya que no hay un preprocesador por separado. El compilador de C# no soporta macros, en cambio soporta las siguientes directivas:  Definición de símbolos  Exclusión de código basado en símbolos  Lanzamiento de errores y advertencias Definición de símbolos
  • 74.
    La Definición desímbolos es utilizada para excluir o incluir código dependiendo si son o no son definidos ciertos símbolos. Una forma para definir un símbolo es utilizando la directiva #define en un archivo fuente C#, está definición deberá realizarse antes de cualquier otra sentencia: #define DEBUG #define RELEASE En este caso #define DEBUG, define un símbolo DEBUG y su ámbito es el archivo donde es definido, al igual que el símbolo RELEASE. Otra forma utilizada para definir símbolos es usar el compilador y es de ámbito global para todos los archivos: csc /define:DEBUG nombreArchivo.cs Si se requiere definir múltiples símbolos utilizando el compilador, es necesario separar cada símbolo con una coma (,) csc /define:DEBUG,RELEASE,DEMOVERSION nombreArchivo.cs Si el código fuente incluye directivas de definición de símbolos, es posible deshabilitarlas utilizando la directiva #undef cuyo ámbito también corresponde al archivo donde es definida: #undef DEBUG Exclusión de código basado en símbolos El principal propósito de los símbolos es la inclusión o exclusión condicional del código, basado sobre si son o no son definidos los símbolos. El siguiente código no define símbolos en el archivo fuente: . sentencia; . #if NOMBRE_SIMBOLO sentencia; #else sentencia; #endif . Pero es posible definir o no los símbolos al compilar la aplicación: csc /define:NOMBRE_SIMBOLO NombreAplicacion.cs Las directivas del preprocesador emulado utilizadas para evaluar el símbolo son: #if, #else y #endif las cuales actuan como su contraparte, la sentencia condicional if C#, es posible utilizar &&, || y !: //#define SIMBOLO_A #define SIMBOLO_B #define SIMBOLO_C #if SIMBOLO_A #undef SIMBOLO_C #endif using System; class NombreClase{ public static void Main(){ #if SIMBOLO_A . #elif SIMBOLO_B && SIMBOLO_C . #else . #endif
  • 75.
    } } Lanzamientode errores y advertencias Existe otro uso de las directivas del preprocesador para lanzar errores del compilador o advertencias dependiendo de ciertos símbolos, para ello se utilizan las directivas:  #warning  #error Por ejemplo #if SIMBOLO_ERROR #error Presentar el mensaje de error correspondiente #endif . #if SIMBOLO_WARNING #error Presentar el mensaje de advertencia correspondiente #endif Atributo conditional Un atributo conditional evalua la invocación de una función cuando sierto símbolo es definido y evalua a nada cuando una versión liberada es construida. Un atributo conditional debe tener un tipo void de regreso, cualquier otro tipo de regreso no es permitido. . [conditional("NOMBRE_SIMBOLO")] método A [conditional("NOMBRE_SIMBOLO")] método B . Comentarios de Documentación en XML Es posible construir automáticamente la Documentación utilizando comentarios en el código. La salida que es generada por el compilador es XML puro y puede ser utilizada como entrada para la documentación de un componente. La documentación es una parte extremadamente importante del software y en especial de los componentes por ser utilizados por otros desarrolladores. Elementos XML Nota: todo comentario de documentación son tags XML (eXtensible Markup Language). Para describir un elemento se utiliza el tag <summary></summary>, el cual se escribe en el código fuente anteponiendo tres diagonales que son indicativo de un comentario de documentación: ///<summary>Descripción...</summary> En caso de requerir más una línea para el comentario de documentación utilice el tag: ///<para> ///. ///. ///</para> En caso de requerir una referencia a otros elementos utilice el tag: ///<see cref="NombreElemento"/> En caso de requerir una referencia a un tópico de interes utilice: ///<seealso cref="System.Net"/> Un tag contrario a summary, para un volumen mayor de documentación es: ///<remarks> Es posible incluir listas utilizando los tags : ///<list type="bullet"> /// <item>Constructor
  • 76.
    /// <see cref="Constructor()"/> /// <see cref="Constructor(string)"/> /// </item> ///</list> Para describir parámetros se utiliza el tag: ///<paramref name="nombreParametro"/> Es posible encerrar en un tag un ejemplo completo e incluir la descripción y código: ///<example> /// . /// <code> /// . /// </code> /// . ///</example> Para describir los parámetros de regreso utilice: ///<returns> /// <para>true: valor obtenido</para> /// <para>false: valor no obtenido</para> ///</returns> Para describir propiedades de clase se utiliza un tag especial: ///<value>Propiedad...</value> Una vez que el proceso de documentación para un componente es terminado, es posible crear un archivo XML basado en la documentación descrita en el código fuente y hacerla disponible a quienes utilicen el componente, para ello simplemente se utiliza al compilar el switch /doc: seguido del documento XML. csc /doc:Persona.xml AplicPersona.cc Ejemplo: using System; /// /// Clase Persona /// Autor : Gerardo Ángeles Nava /// Fecha : 10/09/2005 /// Descripción : Clase que define lo que es y puede hacer una persona /// /// class Persona{ ///La propiedad iSueldo se emplea para obtener y asignar el valor del sueldo de una persona private int iSueldo; public int Sueldo{ get{return iSueldo;} set{iSueldo = value;} } public string sNombre; ///Utilizado para asignar el valor de la propiedad sNombre public void AsignarNombre(string sValor){ sNombre = sValor; }
  • 77.
    public string ObtenerNombre(){ return sNombre; } } class AplicPersona{ public static void Main(){ Persona Empleado = new Persona(); Empleado.Sueldo = 33; Console.WriteLine("Edad : " + Empleado.Sueldo); Empleado.AsignarNombre("Gerardo Ángeles Nava"); Console.WriteLine("Nombre : " +Empleado.ObtenerNombre()); } } Salida: para ver el documento XML generado de clic aquí. El compilador realizará validación sobre los tags XML en el archivo fuente, en caso de existir errores el compilador lo notificará y aún así el documento XML es generado pero tendrá un mensaje de error. El documento XML tendrá algunos identificadores seguidos de dos puntos (:) y la ruta del namespace, los cuales por ejemplo tienen los siguiente significados.  N, denota un namespace  T, identifica un tipo, el cual puede ser una clase, interface, estructura, enumeración o delegado  F, describe un campo o clase  P, se refiere a una propiedad la cual también puede ser un indíce o propiedad índice.  M, identifica un método, incluyendo constructores y operadores.  E, denota eventos  !, denota un error el cual provee información acerca de una liga que el compilador C# no pudo resolver. Si un elemento tiene períodos en su nombre, estos son reemplazados por el símbolo #. Los parámetros para métodos son encerrados entre paréntesis y separados por comas (,). Componentes .NET El compilador de C# por default siempre crea componentes .NET para los ejecutables. Un Componente .NET es la unidad fundamental reusable y compartida en el CLR, un componente .NET también es limitante para asegurar la seguridad, permite la distribución de clases y resolución de tipos. Una aplicación puede contener múltiples componentes .NET Un componente .NET contiene cuatro partes referentes al número de versión llamado versión compatible: major version.minor version.build number.revision La versión compatible es utilizada por el class loader para decidir cual es la versión del componente .NET que cargará, en caso de existir diferentes versiones. Se considera una versión incompatible cuando major version.minor version es diferente de la versión solicitada. Se podría considerar una versión compatible cuando el build number es diferente a la versión solicitada. Se considera una QFE (Quick Fix Engineering) compatible cuando revision es diferente. Además del número de versión (versión compatible) se almacena otro número en el componente llamado informational version, el cual es considerado sólo para propósitos de documentación y su contenido podría ser SuperControl Build 1880, el contenido representa algo humano y no para la máquina. Para indicar al compilador que agregue un version information al componente se utiliza el switch:
  • 78.
    csc /a.version:1.0.1.0 /t:library/out:nombreArchivoFuente.dll nombreArchivoFuente.cs El switch /a.version crea una biblioteca con el version information 1.0.1.0, esto puede comprobarse en las propiedades del archivo.dll. Componentes .NET Privados Al ligar una aplicación a un componente .NET utilizando el switch /reference:nombreBiblioteca la información de dependencia registra las herramientas de desarrollo, incluyendo la versión de las bibliotecas ligadas, este registro se hace en un manifiesto y el CLR los números de versión contenidos para cargar la versión apropiada de un componente .NET dependiente en tiempo de ejecución. Cualquier componente .NET que reside en el directorio de la aplicación es considerado privado y no es version-checked Componentes .NET Compartidos Si se requiere construir software que se comparta con otras aplicaciones, el componente .NET deberá ser instalado comocompartido. Interoperabilidad COM COM es una técnica de interoperabilidad, por lo que los clientes .NET deberán ser capaces de invocar componentes COM y componentes COM deberán hacer uso de los nuevos componentes .NET, esta es una característica de iteroperabilidad proporcionada por la plataforma .NET para todos los lenguajes de programación que emiten código administrado. Uso de Componentes .NET en Componentes COM La interoperabilidad permite a clientes COM utilizar componentes .NET, para hacer esto posible en COM primero es necesario registrar un objeto antes de poder ser utilizado, para registrar un objeto COM se utiliza la aplicación regsvr32 y para registrar un componente .NET se utiliza regasm.exe, esta herramienta permite registrar un componente .NET en elRegistry y también crear un archivo Registry. Si requiere examinar las entradas agregadas al Registry escriba en la línea de comandos: regasm nombreComponente.dll /regfile:nombreArchivoComponente.reg Ahora puede examinar el archivo generado nombreArchivoComponente.reg. Nota asegurese de que el directorio en el que se encuentra exista el archivo .dll o escriba la ruta completa de su ubicación. Ejemplo: REGEDIT4 [HKEY_CLASSES_ROOTinformatique.com.mx.iPersona] @="informatique.com.mx.iPersona" [HKEY_CLASSES_ROOTinformatique.com.mx.iPersonaCLSID] @="{37504224-213A-3943-845A-E572758E4174}" [HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174}] @="informatique.com.mx.iPersona" [HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} InprocServer32] @="mscoree.dll" "ThreadingModel"="Both" "Class"="informatique.com.mx.iPersona" "Assembly"="iPersona, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" "RuntimeVersion"="v1.1.4322" [HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} InprocServer32.0.0.0]
  • 79.
    "Class"="informatique.com.mx.iPersona" "Assembly"="iPersona, Version=0.0.0.0,Culture=neutral, PublicKeyToken=null" "RuntimeVersion"="v1.1.4322" [HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} ProgId] @="informatique.com.mx.iPersona" [HKEY_CLASSES_ROOTCLSID{37504224-213A-3943-845A-E572758E4174} Implemented Categories_ {62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}] El motor de ejecución mscoree.dll es invocado cuando una instancia del objeto (componente registrado) es requerida, más no la biblioteca por si sóla. El motor de ejecución es responsable de proveer la CCW (COM Callable Wrapper) al objeto. Una vez registrado el componente puede ser utilizado por lenguajes de programación que soporten esta vinculación, también es posible emplear la utileria tlbexp la cual permite generar una biblioteca tipo para el componente .NET tlbexp nombreComponente.dll /out:nombreBiblioteca.tlb Esta biblioteca tipo puede ser utilizada en lenguajes de programación que soporten esta vinculación. El Componente .NET y todas las clases ya están registradas y se tiene una biblioteca tipo para ambientes Uso de Componentes COM en Componentes .NET Los Clientes .NET pueden interoperar con objetos clásicos COM, para que un componente utilise COM debe tener una biblioteca tipo para el CLR esto se traduce a los metadatos que son almacenados con los tipos. Para que sea posible invocar un componente COM desde un objeto .NET, es necesario envolver el código unsafe, cuando la envoltura o wrapper es invocada, un RCW (Runtime Callable Wrapper) es construido desde la información de la biblioteca tipo. Una herramienta genera el código wrapper basado en la información obtenida de la biblioteca tipo. La herramienta a utilizar es tlimp (type library import): tlbimp nombreComponente.dll /out:nombreBiblioteca.dll Esta herramienta importa el tipo de biblioteca COM, crea y almacena un RCW que puede ser utilizado en el CLR en el archivo nombreBiblioteca.dll. Para ver los metadatos para el RCW utilice ildasm.exe, así podrá distinguir el nombre de la clase que fué generada para el objeto COM original, esta información es una ayuda para poder escribir el objeto .NET que utiliza el componente COM. Invocación de Servicios Se se requiere invocar una función provista por el WIN32 o alguna DLL unmanaged, se utilizan los Servicios de invocación de plataforma (PInvoke). PInvoke se encarga de ejecutar la función correcta y también de la colocación de los argumentos para y desde sus contrapartes unmanaged. Simplemente utilice el atributo sysimport al definir un método externo: [sysimport( dll = nombreDLL, name = puntoEntrada, cgarset = conjuntoCaracteres ] Unicamente el argumento dll es obligatorio y los demás opcionales, aunque si se omite el atributo name, el nombre de la función de implementación externa debe coincidir con el nombre del método estático interno. Código No Seguro
  • 80.
    Si requiere deescribir código no seguro, deberá utilizar dos palabras reservadas:  unsafe, denota un contexto no seguro, cuando requiera realizar acciones no seguras se debe envolver el código correspondiente con este modificador, el cual puede ser aplicado a constructores, métodos y propiedades.  fixed, al declarar una variable con este modificador previene al recolector de basura de reacomodarlo. Debugging El SDK .NET incorpora dos herramientas de depuración de errores:  CORDBG, depurador de línea de comando  SDK, depurador UI  El depurador SDK no soporta la depuración de código nativo, sólo es posible depurar código administrado.  No es posible la depuración de una máquina remota  Window register y disassembly aunque se implementan no son funcionales Antes de depurar el código de una aplicación es necesario crear una versión depurada, la cual contiene información de depuración no optimizada y un archivo adicional PDB (program database) para depuración y un estado de información del proyecto es creado. Para crear una versión depurada son necesarios dos switches al compilar: csc /optimize- /debug+ nombreArchivoFuente.cs Estos comandos utilizados al compilar crea dos archivos: nombreArchivoFuente.exe y nombreArchivoFuente.pdb Para configurar la sesión de depuración es necesario seleccionar la aplicación que se desea depurar y comenzar el depurador SDK ejecutando dbgurt.exe, el cual esta almacenado en el directorio ProgramFilesNGWSSDKGuiDebug Una vez que la aplicación depuradora comienza se selecciona el programa que se desea depurar en donde será también posible especificar argumentos en la línea de comandos, mismos que son pasados a la aplicación cuando la sesión de depuración inicia. Es posible establecer diferentes tipos de breakpoint:  File, interrumpe la ejecución cuando una ubicación específica en el archivo fuente se alcanza  Data, interrumpe la ejecución cuando una variable cambia a un valor especifico  Function, interrumpe la ejecución en una ubicación específica dentro de una función específica  Address, interrumpe la ejecución cuando una dirección de memoria específica se alcanza Una vez que se interrumpe la ejecución, es posible continuarla utilizando los comandos:  Step Over  Step Into  Step Out  Run to Cursor Es posible modificar valores de variables simplemente dando doble clic en la columna valor de aquella variable que se desea modificar, así como también es posible observar las variables, para ello es necesario dar clic en la columna nombre y escribir el nombre de las variables que se desean observar. Las excepciones son un punto excelente de comienzo para una sesión de depuración, cuando una excepción no es controlada apropiadamente por el código se muestra la ventana de depuración llamada JIT just in time.
  • 81.
    Seguridad Hoy endía el código viene de distintas fuentes, no solo el que es instalado via un setup por el servidor, también puede ser instalado via una página web o correo electrónico. .NET plantea dos posibles soluciones para la seguridad:  Seguridad de acceso al código  Seguridad basada en roles Seguridad de acceso al código La Seguridad de acceso al código controla el acceso protegiendo los recursos y operaciones. El código es confiable en distintos grados, dependiendo su identidad y de donde viene. Funciones de la seguridad de acceso al código:  El administrador puede definir las políticas de seguridad asignando ciertos permisos para definir grupos de código.  El código puede requerir que quien invoca (caller) debe tener permisos especificos  La ejecución de código está restringido en tiempo de ejecución, realizando chequeos para verificar que los permisos otorgados a quien invoca (caller) coincidan con el permiso requerido para la operación.  El código puede requerir los permisos necesarios de ejecución y los permisos que podrían ser utilizados, es decir, verificar los permisos indispensables.  Los permisos son definidos para representar ciertos derechos para acceder a distintos recursos del sistema.  La seguridad de acceso al código otorga permisos cuando un componente es cargado, este otorgamiento esta basado en el requerimiento del código, definiendo operaciones permitidas por las políticas de seguridad. Existen dos puntos importantes de la seguridad de acceso al código, donde el requerimiento mínimo para beneficiarse de la seguridad de acceso al código es para generar un código de tipo seguro.  Verificar el tipo de seguridad del código administrado, el runtime forza la restricción de seguridad del código administrado, para determinar cuando el código es seguro. Es importante que el runtime sea capaz de checar los permisos de quien invoca de manera confiable, evadiendo hoyos de seguridad que son creados cuando código menos confiable invoca código altamente confiable, para ello el código administrado debe ser verificado como tipo seguro. Cada acceso a tipos se realiza sólo en un sentido permitido. El código C# es de tipo no seguro, pero el IL y los metadatos son inspeccionados antes de dar el visto bueno del tipo de seguridad del código.  Permisos que son requeridos por el código, el beneficio de activar el requerimiento de permisos es conocer cuando se tiene el permiso apropiado para realizar acciones y cuando no. Es posible prevenir al código de el otorgamiento de permisos adicionales que no son necesarios. Los permisos mínimos garantizan que el código se ejecute con los recursos justos cuando el código requiere de muchos permisos sin que falle. La categoria de los permisos es:  Required, permiso que el código necesita para ejecutarse correctamente.  Optional, permisos que no son obligatorios para la ejecución correcta del código, pero que podría ser bueno tenerlos.  Refused, permiso que se necesita para que el código nunca se otorgue, aunque la política de seguridad lo permita, se utiliza para restringir vulnerabilidades potenciales. Permisos Estándar Los Permisos Estándar son:
  • 82.
     EnvironmentPermission, claseque define permisos de acceso a variables de ambiente, donde son posibles dos tipos de acceso, de sólo lectura y escritura al valor de una variable de ambiente. El tipo de acceso escritura incluye permisos para crear y eliminar variables de ambiente.  FileDialogPermission, controla el acceso a archivos basado en el sistema de archivos de diálogo. El usuario debe autorizar el acceso al archivo via el cuadro de diálogo.  FileIOPermission, es posible especificar tres tipos de acceso a archivos de entrada y salida: lectura, escrituray adición, el acceso lectura incluye acceder a la información del archivo, el tipo escritura incluye eliminar y sobreescribir, el acceso adición no permite leer otros bits.  IsolatedStoragePermission, controla el acceso a almacenamientos aislados, este acceso permite utilización, tamaño de almacenamiento, tiempo de expiración y almacenamiento de datos.  ReflectionPermission, controla la capacidad de leer el tipo de información de tipos miembro no públicos y controla el uso de Reflection.Emit  RegistryPermission, Control de lectura, creación y escritura en el registry  SecurityPermission, colección de permisos simples que son utilizados por el sistema de seguridad, es posible controlar la ejecución de código, sobreescritura de chequeos de seguridad, invocación de código no administrado, serialización, etc.  UIPermission, define el acceso a varios aspectos de la interfaz de usuario, incluyendo el uso de windows, acceso a eventos y uso del portapapeles. Permisos Identidad Los Permisos Identidad son:  PubilsherIdentityPermission, la firma de componentes .NET provee resistencia de software publisher  StrongNameIdentityPermission, define el nombre del componente criptograficamente, ya que el nombre compromete la identidad.  ZoneIdentityPermission, define la zona de donde el código tiene origen, un URL puede pertenecer a sólo una zona  SiteIdentityPermission, permisos derivados basados en el sitio web de donde el código tiene origen  URLIdentityPermission, permisos derivados basados en URL de donde el código tiene origen Seguridad basada en roles La Seguridad basada en roles representa a un usuario o agente que actua en representación de un usuario dado. Las aplicaciones .NET hacen decisiones basadas en la identidad principal o su role como miembro. Un role es un nombre para un conjunto de usuarios quienes comparten los mismos privilegios. Un principal puede ser un miembro de múltiples roles y de esta manera se puede utilizar un role para determinar si ciertas acciones requeridas quizá sean realizadas por un principal. Un principal no necesariamente es un usuario, también puede ser un agente. Existen tres tipos de principal:  Generic principals, representa usuarios no autentificados.  Windows principals, relación de usuarios windows y sus roles, el acceso a recursos de otro usuario es permitido.
  • 83.
     Custom principals,definido por una aplicación. Pueden extender la noción básica de la identidad y los roles del principal. La restricción es que la aplicación debe proveer un módulo de autentificación de los tipos que elprincipal puede implementar. La clase Principalpermission provee consistencia con la seguridad de acceso, permitiendo al runtime realizar la autorización en un sentido similar al chequeo de la seguridad de acceso al código, pero es posible acceder directamente a la información de identidad principal y realizar chequeos de role e identidad en el código cuando sea necesario. Función ToString() Analice el siguiente ejemplo: using System; class Empleado{ string usr; string pwd; public Empleado(string login, string pwd){ this.usr = login; this.pwd = pwd; } } class App{ public static void Main(){ Empleado empleado = new Empleado("gangeles","123"); Console.WriteLine("Empleado : " + empleado); } } Salida: Empleado : Empleado La salida fue el nombre de la clase Empleado ya que es la representación más cercana que encontro. Es posible especificar algo con mayor sentido para ello se necesita redefinir la función ToString(): using System; class Empleado{ string usr; string pwd; public Empleado(string login, string pwd){ this.usr = login; this.pwd = pwd; } public override string ToString(){ return("Usuario : " + usr + ", Password : " + pwd); } } class App{ public static void Main(){ Empleado empleado = new Empleado("gangeles","123"); Console.WriteLine(empleado); } } Salida: Usuario : gangeles, Password : 123 Función Equals() La función Equals() es utilizada para determinar cuando dos objetos tienen el mismo contenido.
  • 84.
    En el siguienteejemplo se redefinen las funciones operator==() y operator!=(), para permitir la sintaxis del operador, estos operadores deben ser redefinidos en pares, no pueden ser redefinidos separadamente. Ejemplo: using System; class Empleado{ string usr; string pwd; public Empleado(string login, string pwd){ this.usr = login; this.pwd = pwd; } public override string ToString(){ return("Usuario : " + usr + ", Password : " + pwd); } public override bool Equals(object o){ Empleado empB = (Empleado)o; if(usr != empB.usr){return false;} if(pwd != empB.pwd){return false;} return true; } public static bool operator==(Empleado empA, Empleado empB){ return empA.Equals(empB); } public static bool operator!=(Empleado empA, Empleado empB){ return !empA.Equals(empB); } } class App{ public static void Main(){ Empleado empleado1 = new Empleado("gangeles","123"); Empleado empleado2 = new Empleado("gangeles","123"); Console.WriteLine("El empleado1 es igual al empleado2: "+empleado1.Equals(empleado2)); Console.Write("empleado1 == empleado2 : "); Console.Write(empleado1 == empleado2); } } Salida: El empleado1 es igual al empleado2 : True empleado1 == empleado2 : True Al compilar el ejemplo anterior se presentara un warning indicando que no fue redefinada la función GetHashCode() ya que los valores que regresa son requeridos para ser relacionados al valor de regreso de Equals(). Cuando se invoca la funciónEquals() y dos objetos son iguales siempre se debe regresar el mismo código hash. Si no es redefinido el código hash podría ser sólo identico para la misma instancia de un objeto y una búsqueda para un objeto que es igual pero no la misma instancia podría fallar. Es posible utilizar un miembro que es único para el código hash, pero si no existe un valor único el código hash debería ser creado fuera de los valores contenidos en la función. Si la clase no tiene un identificador único pero tiene tiene otros campos, podrías ser utilizados por la función hash: using System; class Empleado{
  • 85.
    string usr; stringpwd; public Empleado(string login, string pwd){ this.usr = login; this.pwd = pwd; } public override string ToString(){ return("Usuario : " + usr + ", Password : " + pwd); } public override bool Equals(object o){ Empleado empB = (Empleado)o; if(usr != empB.usr){return false;} if(pwd != empB.pwd){return false;} return true; } public static bool operator==(Empleado empA, Empleado empB){ return empA.Equals(empB); } public static bool operator!=(Empleado empA, Empleado empB){ return !empA.Equals(empB); } public override int GetHashCode(){ return usr.GetHashCode() + pwd.GetHashCode(); } } class App{ public static void Main(){ Empleado empleado1 = new Empleado("gangeles","123"); Empleado empleado2 = new Empleado("gangeles","123"); Console.WriteLine("El empleado1 es igual al empleado2: "+empleado1.Equals(empleado2)); Console.Write("empleado1 == empleado2 : "); Console.Write(empleado1 == empleado2); } } La implementación del código GetHashCode anterior agrega los elementos y los regresa. Clase Hashtable La clase Hashtable es muy utilizada para realizar una búsqueda de objetos por una llave. Una tabla hash trabaja utilizando una función hash, la cual produce un entero llave para una instancia específica de una clase, donde esta llave es una versión condensada de la instancia. Una tabla hash utiliza esta llave para limitar drasticamente el número de objetos que deben ser buscados para encontrar un objeto específico en una colección de objetos. Interface IHashCodeProvider Si requiere definir diferentes códigos hash para un objeto específico, podría hacer esto implementado la InterfaceIHashCodeProvider para proveer una función alterna hash y además de que se requiere una coincidencia de la implementación de IComparer, estas nuevas implementaciones son pasadas al contructor de la Hashtable: using System; using System.Collections; public class Lenguaje : IComparable{ public string nombre; int id;
  • 86.
    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 override int GetHashCode(){ return id; } public static IComparer OrdenaPorNombre{ get{ return (IComparer) new OrdenaNombres(); } } public static IHashCodeProvider HashNombre{ get{ return (IHashCodeProvider) new HashNombreCls(); } } 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 HashNombreCls : IHashCodeProvider{ public int GetHashCode(object o){ Lenguaje len = (Lenguaje)o; return len.nombre.GetHashCode(); } } class App{ public static void Main(){ Lenguaje[] aLenguaje = new Lenguaje[5]; aLenguaje[0] = new Lenguaje("C",3);
  • 87.
    aLenguaje[1] = newLenguaje("ActionScript",5); aLenguaje[2] = new Lenguaje("JavaScript",2); aLenguaje[3] = new Lenguaje("Java",8); aLenguaje[4] = new Lenguaje("PHP",1); Hashtable lenguajes = new Hashtable(Lenguaje.HashNombre, Lenguaje.OrdenaPorNombre); lenguajes.Add(aLenguaje[0], "zxc"); lenguajes.Add(aLenguaje[1], "bnm"); lenguajes.Add(aLenguaje[2], "sdf"); lenguajes.Add(aLenguaje[3], "wer"); lenguajes.Add(aLenguaje[4], "tgh"); Lenguaje clone = new Lenguaje("MiLenguaje", 12345); string s = (string) lenguajes[clone]; Console.WriteLine(clone.ToString(), s); } } Función IClonable La función object.MemberWiseClone() puede ser utilizada para crear un clon de un objeto. La implementación por default de esta función produce una copia de un objeto, los campos de un objeto son copiados exactamente en lugar de ser duplicados: using System; class Saludo{ public string s; public Saludo(string s){ this.s = s; } } class MiClase{ public Saludo saludo; public MiClase(string s){this.saludo = new Saludo(s);} public MiClase Clon(){return (MiClase)MemberwiseClone();} } class App{ public static void Main(){ MiClase miClase = new MiClase("Hello World!"); MiClase miClon = miClase.Clon(); Console.WriteLine("miClase : " + miClase.saludo.s); Console.WriteLine("miClon : " + miClon.saludo.s); miClon.saludo.s = "Hola Mundo"; Console.WriteLine("miClase : " + miClase.saludo.s); Console.WriteLine("miClon : " + miClon.saludo.s); } } Salida: miClase : Hello World! miClon : Hello World! miClase : Hola Mundo miClon : Hola Mundo El resultado anterior es porque la copia hecha por la función MemberWiseClonre() es una copia, el valor de saludo es el mismo en ambos objetos por lo que se se cambia un valor dentro del objeto Saludo afecta ambas instancias de MiClase.
  • 88.
    Interface ICloneable Paracrear una copia deep, donde una nueva instancia de Saludo es creada para para la nueva instancia de MiClase, para ello se hace una implementación de la interface ICloneable: using System; class Saludo{ public string s; public Saludo(string s){ this.s = s; } } class MiClase : ICloneable{ public Saludo saludo; public MiClase(string s){this.saludo = new Saludo(s);} public object Clone(){return (new MiClase(this.saludo.s));} } class App{ public static void Main(){ MiClase miClase = new MiClase("Hello World!"); MiClase miClon = (MiClase) miClase.Clone(); Console.WriteLine("miClase : " + miClase.saludo.s); Console.WriteLine("miClon : " + miClon.saludo.s); miClon.saludo.s = "Hola Mundo!"; Console.WriteLine("miClase : " + miClase.saludo.s); Console.WriteLine("miClon : " + miClon.saludo.s); } } Salida: miClase : Hello World! miClon : Hello World! miClase : Hello World! miClon : Hola Mundo La invocación a Memberwiseclone() regresa una nueva instancia de Saludo y su contenido puede ser modificado sin afectar el contenido de miClase. Note que en este caso ICloneable requiere implementar la función Clone(). Formato Numérico Los tipos numéricos son formateados a través de la función miembro Format() del tipo de dato, la cual puede ser invocada directamente a través de String.Format() la cual invoca a la función Format() de cada tipo de dato o Console.WriteLine()la cual invoca a String.Format(). Existen dos tipos de métodos para el formateo específico numérico: Formato Estándar String Formato Estándar String, el cual puede ser utilizado para convertir un tipo numérico a una representación específica string. Este formato consiste del formato específico del caracter seguido de la secuencia de precisión específica de digitos, los formatos soportados son: Formato Descripción Ejemplo Salida
  • 89.
    C, c Currency Console.WriteLine("0:C", 123.8977); $123,345.90 D, d Decimal Console.WriteLine("0:D7", 12345); 0012345 E, e Scientific (exponential) Console.WriteLine("0:E", 33345.8977); 3.334590E+004 E, f Fixed-point Console.WriteLine("0:F", 33345.8977); 3.334590E+004 G, g General Console.WriteLine("0:G", 33345.8977); 33345.8977 N, n Number Console.WriteLine("0:N", 33345.8977); 33,345.90 X, x Hexadecimal Console.WriteLine("0:X", 255); FF Formato Personalizado String El Formato Personalizado String, son utilizados para obtener más control, sobre la conversación que está disponible a través del formato estándar de strings. Reemplazo de Cero o Digito Console.WriteLine("{0:000}",12); Salida: 012 Reemplazo de Espacio o Digito Console.WriteLine("{0:#####}",123); Salida: 123 Punto Decimal Console.WriteLine("{0:#####.000}",12345.2); Salida: 12345.200 Separador de Grupo Console.WriteLine("{0:##,###}",1123456789); Salida: 1,123,456,789 Separador de Grupo Console.WriteLine("{0:000,.##}",12394); Salida: 123.95 Porcentaje Console.WriteLine("{0:##.000%}",98144); Salida: 98.144% Notación Exponencial Console.WriteLine("{0:###.000E-00}",3.1415533E+04); Salida: 314.155E-02 Separador de Sección Console.WriteLine("{0:###.00;0;(###.00)}",-456.55); Salida: 457 Escape y Literales Console.WriteLine("{0:####}",255); Salida: 255# Parseo Numérico Los números pueden ser parseados utilizando el método Parse(), esto es posible ya que lo provee el tipo de dato. int iValue = Int32.Parse("123"); double dValue = Double.Parse("1.23"); Input/Output El lenguaje Común en Tiempo de Ejecución .NET provee funciones de entrada/salida en el namespace System.IO. La lectura y escritura la realiza la clase Stream, la cual describe como los bytes pueden ser escritos o leidos. Stream es una clase abstracta que en la práctica las clases derivan de Stream para ser utilizadas. Clases disponibles:
  • 90.
     FileStream, flujoen un archivo de disco  MemoryStream, flujo que es almacenado en memoria  NetworkStream, flujo en una conexión de red  BufferedStream, implementa un buffer en la cima de otro stream. Lectura y Escritura de Archivos Existen dos formas de obtener flujos (streams) que conectan a un archivo, uno utilizando la clase FileStream, la cual provee un control total sobre el acceso de archivos, incluyendo modo de acceso, compartido y buffering. A continuación se muestra un ejemplo donde, cada vez que el programa es ejecutado se sobreescribe el contenido del archivo en caso de que este exista, si no existe se crea: using System; using System.IO; class App{ public static void Main(){ FileStream f = new FileStream("nuevo.txt", FileMode.Create); StreamWriter s = new StreamWriter(f); for(int iNumberLine = 1; iNumberLine <= 10; iNumberLine++){ s.WriteLine("Linea " + iNumberLine); } s.Close(); f.Close(); } } Salida, Archivo Nuevo.txt cuyo contenido es: Linea 1 Linea 2 Linea 3 Linea 4 Linea 5 Linea 6 Linea 7 Linea 8 Linea 9 Linea 10 Serialización La Serialización es el proceso utilizado por el runtime para objetos persistentes en algún orden de almacenamiento o para transferirlos de un lugar a otro. La información de los metadatos en un objeto contiene información suficiente para que el runtime serialice los campos, pero es necesario indicar al runtime hacerlo correctamente a través de dos atributos [Serializable] el cual es utilizado para marcar un objeto que es posible serializar y [NonSerialized] que es aplicado para un campo o propiedad para indicar que no debería ser serializado. Threading (Hilos) El namespace System.Threading contiene clases utilizadas para threading y sincronización. El tipo apropiado de sincronización y/o exclusión depende del diseño del programa, pero C# soporta exclusión simple utilizando la sentencialock. lock utiliza la clase System.Threading.Monitor y provee funcionalidad similar a la invocación de CriticalSection en Win32