Sunteți pe pagina 1din 144

C sharp NET/Texto Completo

Contenido

 1 C sharp NET
o 1.1 El lenguaje C#
o 1.2 Tabla de Contenidos
o 1.3 Pre-requisitos necesarios para este curso
o 1.4 Autores
o 1.5 Licencia
o 1.6 Referencias
 2 Capítulo 0
 3 Una Breve Introducción a los Lenguajes de Programación
 4 Capítulo 1
 5 Introducción
o 5.1 La plataforma .NET
 5.1.1 Independencia de lenguaje
 5.1.2 Librería de clases común
 5.1.3 Multiplataforma
 5.1.4 Windows Forms, Web Forms, Web Services
 5.1.5 Estandarización
o 5.2 Un resúmen introductorio sobre el lenguaje C#
 5.2.1 C# frente a Java
 5.2.2 C# frente a C++
 5.2.3 ¿Por qué C#?
o 5.3 Instalando lo necesario para empezar
 6 Capítulo 2
 7 Primer programa
o 7.1 Primer programa ¡Hola Mundo!
o 7.2 Comentarios
o 7.3 Clases, Objetos y tipos
o 7.4 Métodos o Funciones
 7.4.1 Crear nuevos métodos o funciones
o 7.5 Aplicaciones de consola
o 7.6 Namespaces
o 7.7 La palabra clave using
o 7.8 Caracteres sensibles
 8 Capítulo 3
 9 Fundamentos del lenguaje
o 9.1 Tipos
 9.1.1 Importancia de los tipos de datos
 9.1.2 Tipos en C#
 9.1.3 Tipos básicos o internos
 9.1.4 Escogiendo qué tipo usar
 9.1.5 Enteros
 9.1.6 Tipos de coma flotante
 9.1.7 El tipo decimal
 9.1.8 El tipo bool
 9.1.9 El tipo char
 9.1.10 Tipo Cadenas
 9.1.11 Convirtiendo tipos
o 9.2 Arreglos
o 9.3 Identificadores, Variables, Constantes y Enumeraciones
 9.3.1 Identificadores
 9.3.2 Variables
 9.3.3 Constantes
 9.3.4 Enumeraciones
o 9.4 Operadores
 9.4.1 Operadores matemáticos
 9.4.2 Operadores de asignación
 9.4.3 Operadores de comparación
 9.4.4 Operadores lógicos
 9.4.4.1 AND
 9.4.4.2 OR
 9.4.4.3 NOT
 9.4.4.4 XOR
 9.4.4.5 <<
 9.4.4.6 >>
 9.4.5 Operadores lógicos de unión
 10 Capítulo 4
 11 Estructuras de control
o 11.1 Saltos incondicionales
 11.1.1 La sentencia goto
o 11.2 Saltos condicionales
 11.2.1 Instrucción if
 11.2.2 Instrucción switch
 11.2.3 Bucle for
 11.2.4 Bucle while
 11.2.5 Bucle do-while
 11.2.6 Bucle foreach
 11.2.7 Usando continue y break
 12 Capítulo 5
 13 Introducción a las clases y objetos
o 13.1 Introducción a las clases en C#
 13.1.1 Métodos
 13.1.2 Pasando valores a los métodos
 13.1.2.1 Parámetros
 13.1.2.2 Paso por valor
 13.1.2.3 Paso por referencia
 13.1.2.4 Parámetro de salida
 13.1.2.5 Arreglo de parámetros
 13.1.3 Modificadores public y static
 13.1.4 Constructores e instancias de una clase
 13.1.5 Sobrecarga de métodos
 13.1.6 La palabra reservada this
o 13.2 Propiedades e indizadores
 13.2.1 Propiedades
 13.2.2 Indexadores
 14 Capítulo 6
 15 Herencia y Polimorfismo
o 15.1 Herencia
 15.1.1 La palabra reservada base
 15.1.2 Clases Abstractas
 15.1.3 Miembros virtual
 15.1.4 Problemas propuestos
 16 Capítulo 7
 17 Sobrecarga de operadores
o 17.1 ¿Qué es la sobrecarga de operadores?
o 17.2 Sobrecargando operadores en la práctica
o 17.3 Operadores binarios
o 17.4 Operadores Unarios
 18 Capítulo 8
 19 Estructuras
o 19.1 Rendimiento
 20 Capítulo 9
 21 Interfaces
o 21.1 Definición
o 21.2 Obteniendo Referencias a la Interfaz
o 21.3 Pasar interfaces como parámetros
o 21.4 Implementación Explícita de una Interfaz
o 21.5 Jerarquías de interfaces
 22 Capítulo 10
 23 Estructuras de datos
o 23.1 Enumeraciones
o 23.2 Estructuras
 24 Capítulo 11
 25 Capítulo 12
 26 Manejo de excepciones
 27 Capítulo 13
 28 Delegación y Eventos
o 28.1 Programando con delegados
 28.1.1 Llamadas a múltiples métodos
 28.1.2 Ejemplo de uso de delegados
 29 Capítulo 14
 30 Capítulo 15
 31 Capítulo 16
 32 Capítulo 17
 33 Programando aplicaciones WEB con ASP.NET usando C#
 34 Capítulo 18
 35 Capítulo 19
 36 XML
o 36.1 Conceptos básicos de XML
o 36.2 XML en .Net
o 36.3 Escribir un archivo XML
 37 Capítulo 20
 38 Capítulo 21
 39 Capítulo 22
 40 Programando en Redes
 41 Capítulo 23
 42 Capítulo 24
 43 Capítulo 25
 44 Capítulo 26
 45 Utilizando Bases de Datos
o 45.1 Diferencias entre ADO y ADO.NET
o 45.2 ADO.NET a grandes rasgos
 46 Capítulo 27
 47 Capítulo 28
 48 Manejo de puertos con Mono y C#
o 48.1 Introducción
o 48.2 Limitaciones
 48.2.1 Código de ejemplo
o 48.3 Puntos a considerar
o 48.4 Links relacionados
 49 Capítulo 29
 50 Sockets
o 50.1 Introducción a los Sockets
o 50.2 Sincrónico y Asincrónico
o 50.3 Clase Socket
o 50.4 Servidor
o 50.5 Cliente
 51 GNU Free Documentation License
o 51.1 0. PREAMBLE
o 51.2 1. APPLICABILITY AND DEFINITIONS
o 51.3 2. VERBATIM COPYING
o 51.4 3. COPYING IN QUANTITY
o 51.5 4. MODIFICATIONS
o 51.6 5. COMBINING DOCUMENTS
o 51.7 6. COLLECTIONS OF DOCUMENTS
o 51.8 7. AGGREGATION WITH INDEPENDENT
WORKS
o 51.9 8. TRANSLATION
o 51.10 9. TERMINATION
o 51.11 10. FUTURE REVISIONS OF THIS LICENSE

C sharp NET
El lenguaje C#
Los primeros rumores de que Microsoft estaba desarrollando un nuevo
lenguaje de programación surgieron en 1998, haciendo referencia a un
lenguaje que entonces llamaban COOL y que decían era muy similar a Java.
En junio de 2000, Microsoft despejó todas las dudas liberando la
especificación de un nuevo lenguaje llamado C#. A esto le siguió rápidamente
la primera versión de prueba del entorno de desarrollo estándar (SDK) .NET,
que incluía un compilador de C#. El nuevo lenguaje estaba diseñado por
Anders Hejlsberg ( creador de Turbo Pascal y arquitecto de Delphi ), Scott
Wiltamuth y Peter Golde. Entonces describieron el lenguaje como "...simple,
moderno, orientado a objetos, de tipado seguro y con una fuerte herencia de
C/C++".

Una muestra de esta nueva tecnología es el nuevo lenguaje de programación


C#. Este nuevo lenguaje orientado a objetos con énfasis en Internet se basa
en las lecciones aprendidas de los lenguajes C, C++, Java y Visual Basic. Por
ello se trata de un lenguaje que combina todas las cualidades que se pueden
esperar de un lenguaje moderno (orientación a objetos, gestión automática de
memoria, etc.) a la vez que proporciona un gran rendimiento.
En este curso intentaremos examinar las cualidades de este lenguaje desde lo
más básico a lo más avanzado incluyendo la versión 2.0. Examinaremos paso
a paso cómo crear poderosas aplicaciones de escritorio o basadas en web.

Tabla de Contenidos

1. Capítulo 0: Breve introducción a los lenguajes de programación


2. Capítulo 1: Introducción
3. Capítulo 2: Primer programa
4. Capítulo 3: Fundamentos del lenguaje
5. Capítulo 4: Estructuras de control
6. Capítulo 5: Introducción a las clases y objetos
7. Capítulo 6: Herencia y Polimorfismo
8. Capítulo 7: Sobrecargando operadores
9. Capítulo 8: Estructuras
10. Capítulo 9: Interfaces
11. Capítulo 10: Estructuras de datos
12. Capítulo 11: Cadenas y Expresiones regulares
13. Capítulo 12: Manejo de excepciones
14. Capítulo 13: Delegación y Eventos
15. Capítulo 14: Creando Aplicaciones gráficas usando
Windows.Forms
16. Capítulo 15: Creando Aplicaciones gráficas usando Gtk# y
Qyoto(Qt4)
17. Capítulo 16: Accediendo a la información usando ADO.NET
18. Capítulo 17: Programando Aplicaciones WEB con ASP.NET
2.0
1. Lo básico de una página
19. Capítulo 18: Programando Servicios del Web
20. Capítulo 19: XML
21. Capítulo 20: Ensamblados y Versiones
22. Capítulo 21: Atributos y Reflexión
23. Capítulo 22: Programando en Redes
24. Capítulo 23: .NET y COM
25. Capítulo 24: Creando aplicaciones multimedia SDK y DirectX
26. Capítulo 25: OpenGL y Direct3D
27. Capítulo 26: Utilizando Bases de Datos
28. Capítulo 27: Solución a los problemas propuestos
29. Capítulo 28: Manejo de puertos
30. Capítulo 29: Sockets

Pre-requisitos necesarios para este curso

Lo único que necesitáis para este curso es tener un compilador de C# y un


entorno de ejecución de código gestionado. Los más utilizados son el mono,
dotgnu y Visual C# 2010 Express Edition de Microsoft.

Autores

Editores principales (si has colaborado añade tu nombre a esta lista):

 Fabian Seoane. Tutorial de MonoHispano, migración y editor.

 David Cañar - Creación del libro, Capítulo 2 y varias porciones del


capítulo 1, 3 y 4

 Javier Hernández Sánchez - Creación del capítulo 9 Interfaces

 Sebastian Sasías - Revisión, ampliación y formalización de conceptos.

 Gustavo Novaro - Varios, para más detalles ver historial.

 Perla Lizet Cruz Martinez - Diseñadora de la pagina.


Capítulo 0

Una Breve Introducción a los Lenguajes de Programación

Comencemos con lo mas elemental, cuando dos o mas personas se


comunican, para transmitirse información, expresar ideas, noticias, conceptos
y hasta contarse algún chisme, utilizan un elemento para hacerlo y a tal
elemento lo conocemos como Lenguaje. Sin ahondar en formalidades, un
lenguaje es una herramienta para comunicarse por medio de signos, que
pueden ser, escritos, sonoros, gestuales, etc. Usualmente, al lenguaje que
usamos los humanos (independientemente del idioma) se le llama Lenguaje
Natural. De esta manera, por ejemplo, un español, un mexicano y un
uruguayo se comunicarán entre sí usando su lenguaje natural (que en
particular es expresado en Español con sus variantes regionales), de forma
análoga dialogarían un australiano, un irlandés y un estadounidense
(seguramente comunicándose en Inglés con sus variantes regionales).

La importancia en si, de ésta herramienta, es el poder de comunicar, solicitar


e incluso dar instrucciones para realizar algo concreto. En el mundo de la
informática, debemos ser muy precisos en como damos esas indicaciones y
por ello se procura siempre que todo sea claro, sin confusiones,
contradicciones ni ambigüedades. El Lenguaje Natural humano, es complejo
para ser interpretado por máquinas, para ello, a lo largo de la historia, se han
creado lenguajes simples y concretos, para poder interactuar con máquinas.
La Programación, es una actividad en la cual tenemos que dar instrucciones
precisas a una máquina, para que haga exactamente lo que queremos y para
ello, se han creado y perfeccionado lenguajes, conocidos hoy como

Lenguajes de Programación.

Los actuales lenguajes de programación tienen características que pueden


asemejarse en buen grado a los lenguajes naturales como el Español, Inglés, o
cualquier otro; ya que al igual que en la comunicación oral o escrita, los
lenguajes de programación tienen un vocabulario definido, además de reglas
ortográficas, de estructura, de puntuación, es decir la sintaxis que deben seguir
y por supesto una semántica, esto es el significado que adquieren las
conformaciones basadas en las reglas antes mencionadas.

Como en los lenguajes humanos corrientes (Lenguajes Naturales), estas reglas


deben tomarse en cuenta para que las ideas que tratamos de expresar tengan
sentido. También sucede que un lenguaje de programación cambia con el
tiempo, igual que un lenguaje natural. Si tú eres un hispano-hablante sabrás
que en cada país hispanoamericano se usan palabras que en otros países no se
utilizan o las mismas palabras tienen un significado distinto entre países (esto
es llamado ambigüedad): el idioma Español ha venido evolucionando con el
tiempo y se ha adaptado a la geografía.

En este mismo sentido, el lenguaje de programación C#.NET es una


actualización técnica de otros lenguajes de programación más antiguos como
lo son el C, C++, Java, Visual Basic, Delphi, etc. El creador de este lenguaje
Anders Heljsberg, es un calificado profesional de la informática que además
ha desarrollado lenguajes y herramientas como Turbo Pascal, Delphi (de
Borland Co.) y Visual J++ (Microsoft). Por lo que C# pretende ser el
siguiente eslabón en la evolución de los lenguajes de programación mas
avanzados, buscando combinar en proporciones adecuadas: Potencia,
Sencillez y Actualidad/Tendencia.

La evolución de los ordenadores (computadores) y de los lenguajes de


programación ha sido grandísima. No hace muchos años existían personas
que tenían que hacer agujeros en tarjetas de cartulina para poder dar
instrucciones al computador: eran las famosas tarjetas perforadas.

Pocos años despúes de esos días existían personas (y hasta ahora existen
aquellos) que pueden "hablar con el ordenador", se trata de informáticos que
en su época solo disponían de elementos de muy bajo nivel para controlar el
computador, eso hasta el día de hoy es la base de las técnicas de cómputo,
independientemente de cuanto hayan aumentado las capacidades de
memorias, velocidades de microprocesadores, miniaturizaciones, etc.

La forma en que una máquina de cómputo recibe instrucciones es una manera


peculiar. No es la idea aquí ahondar sobre este tema, pero es importante
brindar una idea al respecto.

Supóngase que se cuenta con un sistema de riego, en principio asúmase algo


simple, un jardín con un dispersor de agua, que se activa o desactiva utilizando
una simple llave de encendido y apagado, como las de las luces.

De esta forma podemos decir que tenemos un sistema que interpreta o


entiende 2 instrucciones:

Llave para arriba ---> ENCENDIDO Llave hacia abajo ---> APAGADO

De esta manera el sistema se enciende o se apaga conforme alguien le da una


de estas 2 instrucciones que el mismo interpreta o "entiende".

Podemos esquematizar el referido sistema de esta forma:

[ INSTRUCCIONES]====>[ ROCESAMIENTO ]====>[ Acciones


/ Resultados ]

Para tratar de simplificar la forma de transmitir o escribir una de esas


instrucciones, podemos decir que si al bloque de procesamiento le enviamos
la instrucción "1", implica un ENCENDIDO, es decir que le estamos
pidiendo poner en marcha el sistema. Por el contrario, si le enviamos un "0",
es un APAGADO, le estamos pidiendo detener el sistema. Hemos hecho esto,
porque como antes mencionamos, las máquinas deben trabajar con
interpretaciones lo mas concreta y simplificadas posibles. Es mas fácil para
una máquina elemental, interpretar un "1", un "0" o incluso una serie de
éstos números [*].

Así que ahora tenemos algo como un lenguaje elemental para dar indicaciones,
donde tenemos dos instrucciones:
Instrucción: ENCENDIDO............. Equivale a: 1

Instrucción: APAGADO.............. Equivale a: 0

Imaginemos ahora que el sistema de riego, además de tener encendido y


apagado, tiene otra modalidad llamada DIARIO, que simplemente provoca
que el mismo se encienda una vez al día, a una hora indicada, por un lapso
determinado (ej: 90 minutos).

Así que necesitamos otra representación o equivalencia simple para indicar


esta nueva instrucción. Reformulemos el caso anterior, de la siguiente manera:

Instrucción: ENCENDIDO............. Equivale a: 01

Instrucción: APAGADO................ Equivale a: 00

Instrucción: DIARIO............. Equivale a: 10

Añadir más para ilustrar sobre el lenguaje binario (bajo nivel) y los lenguajes
de alto nivel**** (en construcción)

Ya que hemos recalcado las similitudes entre los lenguajes de programación


y los lenguajes naturales también hay que tener presente que un lenguaje de
programación es mucho más estricto que uno natural en el sentido de que no
pueden existir ambigüedades. Es muy fácil en el idioma Español tener frases
que impliquen algo que ya conocemos o que signifiquen cosas diferentes. Por
el contrario los lenguajes de programación son claros, específicos y directos,
no se admiten ambigüedades ni suposiciones.

Para poder dominar un lenguaje de programación (al igual que con los
lenguajes naturales) se requiere mucho estudio pero por sobre todo
muchísima práctica. Una persona podría leer este manual unas 500 veces pero
si no pone en práctica los conceptos ni investiga por su cuenta, jamás llegará
a dominar este maravilloso lenguaje de programación. Así que: ¡a practicar se
ha dicho!
[*] Al sistema numérico compuesto únicamente por los elementos "0" y "1"
se le llama Sistema Numérico Binario, o popularmente Sistema Binario.

Capítulo 1

Introducción

Como hemos dicho C# (C Sharp) es parte de la plataforma .NET. C# es un


lenguaje orientado a objetos simple, seguro, moderno, de alto rendimiento y
con especial énfasis en internet y sus estándares (como XML). Es también la
principal herramienta para programar en la plataforma .NET.

Tal vez os habréis preguntado ¿Qué es la plataforma .NET? ¿Porqué


Microsoft está invirtiendo tanto en esta nueva tecnología? ¿Qué es lo que es
tan novedoso? ¿Como es que con .NET se pueden producir aplicaciones
multi-plataforma? A continuación hablaremos un poco de la plataforma
.NET

La plataforma .NET

Marco de trabajo .NET


La plataforma .NET es una plataforma de desarrollo de software con especial
énfasis en el desarrollo rápido de aplicaciones, la independencia de lenguaje y
la transparencia a través de redes.

La plataforma consta de las siguientes partes:

 Un conjunto de lenguajes de programación (C#, J#, JScript, C++


gestionado, Visual Basic.NET, y otros proyectos independientes).
 Un conjunto de herramientas de desarrollo (entre ellos Monodevelop
o Visual Studio.NET de Microsoft )
 Una libreria de clases amplia y común para todos los lenguajes.
 Un sistema de ejecucion de Lenguaje Común. (CLR).
 Un conjunto de servidores .NET
 Un conjunto de servicios .NET
 Dispositivos electrónicos con soporte .NET (PDA,Celulares, etc).

Los puntos fuertes de la plataforma son:

Independencia de lenguaje

Todos los lenguajes que conformen con los estándares .NET, sin importar
cual, podrán interoperar entre sí de forma totalmente transparente, las clases
podrán ser heredadas entre unos lenguajes y otros, y se podrá disfrutar de
polimorfismo entre lenguajes. Por ejemplo, si yo tengo una clase en C#, esta
clase podrá ser heredada y utilizada en Visual Basic o JScript o cualquier
lenguaje .NET. Todo esto es posible por medio de una de las características
de .NET llamado Common Type System (CTS). También tiene la cualidad
de que se pueden incluir más lenguajes a la plataforma. En la actualidad
existen proyectos independientes de incluir PHP, Python, Ada y otros
lenguajes en la plataforma.

Librería de clases común

Más de 4000 clases, objetos y métodos incluidos en la plataforma .NET están


disponibles para todos los lenguajes.
Multiplataforma

Cuando un programa es compilado, no es compilado en un archivo ejecutable


