DENTRO DE UN PROGRAMA C#
Para comprender cómo funciona un programa en C#, analizaremos cada línea de código de C# del típico programa «Hola a todos».
Hello World, estilo de C#
El lenguaje de C# utiliza las clases para organizar y empaquetar el código. De hecho, todo el código ejecutable de C# debe estar contenido en una clase, incluso en un programa breve como «Hello World!». A continuación se muestra el programa completo que muestra «Hello World!» en la ventana de la consola.
C# |
using System; // A «Hello World!» program in C# namespace HelloWorld { class Hello { static void Main() { System.Console.WriteLine(«Hello World!»); } } } |
Directivas using y espacios de nombres
Cuando se crea una aplicación de consola con Visual C# Express, las primeras líneas en el editor de código contienen directivas using que muestran varios espacios de nombres de .NET Framework. Un espacio de nombres es una manera de agrupar clases y estructuras de una manera que limita su ámbito y evita conflictos de nombres con otras clases y estructuras. Cuando crea un programa en Visual C# Express, se crea automáticamente un espacio de nombres. Para utilizar las clases de otros espacios de nombres en su programa, debe especificarlos con una directivausing. Los espacios de nombres más comúnmente utilizados en .NET Framework se muestran de forma predeterminada cuando crea una nueva aplicación. Si utiliza clases de otros espacios de nombres en la biblioteca de clases, debe agregar una directiva using para ese espacio de nombres al archivo de código fuente. Para obtener más información sobre los espacios de nombres, vea Espacios de nombres (Visual C# Express).
Cuando el Editor de código detecta que se ha declarado una clase o estructura que no puede encontrar en los espacios de nombres enumerados en las directivas using actuales, sugerirá espacios de nombres que contienen la clase o estructura.
Comentarios
Después de las instrucciones using, la línea siguiente contiene un comentario. Los comentarios son útiles para incluir notas personales o destinadas a otros programadores.
C# |
// A «Hello World!» program in C# |
Los caracteres // convierten el resto de la línea en un comentario. Un bloque de texto también se puede convertir en comentario si se coloca entre los caracteres /* y */, por ejemplo:
C# |
/* A «Hello World!» program in C#. This program displays the string «Hello World!» on the screen. */ |
También puede utilizar una opción de formato para comentar código automáticamente.
Clases
El lenguaje C# utiliza las clases para empaquetar código: todo código de C# ejecutable debe estar contenido en una clase. Para obtener más información, vea Clases (Visual C# Express).
Main()
-
Cualquier programa en C# debe contener un método Main, en el cual se inicia y se termina la ejecución. Este método es donde se crean objetos y se ejecutan otros métodos. Main es un método static que reside dentro de una clase o una estructura. En el ejemplo de «Hello World!», se encuentra dentro de la clase Program .
Los métodos Main pueden definirse de alguna de las siguientes formas:
-
Puede devolver void:
C# |
static void Main() { //… } |
-
También puede devolver un valor de tipo int:
C# |
static int Main() { //… return 0; } |
-
Puede aceptar argumentos, lo cual resulta útil para crear programas de línea de comandos:
C# |
static void Main(string[] args) { //… } |
-
O bien
C# |
|
static int Main(string[] args) { //… return 0; } |
El parámetro del método Main es una matriz de tipo string que representa los argumentos de la línea de comandos utilizados para invocar el programa. Observe que, a diferencia de C++, esta matriz no incluye el nombre del archivo ejecutable (.exe). Para obtener más información, vea Main() y argumentos de línea de comandos (Guía de programación de C#).
Entrada y salida de consola
Los programas de consola de C# generalmente utilizan los servicios de entrada y salida proporcionados por la clase Console de .NET Framework. La instrucción Console.WriteLine(«Hello, World!»); utiliza el método WriteLine. Muestra su parámetro de cadena en la ventana de línea de comandos seguida de una nueva línea. Otros métodos de Console se utilizan para otras operaciones de entrada y salida. La clase Console es un miembro del espacio de nombres System. Si no se incluyera la instrucción using System; al principio del programa, tendría que especificar las clases de System de la siguiente manera:
C# |
System.Console.WriteLine(«Hello World!»); |
El método WriteLine es muy útil y lo utilizará a menudo si escribe aplicaciones de consola.
WriteLine puede mostrar cadenas:
C# |
Console.WriteLine(«Hello World!»); |
WriteLine también puede mostrar números:
C# |
int x = 42; Console.WriteLine(x); |
Si necesita mostrar varios elementos, utilice {0} para representar el primer elemento, {1} para el segundo elemento, y así sucesivamente, de la forma siguiente.
C# |
int year = 1066; string battle = «Battle of Hastings»; Console.WriteLine(«The {0} took place in {1}.», battle, year); |
El resultado presentará la siguiente apariencia: The Battle of Hastings took place in 1066.
ESTRUCTURA DE UN PROGRAMA C#
Cuando crea una aplicación de C#, tiene la opción de crear una aplicación de consola o una aplicación para Windows. Ambas difieren no sólo en el tipo de interfaz de usuario; también pueden diferir en su flujo de ejecución.
Aplicaciones para Windows
En una aplicación para Windows típica, con una interfaz de usuario gráfica, casi todas las acciones que tienen lugar después del inicio se realizan en respuesta a acciones del usuario como mover el mouse, seleccionar una opción de menú o escribir texto. Tales acciones producen eventos y llaman a métodos especiales de la aplicación denominados controladores de eventos. Un controlador de eventos inicia casi todo lo que un programa de Windows hace. Cuando no se genera ningún evento, el programa simplemente espera y no hace nada.
Si suele utilizar lenguajes de programación procedimentales, como COBOL, BASIC o FORTRAN, tendrá que acostumbrarse al modelo orientado a eventos. La diferencia más fundamental es que, en la programación orientada a eventos, otro software, incluso el propio sistema operativo, llama a los métodos controladores de eventos en la aplicación. No hay ninguna manera de saber a qué métodos va a llamar. Puede elegir los eventos que controlará en la aplicación, pero no puede saber de antemano el orden exacto en el que se van a producir esos eventos.
En una aplicación para Windows típica, los campos, matrices y colecciones que alojan el estado de la aplicación se colocan en la clase principal Form, que se denomina Form1 de forma predeterminada. En el ámbito de clase, estos miembros son accesibles desde todos los métodos controladores de eventos que se implementan en la misma clase Form. Cuando se llama a un controlador de eventos, éste puede realizar una acción que modifique los datos de la aplicación y, cuando el método devuelve un valor, la aplicación reanuda su estado de espera. Por ejemplo, un formulario puede contener un control TextBox y un botón Actualizar. Cuando el usuario hace clic en el botón, el controlador de eventos de la aplicación podría hacer algo como obtener el texto del control TextBox y, a continuación, agregarlo a una lista de otras cadenas almacenadas en el ámbito de clase. Después de que se ha agregado la cadena, la aplicación regresa al estado de espera. Otros controladores de eventos pueden realizar otros tipos de acciones en esa misma lista de cadenas en respuesta a los datos proporcionados por el usuario.
Sus propias clases personalizadas pueden enviar y recibir eventos utilizando los mismos mecanismos que los formularios Windows Forms.
Aplicaciones de consola
En muchas aplicaciones de consola, el flujo de ejecución pasa de una instrucción a la siguiente hasta que se llega al final del programa y la aplicación finaliza. Por supuesto, esto no ocurre siempre porque los eventos del teclado y los eventos del sistema generados por objetos como temporizadores y conexiones de red pueden seguir controlando la aplicación de consola. Las aplicaciones de consola sencillas a menudo constan de una sola clase, la cual contiene el método Main. Sin embargo, las aplicaciones más complejas pueden contener cualquier número de clases.
ESPACIOS DE NOMBRES
Los espacios de nombres son una manera de organizar los distintos tipos que aparecen en un programa en C#. Conceptualmente es similar a una carpeta en un sistema de archivo del equipo. Al igual que las carpetas, los espacios de nombres permiten a las clases tener un nombre completo único. Un programa en C# contiene uno o más espacios de nombres, que quedan definidos por el programador o como parte de una biblioteca de clases previamente escrita.
Por ejemplo, el espacio de nombres System incluye la clase Console, una clase que contiene los métodos para leer y escribir en la ventana de la consola. El espacio de nombres System también contiene múltiples espacios de nombres diferentes, como System.IO y System.Collections. Sólo .NET Framework tiene más de ochenta espacios de nombres, cada uno con miles de clases: los espacios de nombres se utilizan para minimizar la confusión que se podría producir entre tipos y métodos con nombres parecidos.
Si escribe una clase fuera de una declaración de espacio de nombres, el compilador proporcionará un espacio de nombres predeterminado para esa clase.
Accesos directos a espacios de nombres
Para utilizar el método WriteLine, definido en la clase Console contenida en el espacio de nombres System, utilice una línea de código como ésta:
C# |
System.Console.WriteLine(«Hello, World!»); |
Rápidamente se hace muy pesado el no olvidarse de preceder con System todos los métodos contenidos en Console, por lo que insertar la directiva using en el inicio del archivo de código fuente de C#, resulta una forma rápida y útil:
C# |
using System; |
La inclusión de using System; hace que no sea necesario incluir el espacio de nombres System y, por tanto, puede escribir únicamente lo siguiente:
C# |
Console.WriteLine(«Hello, World!»); |
Crear su propio espacio de nombres
Es común utilizar espacios de nombres cuando se trabaja en programas grandes. Utilizar espacios de nombres propios proporciona un grado de control sobre los métodos y tipos con nombres parecidos. Por ejemplo, suponga que está escribiendo una aplicación que carga datos estadísticos y archivos de imagen de un disco. Podría crear dos nuevos espacios de nombres, uno denominado Images y otro denominado StatisticalData. Si utiliza dos espacios de nombres separados, todos los nombres de los métodos definidos en cada espacio de nombres serán únicos, aun cuando las clases individuales tengan el mismo nombre. Esto significa que podría tener una clase denominada FileHandling en ambos espacios de nombres, y ambos contendrían un método denominado Load. La clase deseada se especificaría haciendo referencia a StatisticalData.FileHandling o Images.FileHandling
Una práctica recomendada es crear una carpeta independiente para cada espacio de nombres en el proyecto de Visual C# Express.
Ejemplo
El siguiente ejemplo define dos espacios de nombres, cada uno con una clase denominada FileHandling. Al especificar el espacio de nombres, se hace posible diferenciar rápidamente entre las clases y los métodos que contienen.
CLASES
C# es un lenguaje de programación orientado a objetos y, como otros lenguajes modernos, agrupa campos relacionados, métodos, propiedades y eventos en estructuras de datos denominadas clases.
Clases y objetos
Una clase es básicamente un plano para un tipo de datos personalizado. Cuando se define una clase, se utiliza cargándola en la memoria. Una clase que se ha cargado en la memoria se denomina objeto o instancia. Crea una instancia de una clase utilizando la palabra clave de C# new.
El siguiente es un ejemplo de definición de una clase denominada SampleClass y de creación de un objeto denominado sampleClass1 que es una instancia de esa clase. Como C# requiere que la función Main esté definida en una clase, el código siguiente también define una clase Program, pero esa clase no se utiliza para crear un objeto.
C# |
using System;
class SampleClass { public void SayHello() { Console.WriteLine(«Hello, World!»); } }
class Program { //Main is the entrypoint, where every C# program starts static void Main(string[] args) { SampleClass sampleClass1 = new SampleClass(); // Create an object sampleClass1.SayHello(); // Call a method } } |
Al igual que se puede construir cualquier número de casas con un mismo plano, se pueden crear instancias de cualquier número de objetos con la misma clase. Es muy común tener matrices o listas que contienen muchos objetos de la misma clase. Cada instancia de la clase ocupa su propio espacio de memoria y los valores de sus campos (excepto los campos estáticos, como se describe a continuación) son independientes. En el ejemplo de código siguiente, puede crear un objeto de tipo Animal y establecer su tamaño en 2, y otro objeto cuyo tamaño puede establecer en 3. Sin embargo, hay una excepción importante a esta regla: el miembro estático.
Miembros estáticos y de instancia
Un miembro estático es un método o campo al que se puede obtener acceso sin hacer referencia a una instancia determinada de una clase. El método estático más común es Main, que es el punto de entrada de todos los programas de C#; tenga en cuenta que no es necesario crear una instancia de la clase contenedora para llamar al método Main. Otro método estático que se utiliza normalmente es WriteLine en la clase Console. Observe la diferencia de sintaxis cuando se tiene acceso a métodos estáticos; puede utilizar el nombre de clase, no el nombre de instancia, en la parte izquierda del operador de punto: Console.WriteLine.
Cuando declara un campo de clase estático, todas las instancias de esa clase compartirán ese campo. Si se declarara size como estático en el ejemplo de código siguiente y un objeto Animal cambiara el valor, éste se cambiaría para todos los objetos de tipo Animal.
Una clase estática es una cuyos miembros son todos estáticos. Las clases, métodos y campos estáticos son útiles en ciertos escenarios por razones de rendimiento y eficacia. Sin embargo, pueden surgir errores casi imperceptibles si se supone que un campo es un campo de instancia cuando de hecho es estático. Para obtener más información, vea Clases estáticas y sus miembros (Guía de programación de C#).
Clases y archivos
Cada programa de C# tiene al menos una clase. Al diseñar el programa, es una buena práctica, aunque no es un requisito, mantener una clase única en cada archivo de código fuente (.cs). Si utiliza el entorno de desarrollo integrado (IDE) de C# para crear las clases, éste, a la vez, creará un nuevo archivo de código en forma automática.
Encapsulación
Una clase representa normalmente las características de una cosa y las acciones que puede realizar. Por ejemplo, para representar un animal como una clase de C#, puede asignarle un tamaño, velocidad y fuerza, que se representan como números, y algunas funciones como MoveLeft(), MoveRight(), SpeedUp(), Stop(), etc., en las que podría escribir código para que el «animal» realice esas acciones. En C#, la clase animal tendría la apariencia siguiente:
C# |
public class Animal { private int size; private float speed; private int strength;
public void MoveLeft() // method { // code goes here… }
// other methods go here… } |
Si examina la .NET Framework Class Library (WinFX), observará que cada clase representa una «cosa», como XmlDocument, String o Form, y cada una de ellas tiene varias acciones que puede realizar (métodos), características que se pueden leer y, a veces, modificar (propiedades) y notificaciones (eventos) que produce cuando se realiza una acción concreta. Los métodos, propiedades y eventos, junto con todas las demás variables internas y constantes (campos), se conocen como miembros de la clase.
La agrupación de miembros en clases no es sólo lógica, además permite ocultar datos y funciones a los que no desea que otro código tenga acceso. Este principio se conoce como encapsulación. Cuando explora las bibliotecas de clases de .NET Framework, está viendo sólo los miembros públicos de esas clases. Cada clase tiene probablemente miembros privados que esa clase o las relacionadas con ella utilizan internamente, pero su finalidad no es que los utilicen las aplicaciones. Cuando cree sus propias clases, decidirá qué miembros deberían ser públicos y cuáles deberían ser privados.
Herencia
Una clase puede heredar de otra, lo que significa que incluye todos los miembros, tanto públicos como privados, de la clase original, más los miembros adicionales que define. La clase original se denomina clase base y la nueva clase se denomina clase derivada. Una clase derivada se crea para representar algo que es un tipo más especializado de la clase base. Por ejemplo, podría definir una clase Cat que hereda de Animal. Cat puede hacer lo mismo que Animal, más una acción única adicional. El código C# tiene la apariencia siguiente:
C# |
public class Cat : Animal { public void Purr() { } } |
La notación Cat : Animal indica que Cat hereda de Animal y, por tanto, Cat también tiene un método MoveLeft y tres variables privadas: size, speed y strength. Si define una clase SiameseCat que hereda de Cat, tendrá todos los miembros de Cat más todos los miembros de Animal.
Polimorfismo
En el campo de la programación, polimorfismo hace referencia a la capacidad de una clase derivada para redefinir o reemplazar los métodos que hereda de una clase base. Esto se hace cuando es necesario realizar una acción específica en un método que es diferente o no está definido en la clase base. Por ejemplo, como el método Animal.MoveLeft tiene que ser muy general a fin de que sea válido para todos los animales, probablemente es muy simple, por ejemplo: «cambiar la orientación para que la cabeza del anima apunte en la dirección X». Sin embargo, en la clase Cat, esto podría no ser suficiente. Podría ser necesario especificar cómo Cat mueve sus patas y cola mientras gira. Además, si también se define una clase Fish o Bird, probablemente habría que reemplazar el método MoveLeft de diferentes maneras para cada una de esas clases. Como el comportamiento del método MoveLeft se puede personalizar para una clase concreta, el código que crea la clase y llama a sus métodos no tiene un método independiente para cada animal del mundo. Siempre y cuando el objeto herede de Amimal, el código de llamada puede llamar simplemente al método MoveLeft para que se invoque la versión del método propia del objeto.
Constructores
Cada clase tiene un constructor: un método que comparte el mismo nombre que la clase. Se llama al constructor cuando se crea un objeto basado en la definición de clase. Los constructores establecen normalmente los valores iniciales de las variables definidas en la clase. Esto no es necesario si los valores iniciales van a ser cero para los tipos de datos numéricos, false para los tipos de datos booleanos o null para los tipos de referencia, ya que estos tipos de datos se inicializan automáticamente.
Puede definir constructores con cualquier número de parámetros. Los constructores que no tienen parámetros se denominan constructores predeterminados. En el ejemplo siguiente se define una clase con un constructor predeterminado y un constructor que toma una cadena y, a continuación, se utilizan ambos:
C# |
class SampleClass { string greeting;
public SampleClass() { greeting = «Hello, World»; }
public SampleClass(string message) { greeting = message; }
public void SayHello() { System.Console.WriteLine(greeting); } }
class Program { static void Main(string[] args) { // Use default constructor. SampleClass sampleClass1 = new SampleClass(); sampleClass1.SayHello();
// Use constructor that takes a string parameter. SampleClass sampleClass2 = new SampleClass(«Hello, Mars»); sampleClass2.SayHello(); } } |
Sobrecarga de operadores
La creación de métodos diferentes con el mismo nombre, en la SampleClass() de ejemplo anterior, se denomina sobrecarga. El compilador sabe qué método utilizar porque la lista de argumentos, si existe, se proporciona cada vez que se crea un objeto. La sobrecarga puede hacer que el código sea más flexible y legible.
Destructores
Si ha utilizado C++, ya conoce los destructores. Debido al sistema de recolección automática de elementos no utilizados de C#, no es probable que tenga que implementar un destructor a menos que la clase utilice recursos no administrados.
Estructuras
Una estructura es un tipo que es similar a una clase de muchas maneras, excepto en que no admite la herencia.
ESTRUCTURAS
Una estructura en C# es similar a una clase, pero las estructuras carecen de ciertas características, como la herencia. Además, como una estructura es un tipo de valor, normalmente se puede crear más rápido que una clase. Si utiliza bucles de pequeñas dimensiones en los que se crean grandes cantidades de estructuras de datos nuevas, debe considerar la posibilidad de utilizar una estructura en vez de una clase. Las estructuras también se utilizan para encapsular grupos de campos de datos como las coordenadas de un punto en una cuadrícula o las dimensiones de un rectángulo.
Ejemplo
Este programa de ejemplo define una struct para almacenar una ubicación geográfica. También reemplaza el método ToString() para generar un resultado más útil cuando se muestra en la instrucción WriteLine. Como no hay ningún método en la struct, no hay ventaja para definirlo como una clase.
C# |
struct GeographicLocation { private double longitude; private double latitude;
public GeographicLocation(double longitude, double latitude) { this.longitude = longitude; this.latitude = latitude; }
public override string ToString() { return System.String.Format(«Longitude: {0} degrees, Latitude: {1} degrees», longitude, latitude); } }
class Program { static void Main() { GeographicLocation Seattle = new GeographicLocation(123, 47); System.Console.WriteLine(«Position: {0}», Seattle.ToString()); } } |
Resultado
El resultado de este ejemplo presenta el siguiente aspecto:
Position: Longitude: 123 degrees, Latitude: 47 degrees
CONSTANTES Y VARIABLES
Una variable representa un valor numérico o de cadena o un objeto de una clase. El valor que la variable almacena puede cambiar, pero el nombre sigue siendo el mismo. Una variable es un tipo de campo. El código siguiente es un ejemplo sencillo de cómo declarar una variable de entero, asignarle un valor y, a continuación, asignarle un nuevo valor.
C# |
int x = 1; // x holds the value 1 x = 2; // now x holds the value 2 |
En C#, las variables se declaran con un tipo de datos y una etiqueta concretos. Si hasta ahora sólo ha utilizado lenguajes con tipos definidos de forma imprecisa como JScript, estará acostumbrado a emplear el mismo tipo «var» para todas las variables, pero en C# tiene que especificar si la variable es de tipo int, float, byte, short u otro cualquiera entre más de 20 tipos de datos diferentes. El tipo especifica, entre otras cosas, la cantidad de memoria exacta que se debe asignar para almacenar el valor cuando la aplicación se ejecuta. El lenguaje C# fuerza ciertas reglas al convertir una variable de un tipo en otro.
C# |
int answer = 42; string greeting = «Hello, World!»; double bigNumber = 1e100;
System.Console.WriteLine(«{0} {1} {2}», answer, greeting, bigNumber); |
Constantes
Una constante es otro tipo de campo. Contiene un valor que se asigna cuando se compila el programa y nunca cambia después. Las constantes se declaran con la palabra clave const; son útiles para que el código sea más legible.
C# |
const int speedLimit = 55; const double pi = 3.14159265358979323846264338327950; |
Una variable readonly es como una constante pero su valor se asigna al iniciar el programa. Esto le permite establecer el valor basándose en alguna otra condición que no conoce hasta que se ejecuta el programa. Pero después de esa primera asignación, el valor no puede cambiar de nuevo mientras el programa se está ejecutando.
OPERADORES
En C#, los operadores tienen una sintaxis similar a otros lenguajes de programación de estilo C. Los operadores se utilizan para hacer cálculos, asignar valores a variables, comprobar la igualdad o desigualdad, y realizar otras operaciones.
En las secciones siguientes se muestran algunos de los operadores normalmente utilizados en C#.
Operadores de asignación e igualdad
En C#, el operador signo igual (=) tiene la misma funcionalidad que en C y C++:
Operador |
Finalidad |
= |
Asigna un valor. |
== |
Comprueba la igualdad. |
Ejemplo
C# |
int x = 100; if (x == 100) { System.Console.WriteLine(«X is equal to 100»); } |
Operadores matemáticos y lógicos
La siguiente lista muestra los operadores matemáticos básicos en orden de prioridad. Utilice paréntesis para imponer otro orden.
Operador |
Finalidad |
*, /, % |
Multiplicación, división, módulos |
+, – |
Suma, resta |
& |
AND lógico |
^ |
XOR lógico |
| |
OR lógico |
Ejemplo
C# |
int x = 1; int y = x + 10 * 100; // multiplication first y = 1001 int z = (x + 10) * 100; // addition first z = 1100 |
Operadores de incremento y decremento
Los accesos directos de estilo de C y C++ son compatibles, incluso los operadores de postfijo y prefijo, como se muestra en estos ejemplos:
Operador |
Finalidad |
v |
Incrementar variable v por 1. |
v |
Incrementar variable v por n. |
v |
Multiplicar variable v por n. |
v |
Restar n de variable v. |
Ejemplo
C# |
int x = 0;
int y = x++; // x is 1, y is 0
System.Console.WriteLine(«{0} {1}», x, y);
int z = ++x; // x is 2, z is 2
System.Console.WriteLine(«{0} {1}», x, z); |
Operadores relacionales
Los siguientes operadores comparan dos valores y devuelven un resultado booleano:
Operador |
Finalidad |
== |
Comprobar igualdad. |
!= |
Comprobar desigualdad. |
> |
Mayor que |
< |
Menor que |
>= |
Mayor o igual que |
<= |
Menor o igual que |
Ejemplo
C# |
int x = int.Parse(System.Console.ReadLine());
if (x > 100) { System.Console.WriteLine(«X is greater than 100»); } |
Operadores lógicos de condición
Los operadores lógicos se utilizan para crear instrucciones de condición más flexibles combinando varias cláusulas:
Operador |
Finalidad |
&& |
AND condicional. |
|| |
OR condicional. |
! |
NOT condicional. |
Ejemplo
C# |
int x = int.Parse(System.Console.ReadLine());
if ((x >= 100) && (x <= 200)) { System.Console.WriteLine(«X is between 100 and 200»); } |
Operadores matemáticos más avanzados
Para realizar operaciones matemáticas más avanzadas, por ejemplo, trigonométricas, utilice la clase Math de .NET Framework. En este ejemplo, se utilizan los métodos Sin (seno) y Sqrt (raíz cuadrada) y la constante PI:
Ejemplo
C# |
double d = System.Math.Sin(System.Math.PI/2); double e = System.Math.Sqrt(144); |
Sobrecarga de operadores
C# admite sobrecarga de operadores; esto permite volver a definir los operadores para que tengan más sentido cuando se utilizan con tipos de datos propios. En el siguiente ejemplo, se crea una estructura que almacena un solo día de la semana en un tipo de variable definido por una enumeración. El operador de suma se sobrecarga para que se pueda agregar un número entero de días al día actual y devolver un nuevo día de la semana. De este modo, domingo, con un día agregado, devuelve lunes.
Ejemplo
C# |
using System;
// Define an DayOfWeek data type enum DayOfWeek { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
// Define a struct to store the methods and operators struct Day { private DayOfWeek day;
// The constructor for the struct public Day(DayOfWeek initialDay) { day = initialDay; }
// The overloaded + operator public static Day operator +(Day lhs, int rhs) { int intDay = (int)lhs.day; return new Day((DayOfWeek)((intDay + rhs) % 7)); }
// An overloaded ToString method public override string ToString() { return day.ToString(); } }
public class Program { static void Main() { // Create a new Days object called «today» Day today = new Day(DayOfWeek.Sunday); Console.WriteLine(today.ToString());
today = today + 1; Console.WriteLine(today.ToString());
today = today + 14; Console.WriteLine(today.ToString()); } } |
BIFURCACIÓN Y TOMA DE DECISIONES
Cambiar el flujo de control en un programa en respuesta a algún tipo de entrada o valor calculado es una parte esencial de un lenguaje de programación. C# proporciona la capacidad para cambiar el flujo de control, ya sea en forma incondicional, pasando a una nueva ubicación en el código, o condicionalmente, realizando una prueba.
Comentarios
El tipo de bifurcación condicional más sencillo utiliza la construcción if. Se puede utilizar una cláusula else con la construcción if y anidar varias construcciones if.
C# |
using System;
class Program { static void Main() { int x = 1; int y = 1;
if (x == 1) Console.WriteLine(«x == 1»); else Console.WriteLine(«x != 1»);
if (x == 1) { if (y == 2) { Console.WriteLine(«x == 1 and y == 2»); } else { Console.WriteLine(«x == 1 and y != 2»); } } } } |
Nota |
A diferencia de C y C++, las instrucciones if requieren valores booleanos. Por ejemplo, no se permite tener una instrucción que no se evalúa con un simple True o False, como (a=10). En C#, no se puede sustituir el valor 0 por False y el valor 1 (o cualquier otro valor) por True. |
Las instrucciones que siguen a las palabras clave if y else pueden ser una única línea de código, tal como se muestra en la primera instrucción if-else del ejemplo de código anterior, o un bloque de instrucciones entre llaves, tal como se muestra en la segunda instrucción if-else. Es posible anidar instrucciones if-else, pero generalmente se considera un mejor hábito de programación utilizar una instrucción switch en su lugar.
Una instrucción switch puede realizar varias acciones dependiendo del valor de una expresión determinada. El código entre la instrucción case y la palabra clave break se ejecuta si se cumple la condición. Si desea continuar el flujo de control con otra instrucción case, utilice la palabra clave goto.
C# |
using System;
class Program { static void Main() { int x = 3;
switch (x) { case 1: Console.WriteLine(«x is equal to 1»); break;
case 2: Console.WriteLine(«x is equal to 2»); break;
case 3: goto default;
default: Console.WriteLine(«x is equal to neither 1 nor 2»); break; } } } |
La expresión que la instrucción switch utiliza para determinar la instrucción case que se ejecutará debe utilizar Tipos de datos integrados (Visual C# Express), como int o string; no se pueden utilizar tipos más complejos definidos por el usuario.
A diferencia de Visual Basic, en C# la condición debe ser un valor constante. Por ejemplo, no se puede comparar la expresión con un intervalo de valores.
CADENAS
Una cadena de C# es un grupo de uno o más caracteres declarados mediante la palabra clave string, que es un método abreviado del lenguaje C# para la clase System.String. Las cadenas de C# son mucho más fáciles de utilizar y mucho menos propensas a errores de programación que las matrices de caracteres de C o C++.
Un literal de cadena se declara utilizando las comillas, como se muestra en el siguiente ejemplo:
C# |
string greeting = «Hello, World!»; |
Puede extraer subcadenas y concatenar cadenas de la siguiente manera:
C# |
string s1 = «orange»; string s2 = «red»;
s1 += s2; System.Console.WriteLine(s1); // outputs «orangered»
s1 = s1.Substring(2, 5); System.Console.WriteLine(s1); // outputs «anger» |
Los objetos String son inmutables y, una vez creados, no se pueden cambiar. Los métodos que actúan sobre las cadenas, devuelven los nuevos objetos de cadena. De esta forma, por motivos de rendimiento, las grandes cantidades de concatenación o de manipulación de cadenas implicadas se deben llevar a cabo con la clase StringBuilder, como se muestra en los siguientes ejemplos de código.
Trabajar con cadenas
Carácter de escape
Los caracteres de escape como «\n» y (nueva línea) y «\t» (tabulador) se pueden incluir en cadenas. La línea:
C# |
string hello = «Hello\nWorld!»; |
equivale a:
Hello
World!
Si desea incluir una barra diagonal inversa, ésta debe estar precedida de otra barra diagonal inversa. La cadena siguiente:
C# |
string filePath = «\\\\My Documents\\»; |
equivale a:
\\My Documents\
El símbolo @
El símbolo @ especifica que se deben omitir los caracteres de escape y saltos de línea cuando se crea la cadena. Las dos cadenas siguientes son, por consiguiente, idénticas:
C# |
string p1 = «\\\\My Documents\\My Files\\»; string p2 = @»\\My Documents\My Files\»; |
ToString()
Todos los tipos de datos integrados de C# ofrecen el método ToString, que convierte un valor en una cadena. Este método se puede utilizar para convertir valores numéricos en cadenas de la siguiente manera:
C# |
int year = 1999; string msg = «Eve was born in » + year.ToString(); System.Console.WriteLine(msg); // outputs «Eve was born in 1999» |
Tener acceso a los caracteres individuales
Se obtiene acceso a los caracteres individuales contenidos en una cadena utilizando métodos como Substring, Replace,, Split y Trim.
C# |
string s3 = «Visual C# Express»;
System.Console.WriteLine(s3.Substring(7, 2)); // outputs «C#» System.Console.WriteLine(s3.Replace(«C#», «Basic»)); // outputs «Visual Basic Express» |
También es posible copiar los caracteres en una matriz de caracteres, tal como se muestra a continuación:
C# |
string s4 = «Hello, World»; char[] arr = s4.ToCharArray(0, s4.Length);
foreach (char c in arr) { System.Console.Write(c); // outputs «Hello, World» } |
Puede obtener acceso a los caracteres individuales de una cadena con un índice:
C# |
string s5 = «Printing backwards»;
for (int i = 0; i < s5.Length; i++) { System.Console.Write(s5[s5.Length – i – 1]); // outputs «sdrawkcab gnitnirP» } |
Cambiar mayúsculas y minúsculas
Para cambiar las letras en una cadena a mayúsculas o minúsculas, se utiliza ToUpper() o ToLower(), de la siguiente forma:
C# |
string s6 = «Battle of Hastings, 1066»;
System.Console.WriteLine(s6.ToUpper()); // outputs «BATTLE OF HASTINGS 1066» System.Console.WriteLine(s6.ToLower()); // outputs «battle of hastings 1066» |
Comparaciones
La manera más sencilla de comparar dos cadenas es utilizar los símbolos == y !=, que realizan una comparación con distinción entre mayúsculas y minúsculas.
C# |
string color1 = «red»; string color2 = «green»; string color3 = «red»;
if (color1 == color3) { System.Console.WriteLine(«Equal»); } if (color1 != color2) { System.Console.WriteLine(«Not equal»); } |
Los objetos String también tienen un método CompareTo() que devuelve un valor entero, basado en si una cadena es menor < o mayor > que otra. Al comparar las cadenas, se utiliza el valor Unicode, y las minúsculas tienen un valor menor que las mayúsculas.
C# |
string s7 = «ABC»; string s8 = «abc»;
if (s7.CompareTo(s8) > 0) { System.Console.WriteLine(«Greater-than»); } else { System.Console.WriteLine(«Less-than»); } |
Para buscar una cadena dentro de otra, utilice IndexOf(). IndexOf() devuelve -1 si la cadena de búsqueda no se encuentra; en caso contrario devuelve el índice de la primera posición de la cadena, con base cero.
C# |
string s9 = «Battle of Hastings, 1066»;
System.Console.WriteLine(s9.IndexOf(«Hastings»)); // outputs 10 System.Console.WriteLine(s9.IndexOf(«1967»)); // outputs -1 |
Dividir una cadena en subcadenas
Dividir una cadena en subcadenas es una tarea de programación común, semejante a dividir una frase en sus respectivas palabras. El método Split() toma una matriz de char de delimitadores (por ejemplo, un carácter de espacio) y devuelve una matriz de subcadenas. Para obtener acceso a esta matriz con foreach:
C# |
char[] delimit = new char[] { ‘ ‘ }; string s10 = «The cat sat on the mat.»; foreach (string substr in s10.Split(delimit)) { System.Console.WriteLine(substr); } |
Este código genera cada palabra en una línea separada de la siguiente forma:
The
cat
sat
on
the
mat.
Utilizar StringBuilder
La clase StringBuilder crea un búfer de cadena que proporciona el mejor rendimiento si el programa realiza una gran manipulación de cadenas. La clase StringBuilder también permite reasignar caracteres individuales, algo que el tipo de datos de cadena integrado no admite.
En este ejemplo, se crea un objeto StringBuilder y el contenido se agrega poco a poco utilizando el método Append.
MATRICES Y COLECCIONES
El almacenamiento de grupos de elementos de datos relacionados es un requisito básico de casi todas las aplicaciones de software; las dos formas principales de almacenarlos son matrices y colecciones.
Matrices
Las matrices son colecciones de objetos del mismo tipo. Una matriz puede tener virtualmente cualquier longitud, lo que significa que se puede utilizar para almacenar miles o incluso millones de objetos, pero el tamaño se tiene que decidir al crearla. Se tiene acceso a cada elemento de la matriz mediante un índice, que es simplemente un número que indica la posición o ranura donde el objeto está almacenado. Las matrices se pueden utilizar para almacenar tipos de referencia y tipos de valor.
Matrices unidimensionales
Una matriz es una colección indizada de objetos. Una matriz unidimensional de objetos se declara así:
type[] arrayName;
A menudo, puede inicializar los elementos contenidos al mismo tiempo en la matriz de la siguiente manera:
C# |
int[] array = new int[5]; |
El valor predeterminado de los elementos numéricos de la matriz es cero y los elementos de referencia cambian de forma predeterminada a null, pero los valores se pueden inicializar durante la creación de la matriz de la manera siguiente:
C# |
int[] array1 = new int[5] { 1, 3, 5, 7, 9 }; |
O también:
C# |
int[] array2 = {1, 3, 5, 7, 9}; |
Las matrices utilizan los índices de base cero, por lo que el primer elemento en la matriz es el elemento 0.
C# |
string[] days = {«Sun», «Mon», «Tue», «Wed», «Thr», «Fri», «Sat»}; System.Console.WriteLine(days[0]); // Outputs «Sun» |
Matrices multidimensionales
Conceptualmente, una matriz multidimensional con dos dimensiones presenta la apariencia de una cuadrícula. Una matriz multidimensional con tres dimensiones presenta la apariencia de un cubo.
C# |
// declare multidimension array (two dimensions) int[,] array2D = new int[2,3];
// declare and initialize multidimension array int[,] array2D2 = { {1, 2, 3}, {4, 5, 6} };
// write elements in a multidimensional array for (int i=0; i<2; i++) { for (int j=0; j<3; j++) { array2D[i,j] = (i + 1) * (j + 1); } }
// read elements in a multidimensional array for (int i=0; i<2; i++) { for (int j=0; j<3; j++) { System.Console.Write(array2D[i,j]); } System.Console.WriteLine(); } |
Matrices escalonadas
Una variación de la matriz multidimensional es la matriz escalonada: una matriz de matrices. Una matriz escalonada es una matriz unidimensional y cada elemento es en sí mismo una matriz. No es necesario que todas las matrices de elementos sean del mismo tamaño.
Se declara una matriz escalonada así:
C# |
int[][] jaggedArray = new int[3][]; |
De esta manera se crea una matriz de tres matrices. Estas matrices se pueden inicializar así:
C# |
jaggedArray[0] = new int[5]; jaggedArray[1] = new int[4]; jaggedArray[2] = new int[2]; |
Utilizar la instrucción foreach
La instrucción foreach se utiliza a menudo para tener acceso a todos los elementos almacenados en una matriz:
C# |
int[] numbers = { 4, 5, 6, 1, 2, 3, -2, -1, 0 }; foreach (int i in numbers) { System.Console.WriteLine(i); } |
Matrices de objetos
La creación de una matriz de objetos, en lugar de una matriz de tipos de datos simples como enteros, es un proceso que consta de dos partes. En primer lugar, se declara la matriz y, después, se deben crear los objetos almacenados en la matriz. En este ejemplo se crea una clase que define un CD de audio. A continuación se crea una matriz que almacena 20 CDs de audio.
C# |
namespace CDCollection { // Define a CD type. class CD { private string album; private string artist; private int rating;
public string Album { get {return album;} set {album = value;} } public string Artist { get {return artist;} set {artist = value;} } public int Rating { get {return rating;} set {rating = value;} } }
class Program { static void Main(string[] args) { // Create the array to store the CDs. CD[] cdLibrary = new CD[20];
// Populate the CD library with CD objects. for (int i=0; i<20; i++) { cdLibrary[i] = new CD(); }
// Assign details to the first album. cdLibrary[0].Album = «See»; cdLibrary[0].Artist = «The Sharp Band»; cdLibrary[0].Rating = 10; } } } |
Colecciones
Una matriz es simplemente una de muchas opciones para almacenar conjuntos de datos utilizando C#. La opción que elija depende de varios factores, entre los que se incluye cómo piensa manipular o tener acceso a los elementos. Por ejemplo, utilizar una lista suele ser más rápido que utilizar una matriz si es necesario insertar elementos al principio o en medio de la colección. Otros tipos de clases de colección incluyen mapa, árbol y pila; cada uno tiene sus propias ventajas. Para obtener más información, vea Tipos de colección utilizados normalmente, System.Collections y System.Collections.Generic.
En el ejemplo siguiente se muestra cómo utilizar la clase List <T>. Observe que a diferencia de la clase Array, los elementos se pueden insertar en medio de la lista. Este ejemplo restringe los elementos de la lista a fin de que sean cadenas.
C# |
public class TestCollections { public static void TestList() { System.Collections.Generic.List<string> sandwich = new System.Collections.Generic.List<string>();
sandwich.Add(«bacon»); sandwich.Add(«tomato»);
sandwich.Insert(1, «lettuce»);
foreach (string ingredient in sandwich) { System.Console.WriteLine(ingredient); } } } |
BUCLES
Un bucle es una instrucción o conjunto de instrucciones que se repite un número especificado de veces o hasta que se cumpla alguna condición. El tipo de bucle que elija dependerá de la tarea de programación y de su preferencia de codificación personal. Una de las principales diferencias entre C# y otros lenguajes como C++ es el bucle foreach, que está diseñado para simplificar la iteración en matrices o colecciones.
Bucles foreach
C# introduce una forma de crear bucles que puede resultar novedosa para los programadores en C++ y C: el bucle foreach. Ya no tendrá que crear una variable solo para indizar una matriz u otra estructura de datos como una colección, puesto que el bucle foreach hace parte de ese trabajo:
C# |
// An array of integers int[] array1 = {0, 1, 2, 3, 4, 5};
foreach (int n in array1) { System.Console.WriteLine(n.ToString()); }
// An array of strings string[] array2 = {«hello», «world»};
foreach (string s in array2) { System.Console.WriteLine(s); } |
Bucles for
Más abajo se muestra cómo crear el mismo bucle utilizando la palabra clave for:
Bucles while
Las versiones del bucle while tienen la apariencia siguiente:
C# |
// An array of integers int[] array1 = {0, 1, 2, 3, 4, 5}; int x = 0;
while (x < 6) { System.Console.WriteLine(array1[x].ToString()); x++; }
// An array of strings string[] array2 = {«hello», «world»}; int y = 0;
while (y < 2) { System.Console.WriteLine(array2[y]); y++; } |
Bucles do-while
Los bucles do-while tienen esta apariencia:
C# |
// An array of integers int[] array1 = {0, 1, 2, 3, 4, 5}; int x = 0;
do { System.Console.WriteLine(array1[x].ToString()); x++; } while(x < 6);
// An array of strings string[] array2 = {«hello», «world»}; int y = 0;
do { System.Console.WriteLine(array2[y]); y++; } while(y < 2); |
ENUMERACION ES
C# permite crear un conjunto propio de constantes con nombre utilizando la palabra clave enum. Estos tipos de datos permiten declarar un conjunto de nombres u otros valores literales que definen todos los valores posibles que se pueden asignar a una variable.
Por ejemplo, si el programa utiliza los días de la semana, es posible crear un nuevo tipo llamado DayOfWeek. Se puede declarar una nueva variable del tipo DayOfWeek y asignarle un valor. La utilización de este tipo de datos permite que el código sea más legible y hace menos probable que se asigne a la variable un valor no válido o inesperado.
C# |
public enum DayOfWeek { Sunday = 0, Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6 }
class Program { static void Main() { DayOfWeek day = DayOfWeek.Monday; int i = (int) DayOfWeek.Monday;
System.Console.WriteLine(day); // displays Monday System.Console.WriteLine(i); // displays 1 } } |
Técnicas de enumeración más avanzadas
A continuación, se muestran varias características de tipos de datos enum que pueden ser útiles.
Mostrar valores literales de enumeración
Si necesita tener acceso al nombre o las palabras que se está utilizando en el tipo de datos enum, puede hacerlo mediante el método ToString(), de la siguiente manera:
C# |
DayOfWeek day = DayOfWeek.Wednesday; System.Console.WriteLine(day.ToString()); // displays Wednesday |
Establecer valores predeterminados
De manera predeterminada, el primer valor en el tipo enumerado es un cero. Se puede especificar un valor inicial diferente, de la siguiente forma:
C# |
enum Color { Red = 1, Yellow = 2, Blue = 3 }; |
De hecho, se pueden definir valores enteros únicos para todos los valores:
C# |
enum Medal { Gold = 30, Silver = 20, Bronze = 10 }; |
CONTROL DE ERRORES Y EXCEPCIONES
Cuando algo va mal mientras un programa de C# se está ejecutando, se inicia una excepción. Las excepciones detienen el flujo actual del programa, y si no se hace nada, el programa dejará de funcionar. Las excepciones se producen por un error en el programa, por ejemplo, si se divide un número por cero, o pueden ser el resultado de alguna entrada inesperada, por ejemplo, cuando un usuario selecciona un archivo que no existe. El programador debe habilitar su programa para que resuelva estos problemas sin bloquearse.
C# proporciona varias palabras clave, try, catch y finally, que permiten a los programas detectar las excepciones, resolverlas y seguir trabajando. Estas constituyen una herramienta muy útil para hacer más fiables sus aplicaciones.
Try y Catch
Las palabras clave try y catch se utilizan conjuntamente. Utilice try para delimitar el bloque de código que podría generar una excepción y catch para contener el código que se ejecutará si la excepción se genera. En este ejemplo, un cálculo crea una excepción al dividir por cero, que luego se detecta. Sin los bloques try y catch, en este programa se produciría un error.
C# |
class ProgramTryCatch { static void Main() { int x=0, y=0;
try { x = 10 / y; }
catch (System.DivideByZeroException) { System.Console.WriteLine(«There was an attempt to divide by zero.»); } } } |
Cuando se esté programando se recomienda ofrecer detalles sobre el tipo de excepción que detecta el código de catch. Cada try puede tener varias instrucciones catch, que se encargarán de excepciones diferentes.
Bloque finally
El código contenido en un bloque finally siempre se ejecuta, se produzca o no una excepción. Utilice el bloque finally para asegurarse de que se devuelven los recursos: por ejemplo, para asegurarse de que un archivo está cerrado.
C# |
try { // Code to try here. } catch (System.Exception ex) { // Code to handle exception here. } finally { // Code to execute after try (and possibly catch) here. } |
Utilizar el control de excepciones
Las excepciones no son siempre una señal de que algo catastrófico ha ocurrido en el programa. A menudo es una forma más conveniente de dejar una sección de código que ya no es pertinente o es una señal de que un método no se ha realizado con éxito. Muchos de los métodos de clase de .NET Framework crean excepciones para advertir de una determinada condición.
También puede crear sus propias excepciones mediante la palabra clave throw. Por ejemplo:
C# |
class ProgramThrow { static void DoWork(int x) { if (x > 5) { throw new System.ArgumentOutOfRangeException(«X is too large»); } }
static void Main() { try { DoWork(10); } catch (System.ArgumentOutOfRangeException ex) { System.Console.WriteLine(ex.Message); } } } |
Utilice las excepciones en sus programas cuando crea que existe la posibilidad de que surja alguna situación inesperada. Por ejemplo, al tratar con los datos introducidos por un usuario, al leer un archivo o al obtener acceso a alguna información de Internet.