Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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
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
•Texto Completo
Versión para imprimir de este libro
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, Capitulo 2 y varias porciones del capítulo 1, 3 y
4
Javier Hernández Sánchez - Creacion del capitulo 9 Interfaces
Sebastian Sasías - Revisión, ampliación y formalización de conceptos (en
proceso)
Gustavo Novaro - Varios Para más detalles vea el historial.
Licencia
Referencias
Este Wikilibro tiene extenso material tomado del tutorial de C# del proyecto
MonoHispano (http://mono-hispano.org/tutoriales/csharp/).
•http://www.mono-project.com
•http://www.w3schools.com/
•http://www.mono-hispano.org
•http://www.mono-hispano.org/wiki/Tutoriales
•http://cougarpc.net/csharp (copia original)
C sharp NET/Capítulo 0
Una Breve Introducción a los Lenguajes de
Programación
Los lenguajes modernos de programación tienen carácterísticas que pueden
asemejarse en buen grado a los lenguajes hablados como el Español, el 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 (tambien generalizado como Lenguaje
Natural), estas reglas deben tomarse en cuenta para que las ideas que tratamos
de expresar tengan sentido. También, un lenguaje de programación al igual que
un lenguaje natural cambia con el tiempo. 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 un ordenador recibe instrucciones es una manera peculiar. ****
Añadir más 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.
Vosotros podréis leer este manual una 500 veces pero si no ponéis en práctica ni
investigáis por vosotros mismos, núnca podréis llegar a dominar este maravilloso
lenguaje de programación. Así que ¡a practicar se ha dicho!
Si señor...
C sharp NET/Capítulo 1
Volver al MENU PRINCIPAL
Capítulo 1
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 Básic.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 (?)
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.
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
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 '::'.
C sharp NET/Capítulo 2
CAPÍTULO 2
mcs Programa.cs
csc Programa.cs
con dotGNU:
ilsrun Programa.exe
o en Windows:
Programa
class HolaMundo
{
static void Main()
{
string var="Mundo";
System.Console.WriteLine ("Hola {0}!", var);
}
}
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 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");
}
}
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
Linea 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().
de acuerdo con nuestro ejemplo 2.1, la función Main() cumple con el formato
establecido:
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);
}
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 ¿que significa la palabra
System que está al inicio de cada programa?. Púes 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();ySecundaria.Presidente.otra_funci
ón();
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;
}
}
}
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.
es diferente a system.Console.WriteLine y es diferente a
system.console.writeLine
que es el nombre de espacio, componente y método que
System.Console.WriteLine
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#.
C sharp NET/Capítulo 3
CAPÍTULO 3
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
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.
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, pués 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 boolusing 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); }}
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
Para una lista completa de caracteres unicode podréis visitar la siguiente página:
http://unicode.coeurlumiere.com/
La siguiente lista contiene los caracteres de escape comunes y su significado:
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:
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 implicita de short a int se lleva a cabo en el
siguiente ejemplo:
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). Asi el siguiente ejemplo no compilará dándonos un error:
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:
Desde byte sbyte short ushort int uint long ulong float doubledecim
/hacia al
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
doubleE E E E E E E E E I E
decim E E E E E E E E E E I
al
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:
el resultado será:
int[,] arr = ;
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 simbolo _, el
identificador puede tener cualquier cantidad de letras, líneas o números.
Ejemplo:
EsteIdentificadorEsValido_este_tambienesteEsOtro1esteEsOtro2
Nota: Es recomendado que las variables siempre empiecen con minusculas 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;
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;
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:
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
El resultado es el siguiente:
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:
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 tambien 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:
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 =
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 tambien 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 capáz 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 simbolo &. Comprueba si todos los números binarios son 1
(o verdadero) entonces la respuesta es 1 (o verdadero)
OR
Representado por el simbolo | (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 simbolo ~ 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, asi por
ejemplo si tenemos el numero binario 01000001 y aplicamos el operador NOT ~
obtendremos 10111110, pero si tenemos una expresion 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 simbolo ^. 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 ultimos números. Asi 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
ultimos números. Asi el número 01000001 si se lo desplaza a la derecha una vez
01000001 >> 1, se convierte en 00100000
C sharp NET/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
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
Nota: Hay que tener mucho cuidado que el simbolo = 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:
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:
Bucle for
El bucle for de C# es idéntico al encontrado en los lenguajes C/C++ y Java. El
formato general es
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
</pre>
</pre>
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
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
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:
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.
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.
C sharp NET/Capítulo 5
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)
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.
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 contenidas dentro de un objeto.
La estructura mínima de un método tiene las siguientes partes:
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.
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
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.
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
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
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
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 )
int a = obj[3];
obj[3] = 6;
a = obj[3];
C sharp NET/Capítulo 6
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:
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
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
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#.
1. define readkey
using System;
namespace m7tr1x {
C sharp NET/Capítulo 7
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:
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:
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á:
C sharp NET/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 )
C sharp NET/Capítulo 9
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:
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 sera nuestra clase base e
IMovil nuestra interfaz.
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.
Otra forma de hacerlo sin tener que recurrir a la gestión de excepciones sería
utilizando la palabra reservada as de C#:
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
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 metodo void Turbo (bool)? Bueno vamos a verlo:
public class Campeon : Formula1, IAltaVelocidad{ public override void
Turbo(bool activar) { //gestion 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 si, 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:
C sharp NET/Capítulo 10
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}
C sharp NET/Capítulo 12
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?, ¿porqué
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:
C sharp NET/Capítulo 13
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.
1.1. 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.
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
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 el 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.
C sharp NET/Capítulo 17
C sharp NET/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.
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/
C sharp NET/Capítulo 26
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.
C sharp NET/Capítulo 28
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; } }
gmcs SerialExample.cs
mono SerialExample.exe
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
-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)
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.
C sharp NET/Capítulo 29
using System.Net.Sockets;