sino en un lenguaje intermedio llamado “Lenguaje Intermedio” (IL) el cual
podrá ser ejecutado por el CLR (Common Language Runtime) en la
plataforma en que el CLR esté disponible (hasta el día de hoy Microsoft
solamente tiene un CLR para los sistemas operativos Windows, pero el
proyecto Mono (www.mono-project.com) y dotGNU (www.dotGNU.org)
han puesto a disposición un CLR para GNU/Linux, MacOS y muchas otras
plataformas).

Los sistemas operativos Windows XP o superiores incluyen el CLR


nativamente y SuSE Linux 9.3 o superior planea incorporar el CLR (Mono)
en su distribución lo que quiere decir que un programa .NET podrá ser
compilado y ejecutado en cualquiera de estas plataformas, o en cualquier
plataforma que incluya un CLR.

El CLR compilará estos archivos IL nuevamente en código de máquina en un


proceso que se conoce como JIT (justo a tiempo) el cual se ejecutará cuando
se requiera. Este proceso producirá código de máquina bien eficiente que se
reutilizará si es que hubiera código que se repitiera, haciendo que los
programas sean ejecutados muy eficientemente.

El CRL

Windows Forms, Web Forms, Web Services

La plataforma .NET incluye un conjunto de clases especial para datos y XML


que son la base de 3 tecnologías claves: Servicios Web (Web Services), Web
Forms, y Windows Forms los cuales son poderosas herramientas para la
creación de aplicaciones tanto para la plataforma como para el Web.

Estandarización

Además de los méritos técnicos, una de las razones del éxito de la plataforma
.NET ha sido por el proceso de estandarización que Microsoft ha seguido (y
que ha sorprendido a más de uno). Microsoft, en lugar de reservarse todos
los derechos sobre el lenguaje y la plataforma, ha publicado las
especificaciones del lenguaje y de la plataforma, que han sido posteriormente
revisadas y ratificadas por la Asociación Europea de Fabricantes de
Computadoras (ECMA). Esta especificación (que se puede descargar
libremente de Internet) permite la implementación del lenguaje C# y de la
plataforma .NET por terceros, incluso en entornos distintos de Windows.
Mono Hispano mantiene una traducción del estándar que describe el lenguaje
C# en http://monohispano.org/ecma/ (Enlace roto)

Un resúmen introductorio sobre el lenguaje C#

El lenguaje es muy sencillo, sigue el mismo patrón de los lenguajes de


programación modernos. Incluye un amplio soporte de estructuras,
componentes, programación orientada a objetos, manipulación de errores,
recolección de basura, etc, que es construido sobre los principios de C++ y
Java. Como sabréis, las clases son la base de los lenguajes de programación
orientados a objetos, lo cual permite extender el lenguaje a un mejor modelo
para solucionar problemas.

C# contiene las herramientas para definir nuevas clases, sus métodos y


propiedades, al igual que la sencilla habilidad para implementar
encapsulación, herencia y polimorfismo, que son los tres pilares de la
programación orientada a objetos. C# tiene un nuevo estilo de
documentación XML que se incorpora a lo largo de la aplicación, lo que
simplifica la documentación en línea de clases y métodos. C# soporta
también interfaces, una forma de estipular los servicios requeridos de una
clase. Las clases en C# pueden heredar de un padre pero puede implementar
varias interfaces.

C# también provee soporte para estructuras, un concepto el cual ha cambiado


signifivamente desde C++. Una estructura es un tipo restringido que no exige
tanto del sistema operativo como una clase. Una estructura no puede heredar
ni dar herencias de clases pero puede implementar una interfaz. C# provee
características de componentes orientados, como propiedades, eventos y
construcciones declaradas (también llamados atributos).
La programación orientada a componentes es soportada por el CLR. C#
provee soporte para acceder directamente a la memoria usando el estilo de
punteros de C++ y mucho más.

C# frente a Java

C# y Java son lenguajes similares, de sintaxis basada en C/C++, orientados


a objetos, y ambos incluyen las características más importantes de los
lenguajes modernos, como son la gestión automática de memoria y la
compilación a código intermedio. Pero por supuesto, también hay diferencias.

Una de las diferencias más importantes es que C# es mucho más cercano a


C++ en cuanto a diseño se refiere. C# toma casi todos sus operadores,
palabras reservadas y expresiones directamente de C++. También se han
mantenido algunas características que en Java se han desestimado.

Por ejemplo, la posibilidad de trabajar directamente con direcciones de


memoria. Si bien tanto Java como .NET proporcionan gestión automática de
memoria, en C# es posible usar lo que se denomina "código no seguro".

Cuando se usa código no seguro en C# es posible operar con punteros de


forma muy similar a como se haría en C/C++, pero el código que utiliza
punteros se queda marcado como no seguro y no se ejecuta en entornos en
los que no tiene permisos.

C# frente a C++

Puesto que C# se ejecuta en una máquina virtual, ésta se hace cargo de la


gestión de memoria y por lo tanto el uso de punteros es mucho menos
importante en C# que en C++. C# también es mucho más orientado a
objetos, hasta el punto de que todos los tipos usados derivan en última
instancia el tipo 'object'. Además, muchos tipos se usan de forma distinta.
Por ejemplo, en C# se comprueban los límites de los arrays antes de usarlos,
evitando así que se pueda escribir pasado el final del vector.
Al igual que Java, C# renuncia a la idea de herencia múltiple de clases presente
en C++. Sin embargo, referido a clases, C# implementa 'propiedades' del
tipo de las que existen en Visual Basic, y los métodos de las clases son
accedidos mediante '.' en lugar de '::'.

¿Por qué C#?

La plataforma .NET acepta varios lenguajes. Por ahora, C#, Visual Basic,
C++ gestionado, Nemerle, FORTRAN, Java, Python, etc. , y con capacidad
para aceptar prácticamente cualquier lenguaje. Entonces la pregunta es,
¿porqué se eligió C# en lugar de cualquier otro lenguaje?.

La razón fundamental es que C# se diseñó para la plataforma .NET y es


capaz de utilizar todo su potencial. También es cierto que es un lenguaje
"limpio" en el sentido de que al no tener que proporcionar compatibilidad
hacia atrás se ha tenido más libertad en el diseño y se ha puesto especial
hincapié en la simplicidad. Por ejemplo, en C# hay un tipo de clase y siempre
se le aplica el recolector de basura mientras que en C++ gestionado hay dos
tipos de clases, una a la que se aplica el recolector y otra a la que no.

Instalando lo necesario para empezar

Para poder empezar con nuestro curso debéis tener instalado en vuestro
ordenador los archivos básicos para poder compilar y ejecutar vuestros
programas. El conjunto de utilidades "Microsoft .NET Framework" y el
".NET Framework SDK" para Windows y el proyecto MONO o dotGNU
para Linux, MacOS, BeOS proporcionan estas herramientas. Podréis
encontrarlas en las siguientes direcciónes:

Para Windows:

http://msdn.microsoft.com/netframework/downloads/updates/default.as
px (no se encuentra la pagina

Para Linux u otras plataformas:

Proyecto Mono
http://mono-project.com/Downloads (manual que describe distintos
métodos de instalación)

Proyecto dotGNU

http://dotgnu.org/pnet-packages.html

Capítulo 2

Primer programa

Primer programa ¡Hola Mundo!

En un alarde de originalidad, empezaremos con un simple programa que


desplegará la frase “¡Hola Mundo!”. Con este programa introduciremos las
bases de nuestro estudio. Debido a que la plataforma .NET es independiente
del sistema operativo en este libro procuraremos mantenernos imparciales en
cuanto este asunto, eso sí, cuando llegue el tiempo de compilar y ejecutar
daremos ejemplos de como hacerlo tanto en Windows como en Linux (en
otras plataformas como MacOS será lo mismo que en Linux).

Si estáis utilizando un entorno de desarrollo integrado debéis abrir un nuevo


proyecto de consola en C#, escribir o copiar lo que se muestra en el ejemplo
2.1 y compilad el proyecto para ver su resultado. Si preferís programar con
un editor de textos, abrid vuestro editor de texto favorito y grabad en un
directorio conocido lo que se encuentra en el ejemplo 2.1 como Programa.cs,
abrid una ventana de terminal (en linux) o una ventana de comandos
(Ejecutar(Run) -> cmd) en Windows y compilad vuestro proyecto con el
siguiente comando:

en linux con MONO(en el directorio donde habéis grabado vuestro


programa):
mcs Programa.cs

en linux con dotGNU(en el directorio donde habéis grabado vuestro


programa):

cscc -o Programa.exe Programa.cs

en Windows (en el directorio donde habéis grabado vuestro programa):

csc Programa.cs

de esta forma obtendréis vuestro primer programa. Para ejecutarlo


simplemente escribid:

con MONO:

mono Programa.exe

con dotGNU:

ilsrun Programa.exe

O en Windows:

Programa

Ejemplo 2.1 Programa Hola Mundo

//Ejemplo 2.1 - Programa Hola Mundo

class HolaMundo
{
static void Main()
{
string var="Mundo";
System.Console.WriteLine ("Hola {0}!", var);
}
}

Analicemos paso a paso nuestro programa de ejemplo:

Comentarios

En la primera línea de nuestro programa encontramos lo siguiente:


//Ejemplo 2.1 .... Esta línea es un ejemplo de comentarios. Un comentario
es una parte en vuestro programa que será ignorado por el compilador.

Existen tres tipos de comentarios en C#. El primer tipo es comentario de un


sola línea. El segundo es comentario de varias líneas. El tercer tipo es para
crear documentación. Este último tipo de comentario lo estudiaremos en el
capítulo 13 de este libro.

Los comentarios de una sola línea pueden ser incluidos en líneas


independientes, en líneas que ya incluyen código para comentar sobre lo que
hace esa línea o para comentar una línea de código que no necesitamos.

Los comentarios de varias líneas sirven para incluir muchas líneas de texto o
código como un comentario. Tienen una marca de inicio que dice cuando
empieza el comentario y una marca que indica el final de dicho comentario.
La marca de inicio es /* y la marca para finalizar es */ Así por ejemplo
tenemos:

//Este es un ejemplo de comentarios de una línea

/*
este comentario
abarca varias lineas
*/

class HolaMundo
{
static void Main()
{
string var="Mundo";
System.Console.WriteLine ("Hola {0}!", var); //Este comentario
puede describir lo que esta función hace
//System.Console.WriteLine ("y esta linea no la vamos a desplegar");
}
}

Clases, Objetos y tipos

La esencia de la programación orientada a objetos es la creación de nuevos


tipos. Un tipo es la representación de un componente (Pensado en un
ordenador (un computador) que está compuesto de varios componentes). En
programación un componente es algo que cumple una función. Por ejemplo,
un componente puede ser un botón en un programa. En una aplicación
podemos tener varios botones del mismo tipo como por ejemplo botones de
Aceptar, Cancelar, etc. Porque son del mismo tipo estos botones tienen
propiedades y comportamientos similares.

Pueden tener propiedades como cambiar de "tamaño", "posición", etc. Las


propiedades son las mismas pero los valores almacenados en sus atributos
pueden ser diferentes. Por ejemplo, un botón puede tener tamaño 10 y otro
tamaño 6. Podemos decir entonces que tenemos varias instancias del mismo
componente (o varios botones del mismo tipo) con diferentes valores en sus
atributos.

La programación orientada a objetos es esto. Crear componentes que puedan


ser reusados. Así no tenemos que programar varios componentes que cumplan
funciones similares sino solo uno que podrá llevar diferentes atributos y que
podrá ser reproducido (o instanciado) tantas veces como lo necesitemos.

Como en varios lenguajes de programación orientado a objetos, en C# los


tipos o componentes son definidos por una clase (class en inglés). Las
diferentes reproducciones o instancias de un componente del mismo tipo se
conoce como objetos. Posteriormente en nuestro estudio veremos que en C#
hay más tipos además de las clases. Aprenderemos sobre enums, structs y
delegates. Pero por el momento nos dedicaremos a las clases.
Como hemos visto en nuestro primer programa (Ejemplo 2.1), empezamos
nuestro programa declarando el tipo de componente que vamos a utilizar (un
componente de tipo class) y su nombre HolaMundo. Para declarar nuestro
componente como una clase, primero introducimos la palabra clave class, y
después el nombre de nuestro componente, después de lo cual declaramos las
funciones que va a cumplir su comportamiento y sus propiedades. Todas las
funciones, comportamiento y propiedades de nuestro componente (en este
caso nuestra clase) deberán estar encerrados entre llaves { }. Las llaves
delimitan el inicio y el final de dicho componente.

Métodos o Funciones

Los métodos o funciones son trozos de código que realizan una acción, esto
es, toman unos argumentos y devuelven un valor. En C#, las funciones deben
ser declaradas dentro de un objeto, normalmente dentro de una clase.

Las funciones normalmente llevan nombre que definen su función. Por


ejemplo, la función WriteLine() de la clase Console como debéis suponer
"Escribe una Línea en la consola". De forma similar se pueden declarar clases
a las que se le añaden un número ilimitado de métodos. En nuestro ejemplo
2.1 la única función que hemos definido lleva el nombre de Main() la cual es
una función especial que indica la entrada principal de ejecución de un
programa. Cada programa en C# debe tener una función Main().

Crear nuevos métodos o funciones

Para declarar un método o función utilizamos el siguiente formato:

[entorno] tipo_a_retornar Nombre_de_la_Función ([tipo Argumento1,


tipo Argumento2,...])
{
//líneas de código
}
las palabras dentro de corchetes [] son partes opcionales
de acuerdo con nuestro ejemplo 2.1, la función Main() cumple con el formato
establecido:

static void Main()


{
//líneas de código
}

 entorno: static
 tipo_a_retornar: void
 Nombre_de_la_Función: Main
 Argumentos: ninguno

En nuestro ejemplo la función Main() tiene como entorno la palabra clave


static y como tipo_a_retornar la palabra clave void. Cuando la función no
retorna ningún tipo, utilizamos la palabra void. Más adelante veremos más
tipos además de void. La palabra static también la veremos más adelante en
nuestro estudio, por el momento debéis confiar en el ejemplo y utilizarla aún
sin saber lo que hace.

Podemos añadir a nuestro componente o clase un ilimitado número de


funciones. Por ejemplo, para añadir más funciones a nuestro primer ejemplo
procederemos a crear una función que suma dos valores. Como ya habéis
aprendido en esta sección, crearemos nuestra función de la forma establecida.
Después del final de la función Main crearemos una función con entorno
static que retorne el tipo int, que se llame Suma y que acepte dos argumentos
de tipo int. Para hacerla funcionar vamos a llamarla desde nuestra función
principal:
Ejemplo 2.2 - Añadiendo funciones a nuestro programa

//Ejemplo 2.2 - Programa Hola Mundo con C# más adición

namespace Programa1
{
class HolaMundo
{
static void Main()
{
string var="Mundo";
System.Console.WriteLine ("Hola {0}!", var);
int num1 = 1;
int num2 = 3;
int resultado = Suma (num1, num2);
System.Console.WriteLine ("{0}+{1} = {2}", num1, num2,
resultado);
}

static int Suma(int valor1, int valor2)


{
return (valor1+valor2);
}
}
}

probad vuestro programa, compiladlo y ejecutadlo. El resultado será el


siguiente:

Hola Mundo!
1+3=4

Ésta es una pequeña introducción a funciones, más adelante estudiaremos más


detalladamente como crear y utilizar funciones.

Aplicaciones de consola
Las aplicaciones de consola no poseen una interfaz gráfica, no tienen botones
o ventanas, poseen una interfaz basada simplemente en texto. El ejemplo que
hemos realizado hasta ahora es una aplicación de consola que despliega texto
en la pantalla, para ello hemos utilizado la función WriteLine.

Como habíamos visto en la introducción, la plataforma .NET posee más de


4000 componentes cada cual con diferentes funciones internas. En el caso de
nuestro programa hemos usado la función WriteLine que se encuentra dentro
del componente Console.

Para poder hacer uso de funciones estáticas que se encuentran en otros


componentes, en C# como en la mayoría de lenguajes de programación
orientados a objetos, debemos especificar el nombre del componente en
primer lugar seguido por un punto y a continuación en el nombre de la
función. Es por esto que utilizamos la frase Console.WriteLine.

Dentro del componente Console la plataforma .NET tiene disponibles


muchísimas otras funciones que nos ayudan a diseñar programas de consola.
El lenguaje C# esta orientado con el paradigma de objetos y hereda muchos
elementos de C++.

Namespaces

Quizá algunos de vosotros os habréis preguntado ¿qué significa la palabra


System que está al inicio de cada programa?. Pues bien, System en este caso
representa un Espacio de nombres (namespace en inglés).

Los espacios de nombres (namespaces) se crearon principalmente para dar


más organización a los componentes. La plataforma .NET tiene
incorporados muchísimos componentes y sería una tarea imposible tratar de
memorizar todos los nombres de ellos para no repetirlos. Tarde o temprano
querréis crear un componente y no sabréis si el nombre que queréis darle ya
existe o no. Para evitarnos este problema y para poder distinguir a dónde
pertenecen ciertos componentes se han creado los espacios de nombres.

Pongamos un ejemplo para comprender este concepto. Supongamos que


deseamos crear varios componentes para una institución educativa que se
compone de educación primaria y educación secundaria. ¿Cómo podríamos
crear un componente que se llame Presidente si existen 2 presidentes que
tienen funciones distintas uno para la sección primaria y otro para la sección
secundaria? En este caso podríamos aplicar un espacio de nombres para poder
crear los componentes Presidente que cumplen distintas funciones.
Podríamos crear el espacio de nombres Primaria y dentro de éste el
componente Presidente.

De igual forma el espacio de nombres Secundaria y dentro de éste el


componente Presidente cada uno componentes podrá tener definiciones
distintas. Para acceder a las funciones de los componentes Presidente
podríamos usar:

Primaria.Presidente.nombre_de_la_función();

Y
Secundaria.Presidente.otra_función();

Cada una de las dos definiciones de Presidente son independientes, no tienen


relación entre sí ya que pertenecen a dos espacios de nombre distintos.

De ésta forma podremos crear componentes y funciones con el nombre que


deseemos siempre y cuando especifiquemos a qué espacio de nombres
pertenecen.

De acuerdo con la línea de código estudiada: System.Console.WriteLine


("Hola {0}!", var); existe una función llamada WriteLine dentro del
componente Console dentro del nombre de espacio System. Cualquier otro
componente llamado Console dentro de otro espacio de nombres, es un
componente diferente con funciones diferentes que quizás no posea la
función WriteLine.

Algo importante que debemos notar es que los espacios de nombres pueden
tener sub-espacios de nombres y estos a su vez sub-espacios de nombres. El
objetivo de esto, como lo hemos dicho, es mantener una organización de los
componentes. Los espacios de nombres, componentes y métodos se accederán
de la misma forma como lo hemos visto a través de un punto.
La palabra clave using

En ciertos proyectos tendremos que usar cierto espacio de nombres muchas


veces. Supongamos que estamos implementando un programa de consola y
tenemos que usar el componente Console repetidamente. Una forma de
ahorrarnos escribir System varias veces es especificar el espacio de nombres
que vamos a usar al inicio de nuestro programa con la palabra clave using.
Por ejemplo en nuestro ejemplo 2.2 si añadimos la línea de código using
System; al inicio de nuestro programa, podemos llamar al componente
Console sin escribir System al inicio:

Ejemplo 2.2 Modificado - La palabra clave using

//Ejemplo 2.2 Modificado - La palabra clave ''using''

using System;

namespace Programa1
{
class HolaMundo
{
static void Main()
{
string var="Mundo";
Console.WriteLine ("Hola {0}!", var);
int num1 = 1;
int num2 = 3;
int resultado = Suma (num1, num2);
Console.WriteLine ("{0}+{1} = {2}", num1, num2, resultado);
}
static int Suma(int valor1, int valor2)
{
return valor1+valor2;
}
}
}
Comparando con nuestro ejemplo 2.2 original se ve en el ejemplo que ya no
es necesario poner la palabra system.Console.WriteLine, quedando
Console.WriteLine

Algo importante para tener en cuenta es que la palabra clave using no puede
ser utilizada para ahorrarse el escribir el nombre de la clase. Por ejemplo la
línea de código using System.Console es inválida y producirá errores de
compilación.

Caracteres sensibles

C# como todos los lenguajes de programación derivados de C hace diferencia


entre caracteres en mayúscula y caracteres en minúscula. Esto quiere decir que
las palabras Using y using son distintas y por lo tanto no cumplen la misma
función. Debido a esto debemos tener mucho cuidado cuando escribimos
nuestros programas.

