Escolar Documentos
Profissional Documentos
Cultura Documentos
NET
Índice Temático
Básico » Intermedio » Avanzado
¿Qué es C#?
Lenguaje Orientado a Objetos
Objeto
Herencia
Clase
Funciones Miembro
Campos Estáticos
Clase Base
Clase Derivada
Clase Abstracta
Clase Sealed
Overloading/Sobrecarga
Herencia Simple
Polimorfismo y Funciones Virtuales
Encapsulación y Visibilidad
Abstracción
Plataforma .NET
.NET Common Language Runtime - CLR
Características del CLR
.NET Runtime Environment
Ambiente/Entorno de Ejecución
Ensamblaje
Interoperabilidad de Lenguaje
Atributos
Biblioteca de Clases de la Plataforma .NET
Requerimientos de Software
Lenguaje Intermedio y Metadatos
JIT
Sistema de Tipo Unificado
El Sistema Virtual de Objetos - VOS
VOS Type System
Metadata y Reflection
Seguridad
Deployment
Interoperabilidad con código no administrado
Common Language Specification - CLS
Virtual Execution System - VES
Tipos de Datos
Constantes y Campos Solo Lectura
Ejemplo Hello World!
Ejemplo Args
Ejemplo Input/Output
Ejemplo String Format
Función Main
Múltiples Funciones Main
Preprocesamiento
Comentarios
Value Types
Tipos simples
Integral
Bool
Char
Floating Point
Decimal
Tipos Estructura
Tipos Enumeración
Tipos Referencia
Tipo Objeto
Tipo Clase
Interfaces
Delegados
Tipo string
Arreglos
Arreglos Multidimensionales
Arreglo de Arreglos
Arreglos de Objetos
Conversión de Arreglos
Clase Array
Interface IComparable
Interface IComparer
IComparer Como Propiedad
Expresiones Regulares
Operador as
Secuencias de Escape
Boxing
Conversiones Boxing
Conversiones Unboxing
Constructores y Destructores
Constructor Estático
Métodos
Parámetros
Valores en Parámetros in
Valores en Parámetros ref
Valores en Parámetros out
Ejemplo de Parámetros In, Ref y Out
Redefinición de Métodos (Overriding)
Ocultamiento de Métodos (Hiding)
Propiedades
Accessors
Propiedades Estáticas
Índices
Eventos
Modificadores
Modificadores de Clase
Modificadores Miembro
Modificadores de Acceso
public
protected
private
internal
Sentencias de Control
Sentencias de Selección
Sentencia if
Sentencia switch
Sentencias de Iteración (repetición)
Sentencia for
Sentencia foreach
Sentencia while
Sentencia do
Sentencias de Salto
break
continue
goto
return
Asignación Definitiva
Precedencia de Operadores
typeof
is
Conversiones
Manejo de Excepciones
Jerarquía de Excepciones
Trabajar con Excepciones
Chequeo de Overflow
Chequeo programático de Overflow
Sentencias para el Manejo de Excepciones
try - catch
try - finally
try - catch - finally
Lanzamiento de Excepciones
Relanzamiento de Excepciones
Creación de Clases Exception
Componentes
Creación de Componentes
Compilación de Componentes
Creación de una Aplicación Cliente
Compilación de Clientes
Namespaces
Envolver Clases en Namespace
Utilizar Namespace en Aplicaciones Cliente
Agregar Múltiples Clases a Un Namespace
Namespace y Ensambles
Compilación Condicional
Uso del Preprocesador
Definición de símbolos
Exclusión de código basado en símbolos
Lanzamiento de errores y advertencias
Atributo conditional
Comentarios de Documentación en XML
Elementos XML
Componentes .NET
Componentes .NET Privados
Componentes .NET Compartidos
Interoperabilidad COM
Uso de Componentes .NET en Componentes .COM
Uso de Componentes COM en Componentes .NET
Invocación de Servicios
Código No Seguro
Debugging
Seguridad
Seguridad de acceso al código
Permisos Estándar
Permisos Identidad
Seguridad basada en roles
Función ToString()
Función Equals()
Clase Hashtable
Interface IHashCodeProvider
Función ICloneable
Formato Numérico
Formato Personalizado String
Formato Estándar String
Parseo Numérico
Input/Output
Lectura y Escritura de Archivos
Serialización
Threading (Hilos)
tema
¿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:
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 operador new, 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:
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ás constructores 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 ---");
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:
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());
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 ---");
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:
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:
El CLR provee los beneficios anteriores, el compilador debe emitir los metadatos en el
managed code. Los metadatos describen 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:
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.
Ensamblaje
En el runtime .NET el mecanismo de empaquetado es el ensamble (assembly), cuando el
código es compilado por uno de los compiladores .NET, es convertido a una forma intermedia
conocida como IL.
El ensamble contiene todos los IL, metadatos y otros archivos requeridos para que un paquete
se ejecute en un paquete completo.
Cada ensamble contiene un manifiesto que enumera todos los archivos que están contenidos en
el ensamble, controla que tipos y recursos son expuestos fuera del ensamble y relaciona las
referencias de esos tipos y recursos a los archivos que contienen los tipos y recursos.
El manifiesto también lista otros ensambles que dependen de un ensamble.
Los ensambles se contienen a sí mismo, existe suficiente información en el ensamble para ser
auto-descrito.
Cuando se define un ensamble, el ensamble puede ser contenido en un solo archivo o puede ser
dividido entre varios archivos. Utilizando varios archivos podría hacer posible un escenario
donde las secciones del ensamble sean descargadas sólo como se necesiten.
Interoperabilidad de Lenguaje
Una de las metas del runtime .NET es ser un lenguaje agnóstico, permitiendo que el código sea
utilizado y escrito desde cualquier lenguaje, no sólo las clases pueden ser escritas en algún
lenguaje .NET como VB.NET y ser invocadas desde otro lenguaje .NET como C#, una clase que
fué escrita en VB.NET puede ser utilizada como una clase base escrita en C# y esa clase podría
ser utilizada desde una clase VC++ o JScript, es decir, no importaria en que clase sea escrita
una clase.
Para hacer lo anterior posible existen algunos obstaculos como las propias características del
lenguaje, ya que un lenguaje no podría soportar ciertas cosas que otro si las soporte, por
ejemplo la sobrecarga de operadores.
Para que una clase sea utilizada desde un lenguaje .NET, la clase debe adherir la Especificación
Común de Lenguaje (Common Language Specification - CLS) la cual describe que características
pueden ser visibles en la interfaz pública de la clase, por ejemplo el CLS prohibe exponer tipos
de datos sin signo, porque no todos los lenguajes pueden utilizarlos.
Atributos
El runtime .NET soporta atributos personalizables, los cuales son en cierto sentido un lugar para
colocar información descriptiva en los metadatos junto con un objeto y entonces recuper
después los datos. Los atributos proveen un mecanismo general para hacer esto y son
utilizados en exceso 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
method método
property porpiedad
event evento
field campo
param parámetro
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 el CLR.
El proceso es el siguiente:
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:
EconoJIT, realiza una conversión muy veloz del IL a managed code nativo
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.
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.
Byte Tipo
Tipo Descripción
s runtime
i 2005
------------
s o----- > Septiembre
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
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
Ejemplo Input/Output
Es posible leer datos de la consola utilizando el método ReadLine y es posible mostrarlos
utilizando el método Write o WriteLine 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();
Tamaño Valor
!
==
!=
&&
||
//, 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
Tamañ
Tipo Valor
o
Entero con
sbyte 8 bit -128 a 127
signo
Entero con
short 16 bit -32,768 a 32,767
signo
Entero con
int 32 bit -2,147,483,648 a 2,147,483,647
signo
Entero con
long 64 bit -9,223,372,036,854,775,808 a -9,223,372,036,854,775,807
signo
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
Floating Point
Tipo Valor
Nota: Si una expresión un valor es de tipo Floating Point todos los otros valores son
convertidos a tipos Floating 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 tipo int 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};
Console.WriteLine();
Console.WriteLine("Los meses del año, y su valor correspondiente
en la enumeración es:");
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 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
Propiedad Descripción
Método Descripción
Append() Agrega la representación string de un objeto
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";
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 ---");
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);
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);
\\ Diagonal invertida
\0 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 es boxed 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 type que 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:
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;}
}
//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));
class Parametros{
public static void Main(){
Param MiParam = new Param();
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;
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;
class Parametros{
public static void Main(){
Param MiParam = new Param();
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 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 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
Ciudadano.Edad = 33;
Console.WriteLine("Edad Ciudadano : " +
Ciudadano.Edad);
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 el accessors set es pasado en el nuevo valor para la propiedad en el
parámetro value.
Las operaciones que pueden realizarse con los atributos son:
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");
Modificadores de Clase
Modificadores Miembro
Modificadores de Acceso
Modificadores de Clase
Existen dos tipos de modificadores de clase:
abstract
Ejemplo:
using System;
abstract
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:
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.
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
class Caracteres{
public static void Main(){
string sNombre = "Gerardo Angeles Nava";
if(Char.IsDigit(chLetra)){
Console.WriteLine(chLetra + " es un
dígito");
}else{
EsMayuscula(chLetra);
EsMinuscula(chLetra);
}
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 clase Char, 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?
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:
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 sentencia continue
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
(x), x.y, f(x), a[x], x++, x--, new, typeof, sizeof, checked,
Primary
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 o interface.
El operador as es muy similar al operador is, pero en vez de determinar cuando un objeto es
un tipo específico o interface, as también realiza la conversión explicita a ese tipo o interface y
si no lo puede convertir el operador regresa null.
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 el runtime .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 try que
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 el factorial 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 para Exception, el
compilador le notificara que existe una excepción que atrapa todas las excepciones y esto es
porque Exception 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 como checked. 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 bloque catch, 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
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;
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;
class AplicClsPersona{
public static void Main(){
ClsPersona Empleado = new ClsPersona();
Empleado.Sueldo = 33;
Console.WriteLine("Edad : " + Empleado.Sueldo);
namespace informatique.com.mx{
public class iPersona{
private int iSueldo;
public int Sueldo{
get{return iSueldo;}
set{iSueldo = value;}
}
class iAplicClsPersona{
public static void Main(){
iPersona Empleado = new iPersona();
Empleado.Sueldo = 33;
Console.WriteLine("Edad : " + Empleado.Sueldo);
class iAplicClsPersonaAbs{
public static void Main(){
informatique.com.mx.iPersona Empleado = new
informatique.com.mx.iPersona();
Empleado.Sueldo = 33;
Console.WriteLine("Edad : " + Empleado.Sueldo);
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;
}
}
}
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 y volver 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 el CLR.
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:
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;}
}
class AplicPersona{
public static void Main(){
Persona Empleado = new Persona();
Empleado.Sueldo = 33;
Console.WriteLine("Edad : " + Empleado.Sueldo);
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 como compartido.
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 el Registry 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_ROOT\informatique.com.mx.iPersona]
@="informatique.com.mx.iPersona"
[HKEY_CLASSES_ROOT\informatique.com.mx.iPersona\CLSID]
@="{37504224-213A-3943-845A-E572758E4174}"
[HKEY_CLASSES_ROOT\CLSID\{37504224-213A-3943-845A-E572758E4174}]
@="informatique.com.mx.iPersona"
[HKEY_CLASSES_ROOT\CLSID\{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_ROOT\CLSID\{37504224-213A-3943-845A-
E572758E4174}\InprocServer32\0.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_ROOT\CLSID\{37504224-213A-3943-845A-
E572758E4174}\ProgId]
@="informatique.com.mx.iPersona"
[HKEY_CLASSES_ROOT\CLSID\{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:
Debugging
El SDK .NET incorpora dos herramientas de depuración de errores:
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 \ProgramFiles\NGWSSDK\GuiDebug
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:
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:
Permisos Estándar
Los Permisos Estándar son:
Permisos Identidad
Los Permisos Identidad son:
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);
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 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:
Console.WriteLine("0:C",
C, c Currency $123,345.90
123.8977);
Console.WriteLine("0:D7"
D, d Decimal 0012345
, 12345);
Scientific Console.WriteLine("0:E",
E, e (exponential 3.334590E+004
33345.8977);
)
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:
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 sentencia lock.
lock utiliza la clase System.Threading.Monitor y provee funcionalidad similar a la
invocación de CriticalSection en Win32.
Agradecimientos a Juan Garcia por enviar el tutorial original de informatique.com.mx