system.console.writeLine es diferente a system.Console.WriteLine y es


diferente a System.Console.WriteLine que es el nombre de espacio,
componente y método que C# incorpora para desplegar texto en la consola.

Finalizada esta pequeña introducción a nuestro primer programa en C#,


pasamos al capítulo 3 en donde examinaremos mas profundamente los
fundamentos de C#.
Capítulo 3

Fundamentos del lenguaje

En el capítulo 2 hemos introducido nuestro primer programa en C#, un


programa sencillo el cual incorpora muchos temas que hemos cubierto
básicamente y solamente en parte. En esta sección del libro procuraremos ver
más a fondo las partes básicas de C#. Nos internaremos más a fondo en la
sintaxis y la estructura de C#.

En este capítulo cubriremos lo que son los tipos. Hablaremos de tipos básicos
o internos y de cómo crear nuevos tipos. También hablaremos en general de
la manipulación de datos. Hablaremos sobre condicionales, operadores
matemáticos y varios otros temas relacionados. Empecemos entonces nuestro
estudio con lo que son tipos.

Tipos

Importancia de los tipos de datos

Los tipos son la base de cualquier programa. Un tipo no es más que un


espacio en el que se almacena una información, ya sean números, palabras o
tu fecha de nacimiento.

Tipos en C#

C# es un lenguaje de tipeado seguro (o fuertemente tipado) lo cual quiere


decir que el programador debe definir a que tipo pertenece cada pedazo de
información o cada objeto que se crea. De esta forma podemos crear objetos
de tipo número entero, de tipo cadenas de texto, de tipo ventana, de tipo
botones, entre otros. Haciendo esto, C# nos ayudará a mantener nuestro
código seguro en donde cada tipo cumple con su función.
En todas las operaciones el compilador comprueba los tipos para ver su
compatibilidad. Las operaciones no válidas no se compilan. De esta forma se
evitan muchos errores y se consigue una mayor fiabilidad. Esto también
permite a C# anticipar de antemano la cantidad de recursos del sistema que
nuestro programa utilizará haciendo nuestro código seguro y eficiente.

'Los tipos en C# al igual que C++ y Java se clasifican en dos secciones: Tipos
básicos o internos y tipos creados por el usuario. Los tipos básicos no son
más que alias para tipos predefinidos en la librería base de la plataforma
.NET. Así, el tipo número entero (que se representa con la palabra clave int),
no es más que una forma rápida de escribir System.Int32.

Dentro de estas dos secciones los tipos del lenguaje C# también son divididos
en dos grandes categorías: tipos por valor y tipos por referencia. Existe una
tercera categoría de tipos, disponible solo cuando se usa código no seguro:
los punteros, que se discutirán más adelante cuando hablemos de los objetos
COM.

Los tipos por valor difieren de los tipos por referencia en que las variables de
los tipos por valor contienen directamente su valor, mientras que las variables
de los tipos por referencia almacenan la dirección donde se encuentran los
objetos, es por eso que se las llaman referencias. Más adelante describiremos
como funcionan cada una de estas categorías.

Tipos básicos o internos

Los tipos básicos como hemos dicho son espacios predefinidos y


categorizados donde se almacena información. En C# tenemos los siguientes
tipos internos:
Tabla 3.1 - Tipos básicos

Nombre para la
Tipo Con Bytes
plataforma Valores que soporta
C# signo? utilizados
.NET
true o false (verdadero o falso en
bool System.Boolean No 1
inglés)
byte System.Byte No 1 0 hasta 255
sbyte System.SByte Si 1 -128 hasta 127
short System.Int16 Si 2 -32.768 hasta 32.767
ushort System.Uint16 No 2 0 hasta 65535
-2.147.483.648 hasta
int System.Int32 Si 4
2.147.483.647
uint System.Uint32 No 4 0 hasta 4.394.967.395
-9.223.372.036.854.775.808
long System.Int64 Si 8 hasta
9.223.372.036.854.775.807
0 hasta
ulong System.Uint64 No 8
18446744073709551615
Approximadamente ±1.5E-45
float System.Single Si 4 hasta ±3.4E38 con 7 cifras
significativas
Approximadamente ±5.0E-324
double System.Double Si 8 hasta ±1.7E308 con 7 cifras
significativas
Approximadamente ±1.0E-28
decimal System.Decimal Si 12 hasta ±7.9E28 con 28 ó 29
cifras significativas
Cualquier carácter Unicode (16
char System.Char 2
bits)
C# tiene una ventaja y característica especial sobre los demás lenguajes de
programación modernos y es que cada vez que se crea un objeto de un tipo
básico, éstos son mapeados internamente a un tipo primitivo de la plataforma
.NET el cual es parte del CLS (Especificación común del lenguaje) lo cual
nos permite acceder y hacer uso de estos desde cualquier lenguaje de la
plataforma .NET. Es decir si es que creamos un objeto de tipo int (entero)
en C#, ese objeto podrá ser usado como tal dentro de J#, JScript, Visual
Basic .NET y cualquier otro lenguaje que conforme los requisitos de .NET.

Escogiendo qué tipo usar

A la hora de programar deberéis decidir qué tipo de variables querréis usar.


Generalmente esta decisión se basa en el tipo de información que vayáis a usar
y en el tamaño de la información.

Por ejemplo en nuestro ejemplo 2.2 del capítulo anterior necesitábamos hacer
la suma de dos valores numéricos por lo que usamos dos tipos básicos de
número entero (usando la palabra clave int) los cuales de acuerdo con nuestra
tabla 3.1 son números enteros (no pueden llevar valores decimales) y podrán
aceptar valores entre -2,147,483,648 y 2,147,483,647 lo cual es más que
suficiente para nuestro ejemplo de añadir dos números.

En el caso de que necesitáramos hacer uso de números reales (los cuales


poseen una parte entera y una parte decimal como el número 10.22)
podremos hacer uso del tipo float, double y decimal de acuerdo con el tamaño
del número que necesitemos y así cada uno de los tipos tiene su uso y
capacidad de acuerdo con la tabla 3.1.

A continuación explicaremos brevemente los tipos más usados en C#:

Enteros

Los tipos que sirven para almacenar números enteros son: byte, sbyte. short,
ushort, int, uint, long y ulong. Como se aprecia en la tabla, C# define
versiones con y sin signo para tipos con los mismos bytes utilizados. Cada
tipo se distingue por la capacidad de almacenaje.

Probablemente el tipo más utilizado es el int, pues se utiliza para controlar


matrices, indexar arreglos (arrays) además de las operaciones normales con
enteros. Además, se trata de un entero de tamaño medio: más pequeño que
long y ulong, pero más grande que byte, sbyte, short y ushort.

El siguiente ejemplo muestra la declaración y uso de algunos tipos enteros


calculando el número de segundos en una hora, día y en un año.

Ejemplo 3.1 - utilizando tipos enteros (int)

// Ejemplo 3.1 - utilizando tipos enteros (int)


using System;
class Enteros{
public static void Main()
{
int minuto = 60; //segundos por minuto
int hora = minuto*60;
int día = hora*24;
long anio = día*365;
Console.WriteLine("Segundos en un día: {0}", día);
Console.WriteLine("Segundos en un año: {0}", anio);
}
}

De nuevo hemos usado el método Console.WriteLine para imprimir los


resultados por la consola. El identificador {0} dentro de la cadena de texto
indica que se sustituye {0} por el primer argumento. si hubiera más de un
argumento, se seguiría con {1}, y así sucesivamente. Por ejemplo, las dos
líneas que utilizan Console.WriteLine se pueden simplificar así:

Console.WriteLine("En un día: {0}; en un año: {1}", día, anio );


Tipos de coma flotante

Los tipos de coma flotante sirven para representar a números con parte
fraccionaria. La representación por supuesto puede no ser exacta, bien por
errores de la máquina, bien porque el número de decimales que se puede alojar
es finito.

Existen tres clases de tipos de punto flotante : float, double y decimal. De los
dos, el más usado es double, pues es el valor que devuelven la mayoría de las
funciones matemáticas de la librería base.

El siguiente ejemplo calcula la raíz cuadrada y el logaritmo de dos:

Ejemplo 3.2 - utilizando tipos flotantes

// Ejemplo 3.2 - utilizando tipos flotantes


using System;

class Flotante{
public static void Main()
{
int a = 2;
double log2 = Math.Log(a);
double raiz2 = Math.Sqrt(a);
Console.WriteLine("El logaritmo de dos es {0}", log2 );
Console.WriteLine("La raiz de dos es {0}", raiz2 );
}
}

y la salida será la siguiente:

El logaritmo de dos es 0.693147180559945


La raíz de dos es 1.4142135623731

si intentamos cambiar el tipo de log2 a otro de menos precisión, como float


o int, el compilador protestará. Esto se debe, como hemos dicho a que el valor
devuelto por Math.Log() es de tipo double y si se quiere convertir a float,
pues se perderán datos. Lo mismo ocurre con la mayoría de los miembros de
la clase Math, como Math.Sin(), Math.Tan(), etc.

El tipo decimal

El tipo decimal es un tipo "nuevo" en el sentido de que no tiene equivalente


en C/C++. Es muy parecido a los tipo de coma flotante float y double.

En la aritmética de los tipos de coma flotante ordinarios, se pueden producir


leves errores de redondeo. El tipo decimal elimina estos errores y puede
representar correctamente hasta 28 lugares decimales. Esta capacidad para
representar valores decimales sin errores de redondeo lo hace especialmente
eficaz para cálculos monetarios.

El tipo bool

El tipo bool sirve para expresar los valores verdadero/falso, que en C# se


muestran con las palabras reservadas true y false.

En C#, por ejemplo, una instrucción de condición solo puede estar


gobernada por un valor bool, no como en C/C++, que lo puede estar
también por un entero. De esta forma se ayuda a eliminar el error tan
frecuente en programadores de C/C++ cuando usa "=" en lugar de "==".
En definitiva, la inclusión del tipo bool en el lenguaje ayuda a la claridad del
código y evita algunos errores muy comunes.

El siguiente ejemplo, muestra algunos usos del tipo bool:

Ejemplo 3.3 - utilizando tipos de decisión bool

// Ejemplo 3.1 - utilizando tipos de decisión bool


using System;
class Booleano{
public static void Main()
{
bool b;
b = true;
Console.WriteLine("b es {0}", b);
if(b)
{
Console.WriteLine("esto saldrá");
}
b = false;
if(b)
{
Console.WriteLine("esto no saldrá");
}
Console.WriteLine("2==2 es {0}", 2==2);
}
}

En la última línea se muestra que el operador "==" también devuelve un


valor booleano. El resultado debería ser el siguiente:

b es True
esto saldrá
2==2 es True

El tipo char

El tipo char permite almacenar un carácter en formato simple, unicode de 16


bits o caracteres de escape. Usando el formato unicode nos garantiza que los
acentos se ven de forma adecuada y además permite la representación de otros
alfabetos, como el japonés, griego, cirílico, etc. Para introducir un carácter se
utilizan comillas simples, de forma que declarar un carácter sigue la estructura

char letra1 = 'a'; //formato simple


char letra2 = '\u0041'; //formato Unicode que representa la letra A
char letra3 = '\n'; //formato carácter de escape
La siguiente lista contiene los caracteres de escape comunes y su significado:

\' apostrofe
\" Comillas
\\ Backslash
\0 Null (nulo)
\a Alerta
\b Retroceso
\f Form feed
\n Línea nueva
\r Retorno del carro
\t Tabulación Horizontal
\v Tabulación Vertical

Tipo Cadenas

Los tipos cadena (palabra clave string) son tipos que almacenan un grupo de
caracteres. En C# los tipos cadena se crean con la palabra clave string seguido
por el nombre de la variable que deseamos instanciar. Para asignar un valor a
este tipo debemos hacerlo entre comillas de la siguiente forma:

string miCadena = "Esta es una cadena de caracteres";

Debido a que el tipo cadena (string) es uno de los tipos más usados en C#,
lo estudiaremos detalladamente más adelante.

Convirtiendo tipos

En nuestros programas muchas veces necesitaremos cambiar de tipo a los


objetos que hayamos creado. Esto lo podremos hacer implícitamente o
explícitamente. Una conversión de tipos implícita sucede automáticamente,
es decir el compilador se hará cargo de esto. Una conversión explicita en
cambio se llevará a cabo únicamente cuando nosotros lo especifiquemos. Hay
que tomar en cuenta que no siempre podremos hacer una conversión de un
tipo hacia otro.
Como regla general las conversiones implícitas se llevan a cabo cuando se
desea cambiar un tipo de menor capacidad hacia un tipo de mayor capacidad
de la misma especie. Por ejemplo si deseamos crear 2 tipos enteros (misma
clase) el uno que lleve el tipo short (menor capacidad) y el otro que lleve el
tipo int (mayor capacidad) una conversión implícita de short a int se lleva a
cabo en el siguiente ejemplo:

short corto = 3;
int entero = corto; //compilará sin ningún problema

aquí sucede una conversión implícita, el valor de la variable corto (en este
caso 3) que es de tipo short es asignado a la variable de tipo int sin que el
compilador nos de ningún problema ya que hará una conversión de short a
int implícitamente por nosotros debido a la regla anteriormente citada.

En el caso que queramos hacer de forma inversa, es decir asignar un valor int
a una variable short, estaríamos violando la regla de asignar un tipo de menor
capacidad a una variable de tipo de mayor capacidad aunque sean de la misma
clase (enteros). Así el siguiente ejemplo no compilará dándonos un error:

int entero = 300;


short corto = entero; //nos dará un error de compilación

En estos casos es cuando podremos hacer una conversión explícita. Debido a


que la información almacenada en la variable entero de tipo int está también
en el rango de capacidad del tipo short y los dos tipos son de la misma clase
(enteros) podremos hacer una conversión explicita designando entre
paréntesis a que tipo queremos convertir de la siguiente manera:

int entero = 300;


short corto = (short) entero; //convertirá la variable entero para que sea del
tipo short

En el ejemplo anterior, el compilador no nos dará ningún problema. Cada


uno de los tipos básicos citados a continuación soportará una conversión
implícita o explícita como se lo expresa en la siguiente tabla:
Conversión Implícita (I) Conversión Explícita (E)
Desde/haci byt sbyt shor ushor in uin lon ulon floa doubl decima
a e e t t t t g g t e l
byte I E I I I I I I I I I
sbyte E I I E I E I E I I I
short E E I E I E I E I I I
ushort E E E I I I I I I I I
int E E E E I E I E I I I
uint E E E E E I I I I I I
long E E E E E E I E I I I
ulong E E E E E E E I I I I
float E E E E E E E E I I E
double E E E E E E E E E I E
decimal E E E E E E E E E E I

Arreglos

En C# se pueden construir arreglos de prácticamente cualquier tipo de dato.


Los arreglos, también llamados vectores o arrays, no son más que una sucesión
de datos del mismo tipo. Por ejemplo, el concepto matemático de vector es
una sucesión de números y por lo tanto es un arreglo unidimensional. Así,
podemos construir arreglos de objetos, de cadenas de texto, y, por supuesto,
arreglos de enteros:

using System;

class Arreglo{
public static void Main()
{
int[] arr = new int[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
Console.WriteLine( arr[1] );
}
}

Para crear un arreglo debemos especificar de qué tipo deseamos crear el


arreglo seguido por corchetes [ ] que es el distintivo del arreglo (en nuestro
ejemplo usamos int[]), seguido por la palabra clave new y el tipo y la cantidad
de parámetros que tendrá nuestro arreglo.

En el ejemplo anterior, por ejemplo, se creó un arreglo arr unidimensional


con capacidad para 3 enteros (especificado por new int[3]), y luego se le
asignó a cada parámetro un entero distinto (nótese que se comienza a contar
a partir de 0 y el número del parámetro se encuentran entre corchetes).

Existe una forma más corta para declarar el arreglo y asignarle los valores:

int[] arr = {1,2,3}; //es exactamente lo mismo que el ejemplo anterior

También se pueden crear arreglos bidimensionales (y de la misma forma para


más dimensiones). En ese caso la sintaxis para declarar un arreglo
bidimensional de enteros será

int[,] arr; //declaración de arreglos bidimensionales en C#

en contraposición a C/C++, en el que se declararía como

int[][] arr; //declaración de arreglos bidimensionales en C/C++

De esta forma, un arreglo bidimensional se declararía y utilizaría de la


siguiente forma:

using System;
class Arreglo2{
public static void Main()
{
int[,] arr = new int[2,2];
arr[0,0] = 1;
arr[1,0] = 2;
arr[0,1] = 3;
arr[1,1] = 4;
Console.WriteLine("El valor que posee la variable arr[1,1] es {0}",
arr[1,1] );
}
}

el resultado será:

El valor que posee la variable arr[1,1] es 4

igual que el ejemplo anterior, podemos declarar todo el arreglo de la siguiente


forma:

int[,] arr = {{1,2},{3,4}};

Hablaremos más sobre arreglos más adelante

Identificadores, Variables, Constantes y Enumeraciones

Identificadores

En una aplicación siempre se deben crear variables, constantes, métodos,


objetos, etc. Para poder crearlos debemos asignar nombres o identificadores.
Estos identificadores deben seguir ciertas reglas:

1. Un identificador DEBE empezar con una letra o un signo _


2. Un identificador NO puede tener espacios en blanco
3. Un identificador NO puede llevar el mismo nombre que una palabra
clave
Después de que el identificador empiece con una letra o un símbolo _, el
identificador puede tener cualquier cantidad de letras, líneas o números.

Ejemplo:

EsteIdentificadorEsValido
_este_también
esteEsOtro1
esteEsOtro2

como ejemplos inválidos tenemos:

Esto es invalido
123_Otro_inválido
int

En los ejemplos que no son válidos, el primero contiene espacios, el segundo


empieza con números y el tercero es una palabra clave lo cual no es permitido.

Nota: Es recomendado que las variables siempre empiecen con minúsculas y


que sigan el patrón llamado Camello es decir que el nombre de la variable que
tenga varias palabras debe ser formado de la siguiente manera: la primera
palabra empezará con minúsculas pero la segunda, tercera, etc palabras estarán
unidas a la primera y tendrán su primera letra en mayúsculas ejemplo:
miVariable. También se ha recomendado que el nombre de Métodos, Clases
y demás nombres que necesitamos especificar deberán llevar el mismo
formato anterior con la excepción de que la Primera letra deberá ser
mayúscula.

Variables

Una variable es el nombre que se le da al espacio donde se almacena la


información de los tipos. Las variables pueden llevar cualquier nombre que
deseemos, eso sí no se podrá hacer uso de palabras claves de C#. En los
ejemplos anteriores hemos usado varias variables con diferentes tipos y
diferentes valores.

Para crear una variable debemos especificar a qué tipo pertenece antes del
nombre que le vamos a dar. Por ejemplo si deseamos crear una variable que
se llame var y que sea del tipo entero (int) procederemos de la siguiente
manera:

int var;

Una vez creada la variable podemos almacenar la información que deseamos.


Para hacerlo utilizamos el símbolo = después del nombre de la variable.

var = 10;

Hay que tener presente que la variable como su nombre lo indica podrá tomar
otros valores. Por ejemplo si deseamos que nuestra variable cambie de valor a
5 hacemos lo que habíamos hecho en el ejemplo anterior pero con el nuevo
valor:

var = 5;

De ahora en adelante la variable var pasa a tener el valor de 5.

Para simplificar el proceso de creación y asignación de valor de una variable


podemos especificar estos dos procesos en una misma línea.

int var = 10;

La primera vez que una variable recibe un valor se llama inicialización de la


variable. En C# todas las variables que van a ser utilizadas deben ser
inicializadas para que vuestro programa pueda funcionar.

En caso anterior hemos creado la variable var e inicializado la variable con el


valor 10. Hay que tener cuidado que la creación de una variable se la hace 1
sola vez y la asignación de diferentes valores se puede hacer cuantas veces
queramos.
Constantes

Las constantes como su nombre lo indica son variables cuyo valor no puede
ser alterado. Éstas se utilizan para definir valores que no cambian con el
tiempo. Por ejemplo podemos definir una constante para especificar cuantos
segundos hay en una hora de la siguiente forma:

const int segPorHora = 3600;

Esta constante no podrá ser cambiada a lo largo de nuestro programa. En el


caso de que queramos asignarle otro valor, el compilador nos dará un error.

Las constantes deben ser inicializadas en el momento de su creación.

Enumeraciones

Supongamos que estamos diseñando un juego y necesitamos crear una


variable para saber cuál es el estado del tanque de combustible de nuestro
automóvil. Suponiendo que tenemos 4 niveles en el tanque: lleno, medio, bajo
y crítico. ¿Qué tipo de variable podríamos usar para especificar estos estados?
Una forma de hacerlo podría ser si especificamos una variable de tipo int
(entero) que tome los valores de 1 para lleno, 2 para medio, 3 para bajo y 4
para crítico. Esta alternativa funcionaría pero a la larga nos olvidaremos qué
número representaba qué. Una forma muy elegante de solucionar este
problema es utilizando el tipo enum o enumeraciones. Veamos el siguiente
ejemplo para comprender este concepto:

Ejemplo 3.1 - Control del estado de combustible

using System;
namespace Autos
{
class Control
{
enum tanque
{
lleno,
medio,
bajo,
critico,
}

static void Main()


{
tanque auto1 = tanque.lleno;
RevisarEstadoTanque(auto1);

auto1 = tanque.critico;
RevisarEstadoTanque(auto1);
}

static void RevisarEstadoTanque(tanque auto)


{
if (auto==tanque.lleno)
Console.WriteLine ("¡El tanque está lleno!");
if (auto==tanque.medio)
Console.WriteLine ("El tanque está por la mitad");
if (auto==tanque.bajo)
Console.WriteLine ("¡El tanque está casi vacío!");
if (auto==tanque.critico)
Console.WriteLine ("¡Alerta! tu auto se quedó sin combustible");
}

Este programa sencillo crea una enumeración llamada tanque y dentro de ella
crea 4 constantes: lleno, medio, bajo, y critico. Dentro de nuestro programa
creamos la variable de tipo tanque llamada auto1 la cual podrá tomar los
valores especificados dentro de la enumeración. Cuando asignamos a la
variable auto1 el valor de tanque.lleno y revisamos el estado del tanque
llamando a la función RevisarEstadoTanque, podremos comprobar cuál es el
estado actual del tanque de combustible. Ésta es una forma muy descriptiva
de cómo crear variables que cambien de estado, una solución elegante y
sencilla a nuestro problema.

En realidad las constantes dentro de las enumeraciones tienen valores enteros


asignados. Estos valores pueden ser inicializados con distintos valores que
nosotros deseemos e incluso podemos especificar qué tipo de entero
queremos usar. En nuestro ejemplo anterior los valores de la enumeración son
valores enteros del tipo int el primer elemento dentro de la enumeración tiene
asignado el valor de 0, el siguiente el valor 1 y así sucesivamente. Esto sucede
cuando no especificamos a qué tipo queremos inicializar nuestra enumeración
y tampoco asignamos valores a las constantes. En el siguiente ejemplo
podemos ver cómo especificar otro tipo de constantes y otros valores.

Supongamos que queremos especificar cuantos segundos hay en un minuto,


cuantos segundos hay en una hora y cuantos segundos hay en 24 horas. Con
enumeraciones lo podemos hacer de la siguiente manera:

Ejemplo 3.2 - Enumeraciones

using System;
namespace Ejemplos
{
class Enumeraciones
{
enum segundos :uint
{
minuto = 60,
hora = 3600,
dia = 86400,
}
static void Main()
{
Console.WriteLine("Existen {0} segundos en 1 minuto, {1} segundos
en 1 hora y {2} segundos en 24 horas",(uint)segundos.minuto,
(uint)segundos.hora, (uint)segundos.dia);
}
}
}

El resultado es el siguiente:

Existen 60 segundos en 1 minuto, 3600 segundos en 1 hora y 86400


segundos en 24 horas

El ejemplo anterior nos muestra otra forma de usar una enumeración. Hay
que tener en cuenta que el tipo que va a tener la enumeración se encuentra
después del nombre de la enumeración precedido por dos puntos. De esa
forma podremos especificar de qué tipo son. Como habíamos dicho
anteriormente se podrá utilizar cualquier tipo de la clase enteros como byte,
sbyte, short, ushort, int, uint, long o ulong. En el caso de que no se especifique
a qué tipo pertenece el compilador le dará el tipo int. También se debe tomar
en cuenta que los valores de las constantes están asignados con el signo = y
están separadas por comas.

Como habéis visto la forma de acceder al valor numérico de las enumeraciones


es especificando entre parentesis a qué tipo pertenecen, en este caso (uint).
Después de lo cual especificamos el nombre de la enumeración seguido por
un punto que separa al nombre de la constante. En el caso de que deseemos
desplegar sólo el nombre de la constante y no su valor, se debe omitir el
nombre del tipo como: segundos.hora sin (uint) al inicio.

En el caso de que solamente especifiquemos algunos valores de las constantes,


el compilador asignará el siguiente valor a la siguiente constante. Así por
ejemplo:

enum números
{
uno, //toma el valor de 0
dos, //toma el valor de 1
diez = 10, //toma el valor de 10
once, //toma el valor de 11
}

Operadores

Los operadores son símbolos con los cuales C# tomará una acción. Por
ejemplo existen operadores matemáticos para sumar, restar, multiplicar y
dividir números. Existen también operadores de comparación que analizará
si un valor es igual, mayor o menor que otro y operadores de asignación los
cuales asignarán nuevos valores a los objetos o variables. A continuación
explicaremos un poco más detalladamente los operadores en C#:

Operadores matemáticos

Casi todos los lenguajes de programación soportan operadores matemáticos.


Estos operadores se utilizan para realizar operaciones matemáticas sencillas
entre números. Entre estos operadores tenemos los de suma, resta,
multiplicación, división y módulo (o residuo): +,-,*,/,%, y se los usa de la
siguiente manera:

using System;

class operadoresMatematicos
{
public static void Main()
{
int a = 7;
int b = 4;
int c = a + b;
int d = a - b;
int e = a * b;
int f = a / b;
int g = a % b;
Console.WriteLine ("De los números: {0} y {1} la suma es: {2},
la resta es:{3}, la multiplicación es: {4}, la división es: {5} con un residuo
de: {6}",a,b,c,d,e,f,g);
}
}

Operadores de asignación

Los operadores de asignación son aquellos que sirven para asignar el valor del
objeto o variable de la derecha al objeto o variable de la izquierda. Un ejemplo
sencillo de este tipo de operadores es la inicialización de variables. Como
habíamos visto, para asignar el valor a una variable simplemente utilizamos el
símbolo (u operador) igual =

int a = 15; //la variable a tomará el valor de 15.


int b = a = 10; //la variable a y la variable b tomarán el valor de 10.

Además de estos operadores de asignación sencillos, existen otros operadores


de asignación que realizan operaciones matemáticas antes de asignar el valor
a la variable u objeto. Entre ellos tenemos: +=, -=, *=, /=, %=, ++, --.
Ejemplos:

var += 10; // realiza la operación var = var+10;


var -= 10; // realiza la operación var = var-10;
var *= 10; // realiza la operación var = var*10;
var /= 10; // realiza la operación var = var/10;
var++; //realiza la operación var = var+1; después de procesar esta línea
++var; //realiza la operación var = var+1; antes de procesar esta línea
var--; //realiza la operación var = var-1; después de procesar esta línea
--var; //realiza la operación var = var-1; antes de procesar esta línea

Operadores de comparación

Estos operadores son muy útiles cuando tenemos que cambiar el flujo de
nuestro programa. Con ellos podemos comparar si un objeto o variable es
igual (==), no es igual (!=), es mayor o igual (>=), es menor o igual (<=),
es mayor (>) o es menor (<) que otro objeto. El resultado de esta
comparación es de tipo bool es decir verdadero o falso (true o false). Estos
operadores se los usa de la siguiente forma:

int a = 10;
int b = 20;
bool resp;

resp = (a == b); // compara si a es igual a b y retorna el valor bool false (o


falso), tómese en cuenta que a==b es MUY diferente a a=b
resp = (a != b); // compara si a es diferente a b y retorna el valor bool true
(o verdadero)
resp = (a <= b); // compara si a es menor o igual a b y retorna el valor bool
true (o verdadero)
resp = (a >= b); // compara si a es mayor o igual a b y retorna el valor bool
false (o falso)
resp = (a < b); // compara si a es menor a b y retorna el valor bool true (o
verdadero)
resp = (a > b); // compara si a es mayor a b y retorna el valor bool false (o
falso)

Operadores lógicos

Para entender como funcionan los operadores lógicos tenemos que aprender
un poco lo que son los números binarios. En esta parte del libro no
cubriremos en detalle este extenso tema de los números binarios ni del
Algebra que gobierna estos números ni mucho menos de como se comportan
las puertas lógicas dentro de un ordenador porque nos tomaría uno o dos
libros completos, pero nos gustaría dar un poco de bases de como es que los
números binarios forman parte de los operadores lógicos. Toda información
que el ordenador opera internamente es representada por números binarios
(por unos y ceros que son conocidos también por verdadero y falso), así la
letra A el ordenador internamente lo representa en código binaro ASCII
como 01000001 que en números "normales" o decimales es 65. Para
manipular esta información en unos y ceros, el ordernador tiene operadores
lógicos los cuales permiten cambiar la información de una manera que nos
convenga. Por medio de estos operadores lógicos el ordenador es capaz de
tomar decisiones, procesar cualquier información, hacer complicadas
operaciones matemáticas, o en otras palabras, por medio de estos operadores
lógicos, el ordenador hace todo lo que vosotros le habéis visto hacer.

Los operadores lógicos más importantes para nuestro estudio en C# son:

AND

Representado por el símbolo &. Comprueba si todos los números binarios


son 1 (o verdadero) entonces la respuesta es 1 (o verdadero)

OR

Representado por el símbolo | (barra vertical de la tecla del 1). Comprueba


si cualquiera de los números binarios es 1 (o verdadero) entonces la respuesta
es 1 (o verdadero)

NOT

Representado por el símbolo ~ y !. Invierte la respuesta. En operaciones con


tipos bool, el operador ! cambia la variable de verdadero a falso o viceversa,
pero en números binarios, el operador ~ cambia cada uno de los unos y ceros
por su opuesto, cuando encuentra un uno lo cambia por un cero y viceversa,
así por ejemplo si tenemos el número binario 01000001 y aplicamos el
operador NOT ~ obtendremos 10111110, pero si tenemos una expresión
que se evalua como true (o verdadera) y si se aplica el operador !, se obtiene
una respuesta false (o falsa). Por ejemplo (!(10==10)) esta expresión tiene
como resultado false

XOR

Representado por el símbolo ^. En dos números, comprueba si los dos


números binarios son iguales, entonces la respuesta es 0 (o falso).

<<
Desplazar a la izquierda desplaza todos los bits hacia la izquierda
introduciendo ceros al final de la derecha y descartando los últimos números.
Así el número 01000001 si se lo desplaza a la izquierda una vez 01000001
<< 1, se convierte en 10000010

>>

Al igual que el operador anterior, desplazar a la derecha desplaza todos los


bits hacia la derecha introduciendo ceros al final de la izquierda y descartando
los últimos números. Así el número 01000001 si se lo desplaza a la derecha
una vez 01000001 >> 1, se convierte en 00100000

Operadores lógicos de unión

En el caso de que deseemos comparar varios valores para saber si todos son
verdaderos o si alguno es verdadero podemos usar los operadores lógicos de
unión && y ||

a && b // esta línea compara si a y b son verdaderos retorna el valor true (o


verdadero) si los dos lo son
a || b // esta línea compara si a o b son verdaderos retorna el valor true (o
verdadero) si alguno de los dos es
!a // esta línea compara si a es verdadero retorna falso si lo es y viceversa.

a y b pueden representar variables, constantes, números, funciones,


expresiones, etc. que den como resultado un valor de decisión (true o false).
Así por ejemplo, el siguiente ejemplo es válido:

int a = 0;
int b = 10;
int c = 20;

if ((a <= b) && (c >= b))


System.Console.WriteLine ("a es menor o igual a b y c es mayor o igual a
b");
Capítulo 4

Estructuras de control

Hay dos maneras de cambiar el rumbo de ejecución de un programa, estos


pueden ser saltos incondicionales y saltos condicionales. En este capítulo se
describen algunas de estas sentencias. Algunas son muy similares a las
existentes en otros lenguajes, como las sentencias if, for, while, etc. y otras,
como foreach, throw o continue, son algo más específicas.

Saltos incondicionales

Las instrucciones de un programa se ejecutan sentencia por sentencia


empezando desde el método o función principal llamado Main() hasta
terminar con el programa. El programa sin embargo, tomará otros rumbos
incondicionalmente en dos oportunidades: 1. Cuando encuentre la llamada a
otros métodos (Ejemplo 4.1) y 2. Con el uso de las palabras claves como
goto, break, continue, return y throw las cuales se discutirán más adelante.

Ejemplo 4.1 - Salto incondicional a otra función

using System;
namespace Ejemplos{
class Ejemplo4_1{
static void Main(){
Console.WriteLine ("Esta parte se ejecuta primero");
LlamadaOtraFuncion();
Console.WriteLine ("Esta parte se ejecuta al final");
}

static void LlamadaOtraFuncion(){


Console.WriteLine ("Ha salido del método Main()");
}
}
}

En el ejemplo anterior el programa ejecuta sentencia por sentencia el método


principal Main() hasta que encuentra la llamada a otro método. Después de
que el método llamado haya terminado el método Main continuará con la
ejecución de las sentencias restantes.

La sentencia goto

En los inicios de los lenguajes de programación la sentencia goto fue la más


popular para ir de un lugar a otro dentro del programa. Sin embargo esto
creaba una tremenda confusión al momento de diseñar la aplicación. Si el
programador quería hacer un esquema de como funcionaba dicha aplicación,
se veía con un laberinto tipo espagueti de líneas y símbolos conectados entre
sí. Es por esto que esta sentencia es un poco "problemática" y fuera de
"moda" entre los lenguajes de programación modernos. C# sin embargo
soporta esta sentencia. Os recomendamos no utilizarla a menos que sea
necesario o si os sentís cómodos haciéndolo, pero cuando os cree un laberinto
difícil de depurar, no digáis que no os advertimos de no utilizarla. Hay
muchas otras mejores maneras de cumplir con el mismo propósito (la
sentencia while por ejemplo es una de ellas), las cuales son más elegantes y
más sencillas de depurar.

La sentencia goto funciona de la siguiente manera:

Primero se crea una etiqueta al inicio de cierto bloque de código y después en


otro lugar podemos saltar hacia esa etiqueta usando la palabra clave goto. El
siguiente ejemplo ilustra la sentencia goto:

using System;
namespace Ejemplos
{
class Ejemplo4_0
{
static void Main()
{
int contador=0;
REPETIR:
Console.WriteLine ("Esta línea se repetirá 100 veces, esta es la linea
numero: {0}", contador);
if (contador++ < 100)
goto REPETIR;
Console.WriteLine ("Despues de que el contador sea igual o
mayor que 100 se imprimirá esta línea");
}
}
}

Esta sentencia es un ejemplo de salto incondicional ya que por si solo saltará


a la etiqueta seleccionada incondicionalmente.

Saltos condicionales

Los saltos condicionales sirven para ejecutar cierto código solamente si se


cumple con alguna condición. Entre otros tenemos:

Instrucción if

Esta sentencia sirve para ejecutar unas instrucciones en caso de que se cumpla
determinada condición. La forma completa de la instrucción if es

if( condición ) {
instrucciones;
...
}
else {
instrucciones;
...
}

donde la cláusula else es opcional. Si la condición es verdadera, se ejecutarán


las instrucciones dentro del bloque if, mientras que si es falsa, se ejecutará el
bloque else. El valor que controla la sentencia if debe ser de tipo bool. El
siguiente ejemplo

//programa que determina si un valor es positivo o negativo


using System;

class InstruccionIf{

public static void Main()


{
double d;

Console.WriteLine("Introduce un numero");
d = Double.Parse( Console.ReadLine() );

if( d>0 )
{
Console.WriteLine("El numero {0} es positivo", d);
}
else
{
Console.WriteLine("El numero {0} es negativo", d);
}
}
}

te pide que introduzcas un número y dependiendo de si se cumple que dicho


número es mayor que cero (condición), se ejecuta un bloque u otro.

La sentencia d = Double.Parse( Console.ReadLine() ); tal vez requiera algo


de explicación adicional. En realidad, con Console.ReadLine() estamos
leyendo lo que el usuario introduce por pantalla, que es una cadena de
caracteres, y con Double.Parse lo que hacemos es interpretar esa cadena de
caracteres y convertirla en un tipo numérico double, de forma que tendrá el
valor del número que introduzcamos por la consola.
Las intrucciones if se pueden anidar, y existe también una extensión de la
sentencia if, la sentencia if-else-if. Su formato es el siguiente:

if( condicion1 )
{
instrucciones;
}
else if( condicion2 )
{
instrucciones;
}
...
else
{
instrucciones;
}

Las instrucciones condicionales se evalúan de arriba a abajo. Tan pronto


como se encuentra una condición true, se ejecuta la instrucción asociada con
ella, y el resto de la escalera se omite. Si ninguna de las condiciones es true, se
ejecutará la última instrucción else. La última instrucción else actúa como
condición predeterminada, es decir, si no funciona ninguna de las otras
pruebas condicionales, se realiza esta última instrucción. Si no existe esta
instrucción else final y el resto de de las condiciones son falsas, entonces no
se realizará ninguna acción. El siguiente ejemplo

using System;

class IfElseIf{
public static void Main()
{
string opcion;

Console.WriteLine("Elija una opción (si/no)");


opcion = Console.ReadLine();
if( opcion=="si" )
{
Console.WriteLine( "Muy bien, ha elegido si" );
}
else if( opcion=="no" )
{
Console.WriteLine( "Ha elegido no" );
}
else{
Console.WriteLine("No entiendo lo que ha escrito");
}
}
}

le pide al usuario que elija una opción si/no y la procesa usando una
estructura if-else-if. Si la opción no es ni "si" ni "no", entonces se ejecuta la
sentencia else por defecto, que imprime por pantalla el mensaje "No entiendo
lo que ha escrito"

Nota: Hay que tener mucho cuidado que el símbolo = no es igual a ==, el
primero sirve para asignar un valor a una variable
y el segundo sirve para comparar si dos términos son iguales.

Instrucción switch

La instrucción switch es muy parecida a la estructura if-else-if, sólo que


permite seleccionar entre varias alternativas de una manera más cómoda.
Funciona de la siguiente manera: el valor de una expresión se prueba
sucesivamente con una lista de constantes. Cuando se encuentra una
coincidencia, se ejecuta la secuencia de instrucciones asociada con esa
coincidencia. La forma general de la instrucción switch es la siguiente:

switch( expresión ){
case constante1:
instrucciones;
break;
case constante2:
instrucciones;
break;
...
default:
instrucciones;
break;
}

La sentencia default se ejecutará sólo si ninguna constante de las que siguen


a case coincide con expresión. Es algo similar al else final de la instrucción if-
else-if.

Sin más, vamos a por un ejemplo

using System;

class InstruccionSwitch{
public static void Main()
{
string s;

Console.WriteLine( "Elige hacer algo con los números 2 y 3");


Console.WriteLine( " + para sumarlos" );
Console.WriteLine( " - para restarlos" );
Console.WriteLine( " * para multiplicarlos" );
Console.WriteLine( " / para dividirlos (division entera)" );

s = Console.ReadLine();

switch(s){
case "+":
Console.WriteLine("El resultado es {0}", 2+3);
break;
case "-":
Console.WriteLine("El resultado es {0}", 2-3);
break;
case "*":
Console.WriteLine("El resultado es {0}", 2*3);
break;
case "/":
Console.WriteLine("El resultado es {0}", 2/3);
break;
default:
Console.WriteLine("No te entiendo");
break;
}
}
}

El cual solicita al usuario que inserte uno de los símbolos +-*/ , y con un
switch compara los resultados para hacer diferentes acciones dependiendo del
valor de s, que es la cadena de caracteres que almacena la elección del usuario.
El resultado debería ser algo parecido a esto:

Elige hacer algo con los números 2 y 3


+ para sumarlos
- para restarlos
* para multiplicarlos
/ para dividirlos (division entera)
*
El resultado es 6

Como habrá notado, al final de todo case siempre hay una sentencia break.
Esto no es obligatorio, puede haber en su lugar otra sentencia de salto como
un goto inclusive en el caso default.

Siempre se deberá tener un break o un goto en cada caso a menos que la


sentencia esté vacía. En esta situación se ejecutará el siguiente caso que viene
en la lista. Si no se toma en cuenta ésto se obtiene un error en tiempo de
compilación. Otros lenguajes, como C/C++ o Java no tienen esta restricción.
La razón de adoptarla en C# es doble: por un lado, elimina muchos errores
comunes y en segundo lugar permite al compilador reorganizar las sentencias
de los case, y así permitir su optimización.

Ejemplo:

using System;

class InstruccionSwitch{
public static void Main()
{
int voto;
Console.WriteLine( "Qué tipo de musica te gusta más");
Console.WriteLine( "1 - Rock" );
Console.WriteLine( "2 - Clásica (clasica cuenta como
instrumental)" );
Console.WriteLine( "3 - Instrumental" );
Console.WriteLine( "4 - Alternativa (alternativo cuenta como
Rock)" );

voto = Int32.Parse(Console.ReadLine());

switch(voto){
case 1:
Console.WriteLine("Has votado por Rock o Alternativo");
break;
case 2: //Debido a que no tiene ni un goto ni break y está vacía
va al siguiente caso
case 3:
Console.WriteLine("Has votado por Clásica o Instrumental");
break;
case 4:
goto case 1;
default:
Console.WriteLine("No te entiendo");
break;
}
}
}

Como bien se puede notar en el ejemplo, en el case 4, se utiliza la instrucción


goto, indicando que se vaya al case 1, ya que según la lógica del ejemplo, es
igual elegir 1 o 4. Nótese que no es necesario usar break; en el case 4 ya que
se utilizó goto.

Bucle for

El bucle for de C# es idéntico al encontrado en los lenguajes C/C++ y Java.


El formato general es

for( inicialización; condición; iteración )


{
instrucciones;
}

Las sentencias de inicialización se ejecutan una vez al principio y sirven


principalmente para asignar valores a las variables que servirán de contador.
Las sentencias de condición, por su parte, se ejecutan cada vez que el bucle
vuelve al principio y sirven para controlar el bucle: éste seguirá realizándose
siempre y cuando estas condiciones sean true.

Las sentencias de iteración se ejecutan también cada vez que se realiza una
nuevo ciclo en el bucle, y sirven para cambiar el estado de las variables que
gobiernan las sentencias de condición. Pero todo esto se entiende mejor con
un ejemplo

using System;

class BucleFor{
public static void Main()
{
int i; //el contador

for( i = 0; i < 10; i++)


{
Console.WriteLine( i );
}
}
}

Este ejemplo imprime por pantalla los 10 primero enteros positivos. Es un


caso muy simple del bucle for. Por cierto, el operador ++ lo que hace es que
añade una unidad a la variable a la que acompaña, de forma que, por ejemplo,
9++ es 10. De esta forma, la variable i se incrementa a cada vuelta.

En el ejemplo anterior, las sentencias de inicialización y de iteración eran


únicas, pero esto no tiene por qué ser así, de hecho se pueden utilizar varias
sentencias separadas por comas. Por ejemplo, se pueden usar dos variables
para controlar el bucle

using System;

class BucleFor2{
public static void Main()
{
int i;
int j;

for( i=0, j=10; i<j; i++, j--)


{
Console.WriteLine("( {0} , {1} )", i, j);
}
}
}

Por su parte, la expresión condicional del bucle for puede ser cualquier
expresión que genere un valor booleano. En este caso se ha usado "i<j", pero
también hubiera sido válida "i==5", "true" (el bucle se realizará
indefinidamente) o "false" (el bucle no se realizará).
Bucle while

El bucle while es un bucle que se realiza mientras se cumpla determinada


condición. Tiene la forma

while( condición )
{
instrucciones;
}

Donde la condición tiene que ser un valor booleano. Tiene una estructura
muy sencilla, así que vamos a ver directamente un ejemplo.

using System;

class BucleWhile{
public static void Main()
{
int i = 0;
while( i<10)
{
Console.WriteLine( i );
i = i+1;
}
}
}

En el que se realiza lo mismo que en el ejemplo anterior, sólo que ahora con
un bucle while.

Bucle do-while

Se trata de una ligera variante del bucle anterior, con la diferencia de que
ahora primero se ejecutan las instrucciones y luego se evalúa la condición, de
forma que tiene tiene una estructura:

do{
instrucciones;
}
while( condición );

El siguiente ejemplo

using System;

class BucleDoWhile{
public static void Main()
{
string s = "";

do
{
Console.WriteLine( "Introduce si para salir del bucle" );
s = Console.ReadLine();
}
while( s != "si" );
}
}

muestra un programa que ejecuta un bucle hasta que el usuario introduce "si".
Por cierto, != es lo contrario de ==, es decir, != devuelve true cuando los
valores comparados son distintos.

Bucle foreach

El bucle foreach se utiliza para hacer iteraciones sobre elementos de una


colección, como pueden ser los enteros dentro de un arreglo de enteros. La
sintaxis sigue la siguiente estructura:

foreach( tipo in coleccion )


{
instrucciones;
}
Como hemos comentado, el uso más inmediato es iterar sobre un arreglo de
números:

using System;

class BucleForeach{
public static void Main()
{
int[,] arr = {{1,2},{2,3}};

foreach( int elem in arr )


{
Console.WriteLine( elem );
}
}
}

Este ejemplo sólo imprime los valores de una matriz, pero como se puede
comprobar mejora mucho la claridad del código comparándolo con una
implementación con bucles for como esta

using System;

class BucleForeach{
public static void Main()
{
int i, j; //seran los indexadores de la matriz

int[,] arr = {{1,2},{2,3}};

for(i = 0; i<2; i++ )


{
for( j = 0; j<2; j++ )
{
Console.WriteLine( arr[i,j] );
}
}
}
}

Además, es posible utilizar el bucle foreach con cualquier tipo que sea una
colección, no solo con arreglos, como veremos más adelante.

Usando continue y break

continue y break son dos palabras clave que nos permiten saltar
incondicionalmente al inicio de un bucle (continue) o fuera de un bucle
(break) cuando se necesite. Por ejemplo:

using System;

class continueBreak
{
public static void Main()
{
for(int i = 0; i<10; i++ )
{
if (i==5)
continue;
if (i==9)
break;
Console.Write("{0},",i);
}
}
}

Este pequeño programa entrará en un bucle for que hará que la variable i tome
los valores del 1 al 10, pero al llegar al número 5 el bucle saltará
incondicionalmente al inicio del bucle sin ejecutar las líneas que siguen más
adelante por lo que no ejecutará la línea que imprime en la pantalla el número
5. Cosa similar sucede cuando llega al número 9: el bucle será detenido por
el salto incondicional break que romperá el bucle cuando encuentre esta
palabra. El resultado será el siguiente:

0,1,2,3,4,6,7,8,

El bucle saltó la línea que imprime 5 y terminó cuando llegó a 9 gracias a las
palabras clave continue y break.

Capítulo 5

Introducción a las clases y objetos

Introducción a las clases en C#

Como hemos dicho, C# es un lenguaje orientado a objetos. A diferencia de


lenguajes como C++ o Python en los que la orientación a objetos es opcional,
en C# y al igual que en Java, la orientación a objetos es ineludible, de hecho
cualquier método o variable está contenida dentro de un objeto. Y el
concepto fundamental en torno a la orientación a objetos es la clase.

Una clase es como una plantilla que describe cómo deben ser las instancias
de dicha clase, de forma que cuando creamos una instancia, ésta tendrá
exactamente los mismos métodos y variables que los que tiene la clase. Los
datos y métodos contenidos en una clase se llaman miembros de la clase y se
accede a ellos siempre mediante el operador ".".

En el siguiente ejemplo, se definirá una clase, Clase1 y en el método Main se


creará una instancia de Clase1 llamada MiClase.
Una buena idea es jugar un poco con el código para ver que la instancia de la
clase efectivamente tiene los mismos miembros que la clase Clase1 (que sería
la plantilla de la que hablábamos antes)

using System;

//definimos nuestra clase


class Clase1{

public int a = 1;
private double b = 3;
public char c = 'a';
}

//usamos la clase que hemos creado


class UsoClase{
public static void Main()
{
Clase1 MiClase = new Clase1(); // asi creamos una instancia de
Clase1
Console.WriteLine( MiClase.c ); //podemos llamar a los tipos que
hay dentro de Clase1
}
}

los identificadores public delante de los tipos que hay dentro de Clase1 son
necesarios para luego poder ser llamados desde otra clase, como en este caso,
que estamos llamando a los miembros de una instancia de Clase1 desde
UsoClase. Pero en las clases no solo hay variables, también podemos incluir
métodos.

using System;

//definimos nuestra clase


class Clase1{
public int a = 1;
public double b = 3;
public char c = 'a';

public void Descripcion()


{
Console.WriteLine("Hola, soy una clase");
}
}

//usamos la clase que hemos creado


class UsoClase{
public static void Main()
{
Clase1 MiClase = new Clase1(); // asi creamos una instancia de
Clase1
Console.WriteLine( MiClase.c ); //podemos usar todos los tipos
que hay dentro de Clase1
MiClase.Descripcion();
}
}

Podemos hacer más cosas con las clases, como heredar otras clases o
implementar interfaces, pero en este capítulo nos centraremos en el uso de
métodos y variables.

Métodos

Los métodos, también llamados funciones, son trozos de código que reciben
unos datos, hacen algo con esos datos, y a veces devuelven algún valor. En
C#, todos los métodos se encuentran contenidos dentro de un objeto.

La estructura mínima de un método tiene las siguientes partes:

* Tipo devuelto
* Nombre del método
* Parámetros (puede ser vacío)
* Cuerpo del método

de forma que el siguiente método:

double Divide( double a, double b )


{
return a/b;
}

devuelve un tipo double, tiene por nombre Divide, los parámetos son a y b,
ambos del tipo double, y el cuerpo del método es simplemente "return a/b;".

Cuando queramos llamar a un método, debemos simplemente poner el


nombre del método y sus argumentos dentro de un paréntesis separados por
comas. Para llamar al método Divide declarado antes, simplemente debemos
escribir Divide(8, 2);

Según lo que hemos visto, el ejemplo del método Divide() completo necesita
tener una clase donde definirse y un método Main() donde ejecutarse.

using System;

class Metodo{
public double Divide( double a, double b )
{
return a/b;
}
}

class Principal{
public static void Main()
{
Metodo m = new Metodo();
Console.WriteLine( m.Divide(8, 2) );
}
}

Pasando valores a los métodos

Parámetros

La declaración formal de parámetros también define variables. Hay cuatro


tipos de parámetros: parámetros por valor, por referencia, parámetros de
salida, y arreglos de parámetros.

Paso por valor

El paso de parámetros por valor es usado por defecto para pasar parámetros
a métodos. Cuando se pasa un parámetro por valor a una función realmente
se está pasando una copia de dicho parámetro, por lo que las modificaciones
que le hagamos al parámetro dentro del método no afectarán al parámetro
original. El ejemplo

using System;

class Test {
static void F(int p) {
p++;
Console.WriteLine("p = {0}", p);
}
static void Main() {
int a = 1;
Console.WriteLine("pre: a = {0}", a);
F(a);
Console.WriteLine("post: a = {0}", a);
}
}

muestra un método F que tiene un parámetro por valor llamado p. El ejemplo


produce la salida:

pre: a = 1
p=2
post: a = 1

aunque el valor del parámetro p haya sido modificado dentro del método,
éste parámetro solamente tenía una copia del valor del parámetro a que
pasamos al método; por lo que cuando imprimimos el parámetro a vemos que
éste parámetro ha mantenido su valor original.

Paso por referencia

El paso de parámetros por referencia es la contraposición lógica al paso por


valor. En el paso por referencia no se realiza ninguna copia del objeto, sino
que lo que se le pasa a la función es una referencia del objeto, de forma que
el parámetro pasa directamente a la función y cualquier modificación sobre
el parámetro dentro de la función afectará al parámetro original

using System;

class Test {
static void Swap(ref int a, ref int b) {
// intercambia los dos valores
int t = a;
a = b;
b = t;
}
static void Main() {
int x = 1;
int y = 2;

Console.WriteLine("pre: x = {0}, y = {1}", x, y);


Swap(ref x, ref y);
Console.WriteLine("post: x = {0}, y = {1}", x, y);
}
}
muestra un método swap que tiene dos parámetros por referencia. La salida
producida es:

pre: x = 1, y = 2
post: x = 2, y = 1

La palabra clave ref debe de ser usada tanto en la declaración formal de la


función como en los usos que se hace de ésta.

Parámetro de salida

El parámetro de salida es similar al parámetro por referencia, salvo que el


valor inicial de dicho argumento carece de importancia. Un argumento de
salida se declara con el modificador out. El ejemplo

using System;

class Test {
static void Divide(int num1, int num2, out int result, out int resid) {
result = num1 / num2;
resid = num1 % num2;
}
static void Main() {
int valor1 = 10;
int valor2 = 3;
int respuesta, residuo;
Divide(valor1, valor2, out respuesta, out residuo);
Console.WriteLine("La división de {0} para {1} = {2} con un
residuo de {3}", valor1, valor2, respuesta, residuo);
}
}

muestra un método Divide que incluye dos parámetros de salida. Uno para
el resultado (variable result) de la división y otro para el resto (variable resid).
Vemos que estos resultados son asignados a las variables respuesta y residuo
respectivamente.
Arreglo de parámetros

Habrá ocasiones que necesitemos pasar varios parámetros a un método (o


función) pero no sabremos con anticipación cuantos parámetros tendremos
que pasar; para esto podremos usar un arreglo de parámetros. Un arreglo de
parámetros permite guardar una relación de varios a uno: varios argumentos
pueden ser representados por un único arreglo de parámetros. En otras
palabras, los arreglos de parámetros permiten listas de argumentos de tamaño
variable.

Un arreglo de parámetros se declara con el modificador params. Sólo puede


haber un arreglo de parámetros en cada método, y siempre debe ser el último
parámetro especificado. El tipo del arreglo de parámetros debe ser siempre
un tipo arreglo unidimensional. Al llamar a la función se puede pasar uno o
varios argumentos del tipo del arreglo. El ejemplo

using System;

class Test
{
static void F(params int[] args) {
Console.WriteLine("nº de argumentos: {0}", args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine("args[{0}] = {1}", i, args[i]);
}
static void Main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(new int[] {1, 2, 3, 4});
}
}

muestra un método F que toma un número variable de argumentos int, y


varias llamadas a este método. La salida es:
nº de argumentos: 0
nº de argumentos: 1
args[0] = 1
nº de argumentos: 2
args[0] = 1
args[1] = 2
nº de argumentos: 3
args[0] = 1
args[1] = 2
args[2] = 3
nº de argumentos: 4
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4

La mayoría de los ejemplos presentes en este capítulo utilizan el método


WriteLine de la clase Console. El comportamiento para las sustituciones,
como muestra el ejemplo

int a = 1, b = 2;
Console.WriteLine("a = {0}, b = {1}", a, b);

se consigue usando un arreglo de parámetros. El método WriteLine


proporciona varios métodos sobrecargados para el caso común en el que se
pasa un pequeño número de argumentos, y un método que usa un arreglo de
parámetros.

using System;

namespace System
{
public class Console
{
public static void WriteLine(string s) {...}
public static void WriteLine(string s, object a) {...}
public static void WriteLine(string s, object a, object b) {...}
...
public static void WriteLine(string s, params object[] args) {...}
}
}

Modificadores public y static

El modificador public lo hemos utilizado anteriormente. Se puede utilizar en


la declaración de cualquier método o variable, y como es de esperar, produce
el efecto de que el campo afectado se vuelve público, esto es, se puede utilizar
desde otras clases

using System;

class Metodo{
public double Divide( double a, double b )
{
return a/b;
}
}

class Principal{
public static void Main()
{
Metodo m = new Metodo();
Console.WriteLine( m.Divide(8, 2) );
}
}

Si por ejemplo intentamos declarar el método Divide sin el modificador


public, obtendremos un error en tiempo de compilación. El modificador
complementario de public es private, que provoca que el método o dato solo
sea accesible desde la clase en la que está declarado. Si no se especifica nada,
se toma por defecto el modificador private

De esta forma podríamos separar las clases Metodo y Principal en dos


archivos separados, llamados por ejemplo metodo.cs y principal.cs . Para
compilar esto, bastará compilar ambos archivos al mismo tiempo, de forma
similar a esto: mcs principal.cs metodo.cs

Además, tampoco es necesario crear una instancia de la clase sólo para acceder
a un método declarado en ella. Para eso debemos anteponer a la declaración
del método el modificador static. Los métodos estáticos se caracterizan por
no necesitar una instancia de la clase para cumplir su función, pero como
contrapartida, no pueden acceder a datos propios de la clase.

using System;

class Metodo{
public static double Divide( double a, double b )
{
return a/b;
}
}

class Principal{
public static void Main()
{
Console.WriteLine( Metodo.Divide(8, 2) );
}
}

Los métodos estáticos se utilizan en multitud de situaciones. Por ejemplo, el


método Console.WriteLine() o las funciones de la librería matemática
estándar no son más que métodos estáticos de sus respectivas clases.

Constructores e instancias de una clase

Como hemos visto, las instancias de una clase se crean con la sintaxis
nombreclase objeto = new nombreclase( argumentos );

donde nombreclase es el nombre que le hemos dado a la definición de la clase,


argumentos es una lista de argumentos posiblemente vacía y objeto es el
nombre que queremos darle a la instancia de la clase.

Una vez creada una clase, sus miembros se inicializan a sus valores
predeterminados ( cero para valores numéricos, cadena vacía para el tipo
string, etc. ). La siguiente clase representa un punto sobre el plano, de forma
que tiene dos valores públicos X e Y, y un método que calcula la distancia al
origen del punto (módulo)

using System;

class Punto{
public double X;
public double Y;

public double Modulo()


{
double d;
d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadrada
return d;
}
}

class Principal{
public static void Main()
{
Punto A = new Punto();

A.X = 1;
A.Y = 1;

Console.WriteLine("El modulo del punto (1,1) es: {0}",


A.Modulo() );

}
}

Ahora bien, la forma en la que se crea la instancia, es decir, inicializando los


datos a cero (ejercicio: comprobar esto), se puede personalizar, de forma que
podemos construir nuestro propio constructor que le diga a la clase los
valores por defecto que debe tomar. Esto se realiza simplemente escribiendo
dentro de la clase un método que tenga el mismo nombre que la clase y en el
que no se especifica el valor devuelto. La clase Punto con un constructor sería
así:

using System;

class Punto{
public double X;
public double Y;

public Punto() //constructor


{
X = 1;
Y = 1;
}

public double Modulo()


{
double d;
d = Math.Sqrt(X*X + Y*Y); //Sqrt = raiz cuadrada
return d;
}
}
de forma que ahora al crear una instancia de la clase se crea el punto (1,1) en
lugar del (0,0), que era el que se creaba por defecto. De esta forma, al crear
la instancia, par ya contendrá los valores (1,1) .

En la práctica se utilizan mucho constructores con parámetos, de forma que


al crear la instancia se le asignan valores según los parámetros. La siguiente
implementación de Par contiene un constructor que acepta un par de valores,
que servirán para inicializar los valores A y B

class Punto{
public Punto( double val1, double val2)
{
X = val1;
Y = val2;
}
...
}

También tenemos la posibilidad de declarar una clase con varios


constructores (cada uno con diferentes parámetros) Lo que hará el
compilador de C# es buscar el constructor que se adecúe a los parámetros
que le llegan, y ejecutarlo como si fuera un método más. Dependiendo de la
llamada que se haga en el "new", usaremos un constructor u otro.

Sobrecarga de métodos

En C#, al igual que en C++ y en Java es posible definir varios métodos con
el mismo nombre pero con distintos parámetros, de forma que el compilador
decide a cuál se llama dependiendo de los parámetros que le lleguen.

Esto es muy práctico, pues no tienes que renombrar cada función según el
tipo de valor que acepta. El siguiente ejemplo implementa un par de métodos
que elevan al cuadrado el valor que reciben, y se implementan para tipos
double y para int. En C, que es un lenguaje que no soporta sobrecarga de
métodos, se tendría que haber llamado distinto a ambos métodos, por
ejemplo alcuadrado_double y alcuadrado_int
using System;

class Eleva{
public static double AlCuadrado( int a )
{
return a*a;
}

public static double AlCuadrado( double a )


{
return a*a;
}
}

class Principal{
public static void Main()
{
Console.WriteLine("4 al cuadrado es {0}", Eleva.AlCuadrado(4)
);
Console.WriteLine("3.2 al cuadrado es {0}",
Eleva.AlCuadrado(3.2) );
}
}

La palabra reservada this

La palabra reservada this sirve para hacer referencia a miembros de la clase en


caso de que se quiera especificar, ya sea por motivos de colisión de nombres
o por la claridad del código. Su sintaxis es

this.campo

donde campo es la variable de la clase a la que queremos hacer referencia.


En el siguiente ejemplo, declaramos un constructor para la clase Punto, que
toma dos argumentos X e Y. Entonces es obligado el uso de this para
distinguir entre el X de la clase y el X tomado como parámetro

class Complejo
{
double X;
double Y;

Complejo(double X, double Y)
{
this.X = X;
this.Y = Y;
}
}

Propiedades e indizadores

Propiedades

Las propiedades son una característica de C# que permiten aparentemente el


acceso a un miembro de la clase mientras mantiene el control asociado al
acceso mediante métodos.

Para los programadores de Java hay que decir que esto no es más que la
formalización del patrón de asignación (setter) y método de lectura (getter)

Las propiedades son como métodos que se declaran dentro de un bloque


asociado a una variable mediante las palabras reservadas get (se encarga de
devolver algo cuando se llama al tipo que lo contiene ) y set (que hace algo
cuando se le asigna un valor a la variable que lo contiene. Este valor viene
especificado en la variable value )

using System;

class TestProperties {
private static string clave;
public string Clave {
get
{
Console.WriteLine ("Acceso a la propiedad clave");
return clave;
}
set
{
Console.WriteLine ("Cambio del valor de clave");
clave = value;
}
}
}

class Test {
public static void Main () {
TestProperties tp = new TestProperties();
string c = "ClaveClave";
tp.Clave = c;
Console.WriteLine (tp.Clave);
}
}

En realidad, lo que se hace es declarar una variable privada de forma que no


se puede acceder de forma directa, y se crean dos métodos ( o uno si solo se
requiere acceso de lectura) que permiten acceder al contenido de la variable y
tal vez modificarla. Si no queremos que se pueda modificar la variable, no
incluímos el método "set" y ya tendríamos propiedades de sólo lectura.

Indexadores

Hemos visto, en el apartado en el que tratamos las propiedades, que podemos


acceder a una variable privada de una clase a través de eventos que nos
permiten controlar la forma en la que accedemos a dicha variable.
Los indexadores nos van a permitir hacer algo parecido. Nos van a permitir
acceder a una clase como si se tratara de un arreglo. Lo vemos de forma más
sencilla con un ejemplo:

using System;

class PruebaIndexadores
{
private int[] tabla = {1, 2, 3, 4};

public int this [int indice]


{
get
{
Console.WriteLine ("La posicion {0} de la tabla tiene el valor {1}",
indice, tabla[indice]);
return tabla[indice];
}
set
{
Console.WriteLine ("Escrito el valor {0} en la posición {1} de la
tabla", value, indice);
tabla[indice] = value;
}
}
}

Tenemos una clase PruebaIndexadores en la que hay un array llamado


"tabla", declarado como privado, por lo que no podremos acceder a él desde
fuera de nuestra clase. Pero hemos declarado también un indexador (public
int this [int indice]), que nos permitirá acceder a él de forma más controlada.

Para probar esta clase, creamos otra clase con un punto de entrada (public
static void Main ()), que será donde hagamos las pruebas.

Primero creamos un objeto de la clase PruebaIndexadores:


PruebaIndexadores obj = new PruebaIndexadores ();

Luego accedemos a una posición del indexador:

int a = obj[3];

Esta línea lo que hace es llamar al indexador, pasándole como parámetro el


índice, en este caso 3. Al ser una consulta de lectura, se ejecuta el código que
haya en la parte "get" del indexador. Una vez ejecutado, lo que nos aparece
por pantalla es esto:

La posicion 3 de la tabla tiene el valor 4

Vamos ahora a hacer un cambio en la tabla:

obj[3] = 6;

Lo que se ejecuta ahora es la parte "set" del indexador. Lo que aparecerá en


pantalla una vez ejecutado esto será:

Escrito el valor 6 en la posición 3 de la tabla


Nótese que tenemos que hacer explícitamente el acceso al array
(tabla[indice]=value) en el set, ya que el indexador no tiene forma de saber
qué variable se supone que tiene que manejar. Si no pusiéramos esa línea, en
realidad el indexador no cambiaría el valor del array.

Para comprobar que realmente se ha hecho el cambio, volvemos a acceder al


indexador:

a = obj[3];

Y esta vez nos aparecerá esto:

La posicion 3 de la tabla tiene el valor 6.

Capítulo 6

Herencia y Polimorfismo

Herencia

La herencia es un concepto fundamental de la programación orientada a


objetos. Cuando se dice que una cierta clase A hereda otra clase B significa
que la clase A contiene todos los miembros de la clase B más algunos que
opcionalmente puede implementar ella misma

Las clases en C# soportan herencia simple, de forma que una clase puede
derivar de otra, pero no de varias (como si era posible en C++). De hecho,
en C# todas las clases derivan implícitamente de la clase object.
La sintaxis que se utiliza es la siguiente:

class MiClaseDerivada : MiClaseBase


{
//miembros
}

En el siguiente ejemplo definimos una clase A con un método F().


Posteriormente definimos una clase B que hereda A y además define un
método G(). Finalmente creamos una clase con un método Main() que
llamará a los dos métodos de B, al implementado por B y al heredado

using System;

class A{
public void F()
{
Console.WriteLine("Soy F() de A");
}
}

class B : A{
public void G()
{
Console.WriteLine("Soy G() de B");
}
}

class Principal{
public static void Main()
{
B clase_heredada = new B();
clase_heredada.F();
clase_heredada.G();
}
}

La palabra reservada base

La palabra reservada base sirve para acceder a miembros de la clase heredada


de la misma forma que this sirve para acceder a miembros de la propia clase.
Su sintaxis es idéntica a la de this, esto es:

base.nombre_del_miembro

En el siguiente ejemplo declaramos una clase B que hereda A y que utiliza el


método F() de A.

class B : A{
public void H()
{
base.F();
Console.WriteLine("soy H() de B");
}
}

Clases Abstractas

Las clases abstractas son clases que contienen algún método incompleto, esto
es, que está definido pero no implementado. Por lo tanto, no se pueden
instanciar y su único propósito es servir de clase base de las que se derivarán
otras clases.

Las clases que heredan una clase abstracta deben implementar los métodos
incompletos. Las clases abstractas se declaran con la palabra reservada abstract

using System;
abstract class A{
public void F(); //metodo no implementado
}

class B : A{
//error en tiempo de compilación, B tiene que definir un método F()
}

Miembros virtual

Métodos, propiedades e indexadores pueden ser virtual, lo que significa que


su implementación puede ser sobreescrita en clases derivadas. El ejemplo

using System;

class A {
public virtual void F()
{
Console.WriteLine("A.F");
}
}

class B: A {
public override void F()
{
base.F();
Console.WriteLine("B.F");
}
}

class Test {
public static void Main()
{
B b = new B();
b.F();
A a = b;
a.F();
}
}

muestra una clase A con un método virtual F, y una clase B que sobreescribe
F. El método sobreescrito en B contiene una llamada, base.F(), el cual llama
al método sobreescrito en A.

Problemas propuestos

Por escribir. Puedes colaborar escribiendo estos problemas.


Este código me pareció interesante de compartir, una modesta recreación del
efecto matrix, escrita en C#.

using System;

namespace m7tr1x
{
class Program
{
static void Main(string[] args)
{
Console.Title = "tH3 M7tr1x 3ff3<t";
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WindowLeft = Console.WindowTop = 0;
Console.WindowHeight = Console.BufferHeight =
Console.LargestWindowHeight;
Console.WindowWidth = Console.BufferWidth =
Console.LargestWindowWidth;
Console.WriteLine("H1T 7NY K3Y T0 C0NT1NU3 =/");
Console.ReadKey();
Console.CursorVisible = false;
int width, height;
int[] y;
int[] l;
Initialize(out width, out height, out y, out l);
int ms;
while (true)
{
DateTime t1 = DateTime.Now;
MatrixStep(width, height, y, l);
ms = 10 - (int)((TimeSpan)(DateTime.Now -
t1)).TotalMilliseconds;
if (ms> 0)
System.Threading.Thread.Sleep(ms);
if (Console.KeyAvailable)
if (Console.ReadKey().Key == ConsoleKey.F5)
Initialize(out width, out height, out y, out l);
}
}

static bool thistime = false;

private static void MatrixStep(int width, int height, int[] y, int[] l)


{
int x;
thistime = !thistime;
for (x = 0; x <width; ++x)
{
if (x % 11 == 10)
{
if (!thistime)
continue;
Console.ForegroundColor = ConsoleColor.White;
}
else
{
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.SetCursorPosition(x, inBoxY(y[x] - 2 - (l[x] / 40 * 2),
height));
Console.Write(R);
Console.ForegroundColor = ConsoleColor.Green;
}
Console.SetCursorPosition(x, y[x]);
Console.Write(R);
y[x] = inBoxY(y[x] + 1, height);
Console.SetCursorPosition(x, inBoxY(y[x] - l[x], height));
Console.Write(' ');
}
}

private static void Initialize(out int width, out int height, out int[] y,
out int[] l)
{
int h1;
int h2 = (h1 = (height = Console.WindowHeight) / 2) / 2;
width = Console.WindowWidth - 1;
y = new int[width];
l = new int[width];
int x;
Console.Clear();
for (x = 0; x <width; ++x)
{
y[x] = r.Next(height);
l[x] = r.Next(h2 * ((x % 11 != 10) ? 2 : 1), h1 * ((x % 11 != 10)
? 2 : 1));
}
}

static Random r = new Random();


static char R
{
get
{
int t = r.Next(10);
if (t <= 2)
return (char)('0' + r.Next(10));
else if (t <= 4)
return (char)('a' + r.Next(27));
else if (t <= 6)
return (char)('A' + r.Next(27));
else
return (char)(r.Next(32, 255));
}
}

public static int inBoxY(int n, int height)


{
n = n % height;
if (n <0)
return n + height;
else
return n;
}
}
}
Capítulo 7

Sobrecarga de operadores

¿Qué es la sobrecarga de operadores?

La sobrecarga de operadores es la capacidad para transformar los operadores


de un lenguaje como por ejemplo el +, -, etc, cuando se dice transformar se
refiere a que los operandos que entran en juego no tienen que ser los que
admite el lenguaje por defecto. Mediante esta técnica podemos sumar dos
objetos creados por nosotros o un objeto y un entero, en vez de limitarnos a
sumar números enteros o reales, por ejemplo.

La sobrecarga de operadores ya era posible en c++ y en otros lenguajes, pero


sorprendentemente java no lo incorpora, así que podemos decir que esta
característica es una ventaja de c# respecto a java, aunque mucha gente, esta
posibilidad, no lo considera una ventaja porque complica el código.

A la hora de hablar de operadores vamos a distinguir entre dos tipos, los


unarios y los binarios. Los unarios son aquellos que solo requieren un
operando, por ejemplo a++, en este caso el operando es 'a' y el operador
'++'. Los operadores binarios son aquellos que necesitan dos operadores, por
ejemplo a+c , ahora el operador es '+' y los operandos 'a' y 'c'. Es importante
esta distinción ya que la programación se hará de forma diferente.

Los operadores que podemos sobrecargar son los unarios, +, -, !, ~, ++, --;
y los binarios +, -, *, /, %, &, |, ^, <<, >>. Es importante decir que los
operadores de comparación, ==, !=, <, >, <=, >=, se pueden sobrecargar
pero con la condición que siempre se sobrecargue el complementario, es decir,
si sobrecargamos el == debemos sobrecargar el !=.
Sobrecargando operadores en la práctica

Para mostrar la sobrecarga vamos a usar el repetido ejemplo de los números


complejos, ( aunque también valdría el de las coordenadas cartesianas). Como
se sabe, los números complejos tienen dos partes, la real y la imaginaria,
cuando se suma dos números complejos su resultado es la suma de las dos
partes, para ello se va a crear una clase llamada ComplexNum que contendrá
ambas partes. Sin esta técnica no se podría sumar dos objetos de este tipo con
este práctico método, ya que esta clase no es válida como operando de los
operadores de c#.

Empecemos con el código de la clase de números complejos.

public class ComplexNum


{
private float img;
private float real;

// constructor de la clase
public ComplexNum(float real, float img)
{
this.real = real;
this.img = img;
}

// propiedad Real
public float Real{
get{
return real;
}
set{
real = value;
}
}

// propiedad Img
public float Img{
get{
return img;
}
set{
img = value;
}
}

// Sobrescribimos el miembro ToString heredado de Object


override public string ToString()
{
if ( img >= 0 )
return real + "+" + img +"i";
else
return real + "" + img + "i";
}

public static ComplexNum operator+(ComplexNum a, ComplexNum


b)
{
return new ComplexNum(a.real + b.real, a.img + b.img);
}

En el ejemplo hemos puesto la clase, con un constructor , dos propiedades


para obtener los datos privados de la clase y un método que nos transfoma el
número complejo a una cadena de caracteres para que se pueda visualizar
fácilmente. Finalmente hemos sobrecargado el operador '+', de forma que
podremos sumar dos números complejos como si se tratara de números
usuales.
Operadores binarios

Para empezar vamos a sobrecargar el operador suma('+') para que al sumar


dos objetos de la clase ComplexNum, es decir dos números complejos
obtengamos un número complejo que será la suma de ambas partes. Cabe
destacar que los prototipos para sobrecargar operadores serán:

public static Operando operator+(Operando a, Operando b)

Este es el prototipo para el operador +, el resto de operadores binarios van a


seguir el mismo patrón. Por tanto el código del método de sobrecarga será el
siguiente:

public static ComplexNum operator+(ComplexNum a,


ComplexNum b)
{
return new ComplexNum(a.Real + b.Real, a.Img + b.Img);
}

Este método sobrecarga el operador suma para que podamos sumar dos
números complejos. Un dato a tener en cuenta es que los métodos que
sobrecargan operadores deben ser static. Como se ve en el código los
operandos son 'a' y 'b', que se reciben como parámetro y el resultado de la
operación es otro número complejo que es el que retorna el método. Por tanto
se limita a crear un nuevo número complejo con ambas partes operadas. De
la misma forma podemos crear la sobrecarga del operador resta('-') para que
lleve a cabo la misma función:

public static ComplexNum operator-(ComplexNum a, ComplexNum


b)
{
return new ComplexNum(a.Real - b.Real, a.Img - b.Img);
}

Como vemos el método es idéntico solo que sustituyendo los + por -. En


este caso el trabajo que hacemos dentro del método es trivial pero podría ser
tan complejo como se quisiera.

Operadores Unarios

En esta sección se verá cómo sobrecargar los operadores unarios, es decir


aquellos que toman un solo operando, como por ejemplo a++. El prototipo
de los métodos que van a sobrecargar operadores unarios será:

public static Operando operator++(Operando a)

Como antes sustituyendo el ++ por cualquier operador unario. El ejemplo


dentro de nuestra clase de números complejos sería:

public static ComplexNum operator++(ComplexNum a)


{

float auximg = a.Img;


float auxreal = a.Real;

return new ComplexNum(++auxreal, ++auximg);


}

A primera vista puede quedar la duda si estamos sobrecargando la operación


++a o a++. Este aspecto se encarga el compilador de resolverlo, es decir, se
sobrecarga la operación ++ y el compilador se encarga de "sumar y asignar"
o "asignar y sumar". Este problema no ocurría en C++, cosa que teníamos
que manejar nosotros.

Como hemos dicho antes, la operación que hagamos dentro del método que
sobrecarga el operador es totalmente libre, se puede poner el ejemplo de
multiplicar dos matrices lo que es mas complejo que sumar dos números
complejos.

Capítulo 8

Estructuras

La lista de similitudes entre clases y estructuras es larga: las estructuras pueden


implementar interfaces, y pueden tener el mismo tipo de miembros que las
clases. Sin embargo, las estructuras difieren de las clases en algunos puntos
importantes: las estructuras son tipos por valor en lugar de tipos por
referencia, y no permiten la herencia. Los valores de las estructuras quedan
almacenados "en la pila" o "alineados". Los programadores cuidadosos
pueden a veces mejorar el rendimiento mediante un uso meditado de las
estructuras.

Por ejemplo, el uso de una estructura más bien que una clase para un Punto
puede producir una gran diferencia en el número de asignaciones producidas
en memoria en tiempo de ejecución. El siguiente programa crea e inicializa
un arreglo de 100 puntos. Con Punto implementado como clase, 101 objetos
separados son inicializados ( uno para el vector y uno para cada uno de los
100 elementos )

class Punto
{
public int x, y;
public Punto(int x, int y) {
this.x = x;
this.y = y;
}
}
class Test
{
static void Main() {
Punto[] Puntos = new Punto[100];
for (int i = 0; i < 100; i++)
{
Puntos[i] = new Punto(i, i*i);
}
}
}

Si Punto fuera implementado como una estructura, como en

struct Punto
{
public int x, y;
public Punto(int x, int y) {
this.x = x;
this.y = y;
}
}

únicamente un objeto es inicializado: el elemento del arreglo. Las instancias


de Punto se almacenan de forma alineada en el arreglo. Esta optimización
puede ser mal usada. Usar estructuras en lugar de clases también puede hacer
que una aplicación funcione más lento o utilice más memoria, pues pasar una
instancia de estructura por valor provoca que una copia de esa estructura sea
creada.
Rendimiento

Como hemos dicho, la principal ventaja por la que se usan en determinadas


circunstancias estructuras y no clases es que en circunstancias particulares
éstas demuestran mucho mejor rendimiento.

Capítulo 9

Interfaces

Definición

Una Interfaz es una colección de miembros abstractos relacionados


semánticamente. Una interfaz representa un comportamiento que una clase
dada puede soportar.

El número de miembros de una interfaz dependen del comportamiento que


queramos soportar, por ejemplo todos los objetos que sean móviles podrían
querer soportar los métodos acelerar y frenar.

Según la interfaz de C# una interfaz sería:

public interface IMovil


{
bool Acelerar(int n);
bool Frenar(int n);
}

También podríamos declarar dentro de la interfaz una propiedad que nos


permita leer y/o escribir la velocidad que queremos que tome nuestro objeto.

public interface IMovil


{
bool Velocidad{get; set;}
}

Dado que una interfaz es una colección de miembros abstractos cualquier


clase o estructura que quiera implementar una interfaz está obligada a
implementar cada uno de los métodos que se declaran en la interfaz. De esta
forma se consigue un cierto tipo de polimorfismo ya que si varias clases
implementan la misma estructura tenemos la posibilidad de tratar con todas
ellas de la misma forma.

Seguramente alguien se preguntara por que usar interfaces pudiendo usar una
clase base abstracta definiendo los métodos anteriores como abstractos, la
primera razón es simplicidad, una clase base abstracta suele hacer más que
definir una colección de métodos, es capaz de definir métodos públicos,
privados, protegidos y también metodos concretos (estáticos) a los que
pueden acceder todas las clases que deriven de ella mientras que una interfaz
se limita a definir una colección de métodos sin ninguna implementación. La
segunda razón es que C# solamente soporta herencia simple, pero sin
embargo podemos hacer que una clase implemente múltiples interfaces.

He aquí como haríamos para heredar de una clase base e implementar una
interfaz, teniendo en cuenta que VehiculoDeMotor será nuestra clase base e
IMovil nuestra interfaz.

public class CocheDeportivo : VehiculoDeMotor, IMovil


{
//Implementación de los métodos abstractos de vehículo

bool Acelerar(int n)
{
//implementación de Acelerar
}
bool Frenar(int n)
{
//implementación de Frenar
}
}
Hay que tener en cuenta que siempre hay que poner la clase base antes de las
interfaces.

Ahora nuestra clase CocheDeportivo así como cualquier otra clase que
implemente IMovil podra acelerar y frenar, hay que tener en cuenta que si
implementamos IMovil tendremos que implementar absolutamente todos sus
métodos.

Obteniendo Referencias a la Interfaz

Si hemos creado la clase CocheDeportivo podemos querer saber si éste


soporta el comportamiento de IMovil de modo que podemos hacer un cast
explícito:

CocheDeportivo coche1 = new CocheDeportivo();


IMovil movil = (IMovil) coche1;
movil.Acelerar(30);

En caso de que nuestro objeto implemente la interfaz podríamos operar sobre


él con todos los métodos de la misma, pero en caso de que no la soporte
tendríamos un error en tiempo de ejecución, con lo cual la forma correcta de
hacerlo es:

CocheDeportivo coche1 = new CocheDeportivo();


try{
IMovil movil = (IMovil) coche1;
movil.Acelerar(30);
}
catch(InvalidCastException e){
//gestión del error
}

Otra forma de hacerlo sin tener que recurrir a la gestión de excepciones sería
utilizando la palabra reservada as de C#:

CocheDeportivo coche1 = new CocheDeportivo();


IMovil movil;
movil = coche1 as IMovil;
if (movil != null)
movil.Frenar(10);
else
//otro tratamiento

La palabra reservada as pone la variable de tipo interfaz a null si la interfaz


dada no está soportada por el objeto.

Por último también podemos usar la palabra reservada is de C# para


descubrir si un objeto implementa o no una interfaz:

CocheDeportivo coche1 = new CocheDeportivo();


if (coche1 is IMovil)
coche1.Acelerar(10);
else
//otra gestión

Pasar interfaces como parámetros

Las interfaces son tipos de datos fuertemente tipados (valga la redundancia)


de modo que se pueden pasar como parámetros a métodos y se pueden usar
también como valores de retorno.

Hemos creado la interfaz IGiro de la siguiente manera:

public interface IGiro


{
void GirarDerecha(int grados);
void GirarIzquierda(int grados);
}

Y queremos que nuestro coche deportivo pueda girar a izquierda y derecha:

public class CocheDeportivo : VehiculoDeMotor, IMovil, IGiro


{
//Implementación de los métodos abstractos de vehículo
bool Acelerar(int n)
{
//Implementación de Acelerar
}
bool Frenar(int n)
{
//Implementación de frenar
}
void GirarDerecha(int grados)
{
//Implementación de GirarDerecha
}
void GirarIzquierda(int grados)
{
//Implementación de GirarIzquierda
}
}

Como hemos visto para soportar otra interfaz simplemente la añadimos al


final después de una ",".

Ahora supongamos que queremos hacer un método que nos provea de


utilidades para el giro por ejemplo hacer trompos, le podríamos pasar una
interfaz IGiro de la siguiente forma

public class UtilsGiro


{
public static void Trompo(IGiro giro)
{
giro.GirarIzquierda(360);
}
}

Implementación Explícita de una Interfaz


Siguiendo con nuestro ejemplo de los coches definimos una nuevas interfaces:

public interface IAltaVelocidad


{
void Turbo(bool activar);
//resto de metodods
}

Como se puede ver nuestra interfaz implementa un método Turbo. ¿Qué


pasaría si una clase heredase a su vez de la clase Formula Uno que también
implemente el método void Turbo (bool)? Bueno vamos a verlo:

public class Campeon : Formula1, IAltaVelocidad


{
public override void Turbo(bool activar)
{
//gestion del turbo
}
}

Esto en un principio sería correcto pero qué pasa si hacemos lo siguiente:

Campeon miCampeon = new Campeon();


miCampeon.Turbo(true);

IAltaVelocidad iav = (IAltaVelocidad) miCampeon;


iav.Turbo(true);

Ambas veces se llamaría al mismo método, el definido en la clase Formula1,


pero como haríamos si quisiéramos tener dos Turbos diferentes? la respuesta
es hacer que los métodos definidos en la interfaz sean sólo accesibles desde
una referencia a la interfaz, esto es lo que se llama implementación explícita
de una interface.

public class Campeon : Formula1, IAltaVelocidad


{
public override void Turbo(bool activar)
{
//gestión del turbo
}

void IAltaVelocidad.Turbo(bool activar)


{
//gestión del turbo
}
}

El segundo método sólo podrá ser llamado si usamos una referencia de tipo
IAltaVelocidad mientras que el primero podrá ser llamado usando una
referencia a Campeon o a Formula1 (su clase base).

Existen algunas reglas extra al hacer esto, por ejemplo no podemos usar
modificadores de accesibilidad (public, private, protected) ya que si
intentamos que sólo se pueda acceder al método desde una referencia a la
interfaz hacerlo sería contraproducente.

También hay que tener en cuenta que pueden haber colisiones de nombres
entre clases base e interfaces y entre interfaces entre sí, técnicamente no existe
ninguna diferencia y todas pueden ser tratadas como hemos explicado arriba.

Jerarquías de interfaces

Las interfaces pueden servir de base para otras interfaces al igual que las clases,
e igual que en éstas la idea es que vayamos de lo general a lo particular.

Por ejemplo:

interface IVehiculo
{
void Acelerar();
void Frenar();
}
interface IVehiculoGasolina : IVehiculo
{
void CambiarVelocidadInyeccion(int velocidad);
}
interface IVehiculo4x4: IVehiculoGasolina
{
void Activar4x4(bool activar);
}

Al implementar una de estas interfaces en nuestra clase tenemos que


implementar todos los métodos de esta interfaz y de sus ancestros.

public class CocheDeJuguete : IVehiculo


{
void IVehiculo.Acelerar(int n)
{
//Gestión del acelerado
}
void IVehiculo.Frenar(int n)
{
//Gestión del frenado
}
}
public class CocheNormal:IVehiculoGasolina
{
void IVehiculo.Acelerar(int n)
{
//Gestión del acelerado
}
void IVehiculo.Frenar(int n)
{
//Gestión del frenado
}
void IVehiculoGasolina.CambiarVelocidadInyeccion(int velocidad)
{
//Gestión de la inyeccion
}
}
public class TodoTerreno:IVehiculo4x4
{

void IVehiculo.Acelerar(int n)
{
//Gestión del acelerado
}
void IVehiculo.Frenar(int n)
{
//Gestión del frenado
}
void IVehiculoGasolina.CambiarVelocidadInyeccion(int velocidad)
{
//Gestión de la inyeccion
}
void IVehiculo4x4.Activar4x4(bool activar)
{
//Gestión de 4x4
}
}

Y lógicamente las llamadas a los métodos serían:

TodoTerreno miTodoTerreno = new TodoTerreno();


((IVehiculo4x4)miTodoTerreno).Acelerar(20);
((IVehiculo4x4)miTodoTerreno).Frenar(20);
((IVehiculo4x4)miTodoTerreno).CambiarVelocidadInyeccion(1000);
((IVehiculo4x4)miTodoTerreno).Activar4x4(true);
Capítulo 10

Estructuras de datos

Hay varias formas de agrupar conjuntos de datos en C#.

Enumeraciones

La palabra clave enum se utiliza para declarar una enumeración, un tipo que
consiste en un conjunto de constantes con el nombre de lista de
enumeradores. Cada tipo de enumeración tiene un tipo subyacente, pudiendo
estos ser cualquier tipo integral menos char. El tipo predeterminado de los
elementos de la enumeración es int. Por defecto el primer enumerador tiene
el valor 0 y el valor sucesivo se incrementa en 1. Ejemplo:

public enum Dias


{
Domingo,
Lunes,
Martes,
Miercoles,
Jueves,
Viernes,
Sabado
}

En esta enumeración Domingo es 0, Lunes es 1 y así sucesivamente. Los


enumeradores pueden tener inicializadores reemplazando estos a los valores
predeterminados, por ejemplo:

public enum Dias


{
Domingo = 1,
Lunes,
Martes,
Miercoles,
Jueves,
Viernes,
Sabado
}

En este caso obligamos a la enumeración a comenzar desde 1 en lugar de 0.

Dijimos que el tipo subyacente de las enumeraciones es int, sin embargo se


necesita una conversión explícita para pasar un valor enum a un valor int, así:

int x = (int)Dias.Lunes;

En este caso el resultado sería que x valdría 2.

Estructuras

Capítulo 11
1. C sharp NET / Capítulo 11
Capítulo 12

Manejo de excepciones

C# proporciona soporte integrado para el manejo de excepciones o decir


situaciones anormales de funcionamiento, las cuales pueden ocurrir en
cualquier momento durante la ejecución del programa y son manejadas por
el código que se encuentra fuera del flujo normal de control.

Todo esto gracias a las instrucciones clave try, throw, catch y finally. C#
proporciona una solución estructurada tanto a nivel del sistema como de
aplicación. A pesar de que es muy similar a C++ en cuanto al manejo de
excepciones existen varias diferencias, entre ellas que cada excepción esta
representada por una instancia de un tipo de clase derivado de
System.Exception. en realidad es algo bastante simple:

try
{
//haz esto...
//si i = 0 throw una excepción
}
catch
{
//si falló haz esto...
}
finally
{
//haya fallado o no, haz esto...
}

Como se puede apreciar el manejo de excepciones es bastante sencillo y fácil


de entender aunque no tengamos mucha experiencia programando, todo
aquello que se puso entre las {} del try es un segmento de código en el que
puede o no generarse un error en tiempo de ejecución(excepción), en caso de
que haya habido un funcionamiento anómalo en el programa (excepción), la
ejecución del código entra en el segmento de código catch y ejecuta el bloque
de instrucciones que hemos definido para manejar ese error, finalmente el
flujo del programa,

haya o no habido excepción, entra en finally; aquí podemos poner rutinas


para marcar los objetos que ya no se utilizarán de manera que el recolector de
basura pueda liberar la memoria que dichos objetos ocupaban, rutinas que
guarden un log de la aplicación para llevar un control de ¿cuántas veces ha
fallado?, ¿por qué fallo?, etc.,

todo bloque try puede tener uno o más catch para tratar cada una de las
posibles excepciones, pero la flexibilidad de C# va más allá de eso, ya que
nos permite lanzar nuestras propias excepciones, por ejemplo si un método
no recibe un valor que debe recibir o recibe un valor que no puede procesar
podemos lanzar nuestra propia excepción. Ejemplo:

using System;
public class ManejoExcepciones
{
public static void Main()
{
try //Le hacemos saber que puede ocurrir un error
{
string s=null; //Declaramos un string y lo dejamos vacío
if (s == null) //Si el string esta vacio
throw(new ExcepcionCadenaVacia()); //Lanzamos un error
personalizado
Console.WriteLine("Esto nunca se imprime"); //Si hay error el código
sale de la ejecución normal
//es por eso que esta parte nunca se ejecuta
}
catch( ExcepcionCadenaVacia e ) //Atrapamos nuestro error
{
Console.WriteLine("La cadena esta vacia"); //Manejamos el error
}
finally
{
Console.WriteLine("Esto siempre se imprime"); //Esto siempre se
ejecutará
}
}
}

Capítulo 13

Delegación y Eventos

Programando con delegados

Otra cuestión a tener en cuenta cuando programemos con delegados, es que


éstos no tienen en cuenta la visibilidad de los métodos. Esto permite llamar a
métodos privados desde otros si ambos tienen acceso al delegado. Es decir,
imaginemos que una clase guarda en un delegado referencia a uno de sus
métodos privados. Si desde otra clase que tenga acceso al delegado (pero no
al método privado) se convoca a éste, se ejecutará ese método. En verdad no
se está violando la privacidad del método, porque no es la clase quien lo
convoca, sino el delegado, que sí tiene acceso al mismo.

Llamadas a múltiples métodos

Hasta el momento hemos visto como hacer que un delegado guarde referencia
de un sólo método. Sin embargo, existe una clase, System.MulticastDelegate,
que deriva de System.Delegate, que se diferencia de esta última en que puede
tener múltiples métodos en su lista de invocaciones. Al usar la palabra clave
"delegate" se están usando estas clases de forma interna, sin tener que hacer
referencia a ellas en el código.

Para poder hacer esto usaremos los operadores sobrecargados '+=' y '-=' que,
respectivamente, añaden o eliminan a un método de la lista de invocaciones
de un delegado.

Para intentar asimilar esto mejor, veámoslo con un ejemplo más completo.

Ejemplo de uso de delegados

using System;

// Define el tipo delegado


delegate void MiDelegado (string cadena);

class Ejemplo {

public static MiDelegado delegado;

public static void Main () {

// Crea una un objeto delegado y le asigna un método.


delegado = new MiDelegado (ClaseA.MetodoEstatico);

ClaseA A = new ClaseA();


delegado += new MiDelegado (A.MetodoPublico);

ClaseB B = new ClaseB(); // El constructor inserta MetodoPrivado

// delegado += new MiDelegado (B.MetodoNoValido); // Excepción


// el método no cumple con la definición del tipo delegado

delegado ("Hola mundo");

class ClaseA {

public static void MetodoEstatico (string cad) {

Console.WriteLine ("ClaseA.MetodoEstatico ha sido llamado: {0}",


cad);

public void MetodoPublico (string cad) {

Console.WriteLine ("ClaseA.MetodoPublico ha sido llamado: {0}",


cad);
}

class ClaseB {

void MetodoPrivado (string cad) {

Console.WriteLine ("ClaseB.MetodoPrivado ha sido llamado: {0}", cad);

public void MetodoNoValido (int entero) {

Console.WriteLine ("ClaseB.MetodoNoValido ha sido llamado: {0}",


entero);

public ClaseB () {

Ejemplo.delegado += new MiDelegado (MetodoPrivado);

}
Podemos ver que, en este caso, nuestro delegado sólo manipula métodos que
no devuelvan nada y que reciban como único parámetro una cadena. Si
observamos los métodos que componen ClaseA y ClaseB, el denominado
MetodoNoValido no concuerda con la definición de nuestro delegado, ya
que recibe un entero y no una cadena. Eso implica que no vamos a poder
llamarlo desde ninguna instancia del delegado que hemos declarado. Sin
embargo, con las otras no tendremos ningún problema.

Bien, observemos paso a paso lo que hace el programa. Fijemos nuestra


atención en el método principal (Main). Primero insertamos un método
estático. Como sabemos, para llamar a un método de este tipo, se hace a partir
del nombre de la clase y no de una instancia. Bien, hasta aquí nada que no
hayamos visto ya, pero ahora insertemos un segundo módulo en el delegado.

Como hemos dicho, hemos usado el operador '+=' para incluir otro método
más en nuestro delegado, en este caso MetodoPublico. Si usaramos de nuevo
el operador '=', borraríamos la antigua lista de invocaciones y crearíamos una
nueva con sólo una función referenciada. Ahora tenemos dos métodos en la
lista de invocaciones de nuestro delegado. Por último, creamos una instancia
de ClaseB, la cual en su constructor incluye una referencia más al delegado,
en este caso a MetodoPrivado.

Si ahora compilamos y ejecutamos este código, obtendremos esto:

ClaseA.MetodoEstatico ha sido llamado: Hola mundo

ClaseA.MetodoPublico ha sido llamado: Hola mundo

ClaseB.MetodoPrivado ha sido llamado: Hola mundo

Como vemos, aunque hemos convocado al delegado desde la clase Ejemplo,


que no tiene acceso a MetodoPrivado, éste ha sido ejecutado. Como
explicamos, esto es así porque realmente quien lo está haciendo es el delegado
y no el método Main.

Por último, una cuestión más. Hasta el momento hemos visto a delegados
que gestionan miembros que no devuelven ningún valor. Pero, ¿qué ocurre
cuando los devuelven? En este caso, la ejecución del delegado no devuelve
todos esos valores, sólo el que retorne el último método de su lista de
invocación.

Capítulo 14
C sharp NET/Capítulo 14

Capítulo 15
C sharp NET/Capítulo 15

Capítulo 16
C sharp NET/Capítulo 16

Capítulo 17

Programando aplicaciones WEB con ASP.NET usando C#

.NET provee un rica infraestructura para diseñar no solamente programas de


escritorio sino también páginas y aplicaciones Web usando la tecnología
llamada ASP.NET. Como hemos dicho anteriormente, el lenguage de
programación C# fue diseñado precisamente pensando en el Internet; debido
a esto, C# es el lenguage de preferencia para programar éstas aplicaciones.
Por supuesto que también usaremos en conjunto con C# los lenguajes
tradicionales para el Web como son el HTML, XML y Javascript para
enriquecer nuestras aplicaciones. Con estas tecnologías a nuestro alcance
podremos crear lo que necesitemos de una manera rápida y sencilla.
ASP.NET es la más avanzada tecnología para crear aplicaciones y páginas
Web sobrepasando a tecnologías populares como PHP o JSP en varios
aspectos como rapidez, sencillez, seguridad e incluso costo (ya que existe el
proyecto MONO www.mono-project.com que proporciona una versión de
ASP.NET [incluyendo un servidor] de forma gratuita). Cuando Microsoft
lanzó al mercado dicha tecnología, fue acogida rápidamente debido a su
facilidad de uso y a su poder. Ésta tecnología alivia inmensamente al
programador de tener que crear código desde cero.

ASP.NET cuenta con una inmensa colección de herramientas tanto controles


como clases y funciones para crear aplicaciones rápidamente. Se dice que por
lo general ASP.NET le ahorra al programador crear de 50% a 70% del
código que se necesita ya que viene actualmente incorporado y listo para
usarse.

ASP.NET 2.0 es la nueva versión de ASP.NET, a decir verdad no incluye


grande cambios sobre la versión 1.1 pero si se ha puesto énfasis en crear más
controles, se han reorganizado y optimizado algunas funciones haciendo que
ASP.NET sea más seguro, flexible y robusto. En este capítulo aprenderemos
como utilizar para nuestro beneficio ésta gran tecnología.

 Introducción a ASP.NET 2.0


 El modelo de programación de ASP.NET
 Creando aplicaciones usando Visual Studio 2005 y MonoDevelop
0.11
 Lo básico de una página
 Controles
 Creando y depurando páginas
 Accediendo y manipulando información
 Contenedores de Datos
 Enlazando datos
 Usando controles para desplegar información
 Creando páginas altamante interactivas usando AJAX
 Temas avanzados
 El futuro de ASP.NET, Silverlight y aplicaciones para el Web usando
WPF

Capítulo 18
C sharp NET/Capítulo 18

Capítulo 19
XML

Cuando Microsoft lanzó las primeras versiones de su plataforma .NET, nos


sorprendió a todos por su apuesta por los estándares, lo cual contrastaba
claramente con prácticas anteriores.

El estándar más importante adoptado en la plataforma .NET es sin duda


XML (eXtensible Markup Language, Lenguaje Extensible de Marcas). XML
es una tecnología tan integrada en .NET, que la propia plataforma utiliza
XML internamente para sus archivos de configuración y para su propia
documentación. XML es, por lo tanto, una tecnología con una importancia
fundamental en .NET.

Conceptos básicos de XML

XML es un lenguaje para estructurar datos. Sirve, por ejemplo, para


almacenar en un archivo de texto una hoja de cálculo, una libreta de
direcciones o un dibujo vectorial. XML hace mucho más fácil al ordenador
el proceso de generar datos, leer los datos y asegurarse de que la estructura de
los datos no es ambigua. A su vez, contiene características que aseguran su
validez durante mucho tiempo: es ampliable, independiente de plataforma,
no pertenece a ninguna firma concreta de software y soporta
internacionalización.
El aspecto de un archivo XML es muy similar al de un archivo HTML, con
texto encerrado entre etiquetas. Las etiquetas no son más que palabras
rodeadas por < y >. El siguiente podría ser un ejemplo de archivo XML que
describe a la persona que escribe éste tutorial.

<?xml version="1.0"?>
<persona>
<nombre>Fabian</nombre>
<apellido>Seoane</apellido>
<organizacion>Mono Hispano</organizacion>
<pagina>http://fseoane.net</pagina>
</persona>

La diferencia fundamental con HTML es que XML no tiene etiquetas


predefinidas, sino que éstas dependen de la implementación. Por ejemplo, en
el archivo que acabamos de ver, las etiquetas disponibles podrían ser persona,
nombre, apellido, ..., mientras que en un archivo XML que describa una
librería, las etiquetas podrían ser ensamblado, clase, método, etc.

Todo ésto podría parecer un lío increíble si no se dispusiera de un mecanismo


para traducir entre los diversos formatos XML, por ejemplo, entre el archivo
persona y un archivo HTML que muestra la información sobre la persona.
Por fortuna, esto existe, se llaman hojas XSL y la librería de clases dispone
de métodos para transformar entre XML a partir de hojas XSL.

Si quieres saber más sobre XML te sugiero que mires las siguientes páginas:
http://w3.org/XML/, http://xml.com

XML en .Net

A lo largo de este capítulo utilizaremos de forma extensiva el espacio de


nombres System.Xml. Los estándares cubiertos por este espacio de nombres
son:
 XML 1.0 - http://www.w3.org/TR/1998/REC-xml-19980210 -
includyendo soporte para DTDs.

 XML Namespaces - http://www.w3.org/TR/REC-xml-names/ -


tanto stream level como DOM.

 XSD Schemas - http://www.w3.org/2001/XMLSchema

 XPath expressions - http://www.w3.org/TR/xpath

 XSLT transformations - http://www.w3.org/TR/xslt

 DOM Level 1 Core - http://www.w3.org/TR/REC-DOM-Level-


1/

 DOM Level 2 Core - http://www.w3.org/TR/DOM-Level-2/

Escribir un archivo XML

Comenzaremos por escribir un sencillo archivo XML. En éste caso será el


ejemplo que acabamos de utilizar.

El código siguiente crea un documento llamado ejemplo.xml con un


documento xml como el del ejemplo anterior.

using System;
using System.Xml;

class EjemploXml{

public static void Main()


{
XmlTextWriter writer = new XmlTextWriter("ejemplo.xml",
System.Text.Encoding.UTF8);

//Usa indentación por legibilidad


writer.Formatting = Formatting.Indented;

//Escribe la declaración del XML


writer.WriteStartDocument();

//Escribe el elemento raiz


writer.WriteStartElement("libro");

//Escribe los elementos dentro de sus etiquetas


writer.WriteElementString("titulo", "El lenguaje C#");
writer.WriteElementString("dificultad", "media");
writer.WriteElementString("organizacion", "Mono Hispano -
wikilibros");

writer.WriteEndElement();

writer.Flush();
writer.Close();
}
}

El cual creará un archivo ejemplo.xml con el siguiente aspecto:

<?xml version="1.0" encoding="utf-8"?>


<libro>
<titulo>El lenguaje C#</titulo>
<dificultad>media</dificultad>
<organizacion>Mono Hispano - wikilibros</organizacion>
</libro>
Capítulo 20
C sharp NET/Capítulo 20

Capítulo 21
C sharp NET/Capítulo 21

Capítulo 22
Programando en Redes

En la actualidad, existe la tendencia cada vez mas firme, de que en algún


momento un programa requiera obtener datos y/o realizar acciones a través
de una red local o incluso Internet.

Un ejemplo simple, puede ser un simple widget, que accede a un sitio web
para brindarnos la cotización de la moneda, la temperatura actual en nuestra
ciudad e incluso el pronóstico meteorológico para los proxmos días.

Obtener esta información, requiere establecer una conexión a un sitio


determinado, acceder a un servicio determinado y traer esa información a
nuestro computador.

Veremos algunos conceptos fundamentales sobre redes, para luego mencionar


como un programa hace uso de esos elementos y en particular se explicará
como realizarlo con C#.

Continuar explicando los conceptos del modelo OSI de 7 capas, situarnos en


la capa de aplicación. Explicar el concepto de dirección y puertos. Mostrar
como una conexión se establece hacia una dirección y puerto determinado.
Mencionar los puertos estándares: 80=HTTP, 21=FTP, etc.
Capítulo 23
C sharp NET/Capítulo 23

Capítulo 24
C sharp NET/Capítulo 24

Capítulo 25
C sharp NET/Capítulo 25

Capítulo 26

Utilizando Bases de Datos

Por lo general tendremos que acceder a bases de datos tanto locales como
remotas, por eso .NET define distintos tipos que nos ayudan en esta tarea,
estos namespaces se conocen como ADO.NET que no es mas que una mejora
del tradicional ADO.

Diferencias entre ADO y ADO.NET

ADO.NET además de ser una mejora de ADO intenta implementar un nuevo


paradigma para el desarrollo de sistemas desconectados sobre todo
centrándose en el desarrollo de lo que en inglés se llama aplicaciones N-Tier
(aplicaciones en n capas) estas son generalmente aplicaciones web que
actualmente están en expansión.
Una de las nuevas criaturas de ADO.NET es el DataSet que representa un
conjunto de tablas y relaciones de manera desconectada de la fuente de datos
de modo que podamos interactuar con los datos, modificarlos y devolverlos
a la fuente primaria usando el ya conocido data adapter.

Otra de las grandes características añadidas es el soporte completo para XML,


los datos usan internamente esta representación para moverse entre las capas
de aplicación a traves de HTTP por ejemplo.

Finalmente nos queda por destacar que ADO.NET es una librería manejada
(managed library) lo que hace que su uso sea exactamente igual en cualquiera
de los lenguajes soportados por .NET.

ADO.NET a grandes rasgos

Los tipos que componen ADO.NET tienen por objetivo obtener datos de la
base de datos rellenar un DataSet, manipular dicho elemento y devolver los
datos manipulados a la base de datos, con la particularidad de que dentro del
DataSet podemos tener la representación de tablas y relaciones entre ellas
hasta llegar a tener una representación completa de toda una base de datos.

Para poder trabajar correctamente con un DataSet están definidas dentro del
namespace System.Data algunas interfaces como son IDbCommand,
IDbDataAdapter, IDbConnection y IDataReader.

Hay dos proveedores que vienen con .Net predeterminados SQL que permite
acceder a SQL Server 7.0 o superior y OleDb que permite acceder a cualquier
base de datos que soporte OLE DB, también es común encontrarnos con
proveedores para bases de datos Oracle por ejemplo.

Capítulo 27
C sharp NET/Capítulo 27
Capítulo 28

Manejo de puertos con Mono y C#

Introducción

El objetivo de este capitulo es mostrar como usar la implementación de


System.IO.Ports, que hace posible leer y escribir en un puerto serie (RS-232)
con Mono.

Limitaciones

En este momento existen una serie de limitaciones a tomar en cuenta.

1) No hay un evento que notifique la recepción de datos. Si deseas recibir


información, hay que establecer un tiempo de espera (timeout) y observar si
se reciben datos con ReadByte() cuando consideres que debe estar enviando
información. 2) Debe leerse los datos en un vector de bytes (byte[]) ya que
no hay soporte para vectores de caracteres (char[]) o strings, por lo que se
debe traducir los bytes recibidos al encodig deseado. 3) Las funciones
DiscardNull, ParityReplace, ReceivedBytesThreshold no están
implementadas.

Código de ejemplo

Este codigo puede usarse para probar una conexión via un MODEM serial
conectado al puerto /dev/ttyS0

using System;
using System.IO.Ports;

public class SerialPortTest


{
public static void Main(string[] args)
{
SerialPortTest myTest = new SerialPortTest();
myTest.Test();
}

private SerialPort mySerial;

// Constructor
public SerialPortTest()
{
}

public void Test()


{
if (mySerial != null)
if (mySerial.IsOpen)
mySerial.Close();

mySerial = new SerialPort("/dev/ttyS0", 38400);


mySerial.Open();
mySerial.ReadTimeout = 400;
SendData("ATI3\r");

// Should output some information about your modem firmware


Console.WriteLine(ReadData());
}

public string ReadData()


{
byte tmpByte;
string rxString = "";

tmpByte = (byte) mySerial.ReadByte();


while (tmpByte != 255) {
rxString += ((char) tmpByte);
tmpByte = (byte) mySerial.ReadByte();
}

return rxString;
}

public bool SendData(string Data)


{
mySerial.Write(Data);
return true;
}
}

Grabe este programa en un archivo con extensión .cs (por ejemplo


SerialExample.cs).

Entonces compílelo con la siguiente instrucción:

gmcs SerialExample.cs

Nota. Es importante que use gmcs en lugar mcs

Y puede ejecutar el resultado de la siguiente manera:

mono SerialExample.exe

Suponiendo que esté conectado un MODEM serial, el programa deberá


registrar la siguiente salida:

$ mono SerialExample.exe
Sportster 14,400/FAX RS Rev. 1.5
OK

Ahora puede acceder su dispositivo serie en Linux desarrollando con Mono


Puntos a considerar

Un punto clave a tener en cuenta es que el usuario que ejecute la aplicación


debe de tener permisos sobre el fichero del controlador del puerto, esto se
verifica ejecutando la siguiente instrucción dentro de la consola.

ls -l /dev/ttyS0

Donde /dev/ttyS0 es el puerto con el que quieres establecer comunicación


(no hay que perder de vista que en Linux los puertos son tratados como
archivos)

El resultado del comando será una lista de los permisos sobre el puerto, por
ejemplo

$ ls -l /dev/ttyS0
crw-rw---- 1 root dialout 4, 64 2007-05-25 10:28 /dev/ttyS0

Donde las letras tienen los siguientes significados:

c - Dispositivo de caracteres (tty o impresora) r - Permiso de lectura w -


Permiso de escritura

Esta es la estructura de los permisos

-rw- rw- r--


|||| ||| |||-----------> Acceso para alguien que no es dueño (other)
|||| ||| ----------------> Acceso para miembros del grupo (group)
|||| ----------------------> Acceso para el dueño (user)
|-------------------------> Tipo de archivo (archivo, directorio, dispositivo,
etc)

Si el usuario no tiene permisos se lanza una excepción IOException, por lo


que es importante validar los permisos asignados con el usuario que se ejecuta
la aplicación.

Links relacionados
Aquí hay unos links que podrían ser de utilidad. .NET Serial Port Library
(http://sourceforge.net/projects/serialportnet/) Este proyecto tiene como
objetivo implementar el namespace System.IO.Ports por completo para el
framework 1.1 de .NET.

Capítulo 29

Sockets

Introducción a los Sockets

Los sockets dotan a las aplicaciones la capacidad de comunicación en red.

Sincrónico y Asincrónico

Clase Socket

using System.Net.Sockets;

Servidor

Cliente

GNU Free Documentation License

Version 1.2, November 2002

Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.


51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other


functional and useful document "free" in the sense of freedom: to assure
everyone the effective freedom to copy and redistribute it, with or without
modifying it, either commercially or noncommercially. Secondarily, this
License preserves for the author and publisher a way to get credit for their
work, while not being considered responsible for modifications made by
others.

This License is a kind of "copyleft", which means that derivative works of


the document must themselves be free in the same sense. It complements the
GNU General Public License, which is a copyleft license designed for free
software.

We have designed this License in order to use it for manuals for free software,
because free software needs free documentation: a free program should come
with manuals providing the same freedoms that the software does. But this
License is not limited to software manuals; it can be used for any textual
work, regardless of subject matter or whether it is published as a printed
book. We recommend this License principally for works whose purpose is
instruction or reference.

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be distributed
under the terms of this License. Such a notice grants a world-wide, royalty-
free license, unlimited in duration, to use that work under the conditions
stated herein. The "Document", below, refers to any such manual or work.
Any member of the public is a licensee, and is addressed as "you". You accept
the license if you copy, modify or distribute the work in a way requiring
permission under copyright law.

A "Modified Version" of the Document means any work containing the


Document or a portion of it, either copied verbatim, or with modifications
and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the


Document that deals exclusively with the relationship of the publishers or
authors of the Document to the Document's overall subject (or to related
matters) and contains nothing that could fall directly within that overall
subject. (Thus, if the Document is in part a textbook of mathematics, a
Secondary Section may not explain any mathematics.) The relationship could
be a matter of historical connection with the subject or with related matters,
or of legal, commercial, philosophical, ethical or political position regarding
them.

The "Invariant Sections" are certain Secondary Sections whose titles are
designated, as being those of Invariant Sections, in the notice that says that
the Document is released under this License. If a section does not fit the
above definition of Secondary then it is not allowed to be designated as
Invariant. The Document may contain zero Invariant Sections. If the
Document does not identify any Invariant Sections then there are none.

The "Cover Texts" are certain short passages of text that are listed, as Front-
Cover Texts or Back-Cover Texts, in the notice that says that the Document
is released under this License. A Front-Cover Text may be at most 5 words,
and a Back-Cover Text may be at most 25 words.

A "Transparent" copy of the Document means a machine-readable copy,


represented in a format whose specification is available to the general public,
that is suitable for revising the document straightforwardly with generic text
editors or (for images composed of pixels) generic paint programs or (for
drawings) some widely available drawing editor, and that is suitable for input
to text formatters or for automatic translation to a variety of formats suitable
for input to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart or
discourage subsequent modification by readers is not Transparent. An image
format is not Transparent if used for any substantial amount of text. A copy
that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII


without markup, Texinfo input format, LaTeX input format, SGML or
XML using a publicly available DTD, and standard-conforming simple
HTML, PostScript or PDF designed for human modification. Examples of
transparent image formats include PNG, XCF and JPG. Opaque formats
include proprietary formats that can be read and edited only by proprietary
word processors, SGML or XML for which the DTD and/or processing
tools are not generally available, and the machine-generated HTML,
PostScript or PDF produced by some word processors for output purposes
only.

The "Title Page" means, for a printed book, the title page itself, plus such
following pages as are needed to hold, legibly, the material this License
requires to appear in the title page. For works in formats which do not have
any title page as such, "Title Page" means the text near the most prominent
appearance of the work's title, preceding the beginning of the body of the
text.

A section "Entitled XYZ" means a named subunit of the Document whose


title either is precisely XYZ or contains XYZ in parentheses following text
that translates XYZ in another language. (Here XYZ stands for a specific
section name mentioned below, such as "Acknowledgements",
"Dedications", "Endorsements", or "History".) To "Preserve the Title" of
such a section when you modify the Document means that it remains a
section "Entitled XYZ" according to this definition.

The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this License, but
only as regards disclaiming warranties: any other implication that these
Warranty Disclaimers may have is void and has no effect on the meaning of
this License.

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the copyright
notices, and the license notice saying this License applies to the Document
are reproduced in all copies, and that you add no other conditions whatsoever
to those of this License. You may not use technical measures to obstruct or
control the reading or further copying of the copies you make or distribute.
However, you may accept compensation in exchange for copies. If you
distribute a large enough number of copies you must also follow the
conditions in section 3.

You may also lend copies, under the same conditions stated above, and you
may publicly display copies.

3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have printed
covers) of the Document, numbering more than 100, and the Document's
license notice requires Cover Texts, you must enclose the copies in covers
that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on
the front cover, and Back-Cover Texts on the back cover. Both covers must
also clearly and legibly identify you as the publisher of these copies.

The front cover must present the full title with all words of the title equally
prominent and visible. You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve the title
of the Document and satisfy these conditions, can be treated as verbatim
copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you
should put the first ones listed (as many as fit reasonably) on the actual cover,
and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more
than 100, you must either include a machine-readable Transparent copy
along with each Opaque copy, or state in or with each Opaque copy a
computer-network location from which the general network-using public has
access to download using public-standard network protocols a complete

Transparent copy of the Document, free of added material. If you use the
latter option, you must take reasonably prudent steps, when you begin
distribution of Opaque copies in quantity, to ensure that this Transparent
copy will remain thus accessible at the stated location until at least one year
after the last time you distribute an Opaque copy (directly or through your
agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to give them
a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release the
Modified Version under precisely this License, with the Modified Version
filling the role of the Document, thus licensing distribution and modification
of the Modified Version to whoever possesses a copy of it. In addition, you
must do these things in the Modified Version:

A. Use in the Title Page (and on the covers, if any) a title distinct from
that of the Document, and from those of previous versions (which
should, if there were any, be listed in the History section of the
Document). You may use the same title as a previous version if the
original publisher of that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified
Version, together with at least five of the principal authors of the
Document (all of its principal authors, if it has fewer than five), unless
they release you from this requirement.
C. State on the Title page the name of the publisher of the Modified
Version, as the publisher.

D. Preserve all the copyright notices of the Document.

E. Add an appropriate copyright notice for your modifications adjacent


to the other copyright notices.

F. Include, immediately after the copyright notices, a license notice


giving the public permission to use the Modified Version under the
terms of this License, in the form shown in the Addendum below.

G. Preserve in that license notice the full lists of Invariant Sections and
required Cover Texts given in the Document's license notice.

H. Include an unaltered copy of this License.

I. Preserve the section Entitled "History", Preserve its Title, and add
to it an item stating at least the title, year, new authors, and publisher
of the Modified Version as given on the Title Page. If there is no
section Entitled "History" in the Document, create one stating the
title, year, authors, and publisher of the Document as given on its Title
Page, then add an item describing the Modified Version as stated in
the previous sentence.

J. Preserve the network location, if any, given in the Document for


public access to a Transparent copy of the Document, and likewise the
network locations given in the Document for previous versions it was
based on. These may be placed in the "History" section. You may omit
a network location for a work that was published at least four years
before the Document itself, or if the original publisher of the version
it refers to gives permission.
K. For any section Entitled "Acknowledgements" or "Dedications",
Preserve the Title of the section, and preserve in the section all the
substance and tone of each of the contributor acknowledgements
and/or dedications given therein.

L. Preserve all the Invariant Sections of the Document, unaltered in


their text and in their titles. Section numbers or the equivalent are not
considered part of the section titles.

M. Delete any section Entitled "Endorsements". Such a section may


not be included in the Modified Version.

N. Do not retitle any existing section to be Entitled "Endorsements"


or to conflict in title with any Invariant Section.

O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or appendices


that qualify as Secondary Sections and contain no material copied from the
Document, you may at your option designate some or all of these sections as
invariant. To do this, add their titles to the list of Invariant Sections in the
Modified Version's license notice. These titles must be distinct from any
other section titles.

You may add a section Entitled "Endorsements", provided it contains


nothing but endorsements of your Modified Version by various parties--for
example, statements of peer review or that the text has been approved by an
organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a


passage of up to 25 words as a Back-Cover Text, to the end of the list of
Cover Texts in the Modified Version. Only one passage of Front-Cover Text
and one of Back-Cover Text may be added by (or through arrangements
made by) any one entity. If the Document already includes a cover text for
the same cover, previously added by you or by arrangement made by the same
entity you are acting on behalf of, you may not add another; but you may
replace the old one, on explicit permission from the previous publisher that
added the old one.

The author(s) and publisher(s) of the Document do not by this License give
permission to use their names for publicity for or to assert or imply
endorsement of any Modified Version.

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified versions,
provided that you include in the combination all of the Invariant Sections of
all of the original documents, unmodified, and list them all as Invariant
Sections of your combined work in its license notice, and that you preserve
all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and multiple
identical Invariant Sections may be replaced with a single copy. If there are
multiple Invariant Sections with the same name but different contents, make
the title of each such section unique by adding at the end of it, in parentheses,
the name of the original author or publisher of that section if known, or else
a unique number. Make the same adjustment to the section titles in the list
of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled "History" in


the various original documents, forming one section Entitled "History";
likewise combine any sections Entitled "Acknowledgements", and any
sections Entitled "Dedications". You must delete all sections Entitled
"Endorsements."

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this License
in the various documents with a single copy that is included in the collection,
provided that you follow the rules of this License for verbatim copying of
each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it
individually under this License, provided you insert a copy of this License
into the extracted document, and follow this License in all other respects
regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and


independent documents or works, in or on a volume of a storage or
distribution medium, is called an "aggregate" if the copyright resulting from
the compilation is not used to limit the legal rights of the compilation's users
beyond what the individual works permit. When the Document is included
in an aggregate, this License does not apply to the other works in the
aggregate which are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of


the Document, then if the Document is less than one half of the entire
aggregate, the Document's Cover Texts may be placed on covers that bracket
the Document within the aggregate, or the electronic equivalent of covers if
the Document is in electronic form. Otherwise they must appear on printed
covers that bracket the whole aggregate.

8. TRANSLATION

Translation is considered a kind of modification, so you may distribute


translations of the Document under the terms of section 4. Replacing
Invariant Sections with translations requires special permission from their
copyright holders, but you may include translations of some or all Invariant
Sections in addition to the original versions of these Invariant Sections. You
may include a translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions of those
notices and disclaimers. In case of a disagreement between the translation and
the original version of this License or a notice or disclaimer, the original
version will prevail.
If a section in the Document is Entitled "Acknowledgements",
"Dedications", or "History", the requirement (section 4) to Preserve its Title
(section 1) will typically require changing the actual title.

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as
expressly provided for under this License. Any other attempt to copy, modify,
sublicense or distribute the Document is void, and will automatically
terminate your rights under this License. However, parties who have received
copies, or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the
GNU Free Documentation License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the


Document specifies that a particular numbered version of this License "or
any later version" applies to it, you have the option of following the terms
and conditions either of that specified version or of any later version that has
been published (not as a draft) by the Free Software Foundation. If the
Document does not specify a version number of this License, you may choose
any version ever published (not as a draft) by the Free Software Foundation.