Programacion en C Sharp
Programacion en C Sharp
Introducción
Historia
Durante el desarrollo de la plataforma .NET, las bibliotecas de clases fueron
escritas originalmente usando un sistema de código gestionado llamado Simple
Managed C (SMC). En abril de 1999, Anders Hejlsberg formó un equipo con la
misión de desarrollar un nuevo lenguaje orientado a objetos. Este nombre tuvo
que ser cambiado debido a problemas de marca, pasando a llamarse C#.1 La
biblioteca de clases de la plataforma .NET fue migrada entonces al nuevo
lenguaje, este después fue modificado por Joseth M.
Hejlsberg lideró el proyecto de desarrollo de C#. Anteriormente, ya había
participado en el desarrollo de otros lenguajes como Turbo Pascal, Delphi y J++.
El nombre C Sharp fue inspirado por el signo '#' que se compone de cuatro signos
'+' pegados. En inglés, "C Sharp" también significa "Do Sostenido", que en música
indica que una nota debe ser un semitono más aguda. Esto hace referencia al
lenguaje C++, donde el símbolo "++" indica que se debe incrementar la variable en
1 tras ser evaluada.
C# Versión 1
Cuando echas la vista atrás, la primera versión de C# sí que se parecía un montón
a Java. Como parte de sus objetivos de diseño escritos para ECMA, buscaba ser
un "lenguaje orientado a objetos de uso general moderno y sencillo". En aquel
momento, podría haber hecho algo peor que mirar hacia Java para alcanzar
dichos fines.
C# Versión 2
Aquí las cosas se empiezan a poner interesantes. Repasemos algunas de las
principales características de C# 2.0, lanzado en 2005, junto con Visual Studio
2005 (no te olvides de echarle un vistazo al libro escrito por el creador de
NDepend, Patrick Smacchia, sobre .NET 2.0, disponible en inglés y francés).
Genéricos
Tipos parciales
Métodos anónimos
Tipos anulables
Iteradores
Covarianza y contravarianza
Aunque Microsoft puede que haya empezado con un lenguaje orientado a objetos
bastante genérico, la versión 2 de C# lo cambió todo enseguida. Se pusieron a la
tarea tras la salida de la versión inicial y fueron a por varias de las frustraciones
que causaba. Y lo hicieron muy en serio.
Con genéricos, tienes tipos y métodos que pueden operar sobre un tipo arbitrario
mientras que aún conservan seguridad de tipos. Así, por ejemplo, tener una
List<T> te permite tener una List<string> o una List<int> y realizar operaciones
seguras de tipo en esas cadenas de caracteres o ints mientras iteras por ellas.
Esto es mucho mejor que crear clases derivadas de listas (ListInt?) o convertir a
un Objeto para cada operación.
Y aún así, Microsoft seguía intentando ponerse a la altura de Java. Java ya había
liberado versiones que incluían genéricos e iteradores. Pero eso cambiaría pronto
a medida que los lenguajes seguían una evolución diferente.
C# Versión 3
La versión 3 de C# apareció a finales de 2007, junto con Visual Studio 2008,
aunque la funcionalidad completa aparecería con la versión 3.5 de C#. ¡Y menuda
actualización resultó ser!. Yo me atrevería a afirmar que ese momento consolidó a
C# como un lenguaje de programación realmente formidable. Veamos algunas de
sus características principales en esta versión:
Propiedades auto-implementadas
Tipos anónimos
Expresiones de consulta - LINQ
Expresiones lambda
Árboles de expresión
Métodos de extensión
Con mirada retrospectiva, muchas de estas características parecen tanto
inevitables como inseparables. De hecho, no es fácil destacar una sobre otra ya
que todas encajan estratégicamente muy bien juntas.
Otros haciendo este mismo análisis no tendrán ese problema, y dirán que, de
largo, la novedad más importante de la versión 3 de C# fue las expresiones de
consulta, también conocidas como LINQ (Language INtegrated Query).
Yo creo que hay más matices y sutilezas puesto que, desde mi punto de vista, los
árboles de expresión, las expresiones lambda y los tipos anónimos fueron los
cimientos sobre los que se creó LINQ. Disertaciones aparte, lo cierto es que nos
encontramos con un concepto realmente revolucionario. Microsoft había
empezado a allanar el terreno para hacer de C# un lenguaje funcional orientado a
objetos híbrido.
C# Versión 4
La versión 4 de C# nació con el estigma de no suponer una innovación rompedora
como sí lo había sido su antecesora versión 3. Y es que con la versión 3, Microsoft
hizo que el lenguaje dejase de estar a la sombra de Java y empezó a destacar.
Rápidamente el lenguaje se estaba convirtiendo en una opción elegante.
Tipos dinámicos
Argumentos opcionales y con nombre
Covarianza y contravarianza genérica
Tipos interop embebidos
Los tipos interop embebidos aliviaron problemas a la hora del despliegue. La
covarianza y contravarianza genéricas te dan mucha potencia, pero son
demasiado académicas y probablemente sean más valoradas entre los creadores
de frameworks y bibliotecas. Los argumentos opcionales y con nombre te permiten
eliminar muchas sobrecargas de método y ofrecen comodidad. Pero ninguna de
estas cosas altera el paradigma propiamente dicho.
Esa distinción sí se la llevan los tipo dinámico. Con esta característica, Microsoft
introdujo en la versión 4 de C# la capacidad de anular el compilador al tiparlo en
tiempo de compilación. Es decir, al usar el tipo de referencia dinámico, te puedes
llegar a pegar un tiro en el pie como en los lenguajes de tipado dinámico como
JavaScript. Puedes crear una "x = cadena de caracteres" dinámica y luego
añadirle 6, dejando al tiempo de ejecución determinar qué diablos tiene que pasar
después.
Esto lo digo un poco de broma obviamente. Está claro que cabe la posibilidad de
cometer errores, pero también otorga mucha potencia dentro del lenguaje.
C# Versión 5
Con la versión 5 de C#, Microsoft liberó una versión con el foco muy puesto en la
innovación del lenguaje. Esta es la lista de las características más importantes:
Pero async y await son las verdaderas estrellas de esta versión. Cuando esto salió
en 2012, Microsoft cambió las reglas del juego nuevamente al meter la asincronía
en el lenguaje como un participante de primera clase. Si ya has gestionado
operaciones largas y la implementación de páginas web con retro-llamadas,
probablemente adores esta funcionalidad.
C# Versión 6
Con las versiones 3 y 5, Microsoft había hecho algunas cosas bastante
impresionantes en un lenguaje orientado a objetos (la versión 2 también, pero
estaban copiando conceptos de Java con esas funciones que introdujeron). Con la
versión 6 se alejaron de la idea de sacar una novedad dominante estrella y, en vez
de eso, liberaron muchas características para hacer felices a los usuarios del
lenguaje de programación. Aquí enumero unas cuantas:
C# Versión 7
Finalmente hemos llegado ya a la versión 7 de C#. Es la versión actual en la fecha
que se ha escrito este artículo. Tiene cosas muy chulas y revolucionarias que ya
estaban en el ADN de la versión 6, pero sin el compilador como servicio. Aquí van
las novedades de esta versión:
Variables out
Tuplas y deconstrucción
Coincidencia de patrones
Funciones locales
Extensión de la funcionalidad de miembros con una expresión como cuerpo
aparecida en C# 6
Referencias locales y devoluciones
Todas ofrecen nuevas capacidades para que los desarrolladores puedan escribir
código más limpio que nunca. En concreto, creo que Microsoft ha dado solución a
problemas que venían desde muy lejos al condensar la declaración de variables
que se pueden a usar con la palabra clave out y al permitir valores de devolución
múltiples vía tuplas.
Además, Microsoft le está dando un uso más amplio al lenguaje. .NET ahora va
dirigido a cualquier sistema operativo y tiene la vista puesta de forma firme en la
nube y en la portabilidad. Esto es lo que más ocupa la mente y el tiempo de los
diseñadores del lenguaje, además de pensar en nuevas características.
Librerías
Tipos de datos
Estructurados y definidos por el Usuario.
Hay dos clases de tipos en C#: tipos de valor y tipos de referencia. Las variables
de tipos de valor contienen directamente los datos, mientras que las variables de
los tipos de referencia almacenan referencias a los datos, lo que se conoce como
objetos. Con los tipos de referencia, es posible que dos variables hagan referencia
al mismo objeto y que, por tanto, las operaciones en una variable afecten al objeto
al que hace referencia la otra variable. Con los tipos de valor, cada variable tiene
su propia copia de los datos y no es posible que las operaciones en una variable
afecten a la otra (excepto en el caso de las variables de parámetro ref y out).
Los tipos de valor de C# se dividen en tipos simples, tipos de enumeración, tipos
de estructura y tipos de valores NULL. Los tipos de referencia de C# se dividen en
tipos de clase, tipos de interfaz, tipos de matriz y tipos delegados.
A continuación, se proporciona información general del sistema de tipos de C#.
Tipos de valor
Tipos simples
Entero con signo: sbyte, short, int,long
Entero sin signo: byte, ushort, uint,ulong
Caracteres Unicode: char
Punto flotante de IEEE: float, double
Decimal de alta precisión: decimal
Booleano: bool
Tipos de enumeración
Tipos definidos por el usuario con el formato enum E {...}
Tipos de estructura
Tipos definidos por el usuario con el formato struct S {...}
Tipos de valor que aceptan valores NULL
Extensiones de todos los demás tipos de valor con un valor null
Tipos de referencia
Tipos de clase
Clase base definitiva de todos los demás tipos: object
Cadenas Unicode: string
Tipos definidos por el usuario con el formato class C {...}
Tipos de interfaz
Tipos definidos por el usuario con el formato interface I {...}
Tipos de matriz
Unidimensional y multidimensional; por ejemplo, int[] y int[,]
Tipos delegados
Tipos definidos por el usuario con el formato delegate int D(...)
Los ocho tipos enteros proporcionan compatibilidad con valores de 8, 16, 32 y 64
bits en formato con o sin signo.
Los dos tipos de punto flotante, float y double, se representan mediante los
formatos IEC-60559 de precisión sencilla de 32 bits y de doble precisión de 64
bits, respectivamente.
El tipo decimal es un tipo de datos de 128 bits adecuado para cálculos financieros
y monetarios.
El tipo bool de C# se utiliza para representar valores booleanos; valores que son
true o false.
El procesamiento de caracteres y cadenas en C# utiliza la codificación Unicode. El
tipo char representa una unidad de código UTF-16 y el tipo string representa una
secuencia de unidades de código UTF-16.
Resume los tipos numéricos de C#.
Entero con signo
sbyte: 8 bits, de -128 a 127
short: 16 bits, de -32,768 a 32,767
int : 32 bits, de -2,147,483,648 a 2,147,483,647
long: 64 bits, de -9,223,372,036,854,775,808 a 9,223,372,036,854,775,807
Entero sin signo
byte: 8 bits, de 0 a 255
ushort: 16 bits, de -0 a 65,535
uint: 32 bits, de -0 a 4,294,967,295
ulong: 64 bits, de -0 a 18,446,744,073,709,551,615
Punto flotante
float: 32 bits, de 1.5 × 10-45 a 3.4 × 1038, precisión de 7 dígitos
double: 64 bits, de 5.0 × 10-324 a 1.7 × 10308, precisión de 15 dígitos
Decimal
decimal: 128 bits, de al menos -7.9 × 10-28 a 7.9 × 1028, con una precisión de al
menos 28 dígitos
Los programas de C# utilizan declaraciones de tipos para crear nuevos tipos. Una
declaración de tipos especifica el nombre y los miembros del nuevo tipo. Cinco de
las categorías de tipos de C# las define el usuario: tipos de clase, tipos de
estructura, tipos de interfaz, tipos de enumeración y tipos delegados.
A tipo class define una estructura de datos que contiene miembros de datos
(campos) y miembros de función (métodos, propiedades y otros). Los tipos de
clase admiten herencia única y polimorfismo, mecanismos por los que las clases
derivadas pueden extender y especializar clases base.
Un tipo struct es similar a un tipo de clase, por el hecho de que representa una
estructura con miembros de datos y miembros de función. Sin embargo, a
diferencia de las clases, las estructuras son tipos de valor y no suelen requerir la
asignación del montón. Los tipos struct no admiten la herencia especificada por el
usuario y todos los tipos de struct se heredan implícitamente del tipo object.
Un tipo interface define un contrato como un conjunto con nombre de miembros de
función públicos. Un class o struct que implementa un interface debe proporcionar
implementaciones de miembros de función de la interfaz. Un interface puede
heredar de varias interfaces base, y un class o struct pueden implementar varias
interfaces.
Un tipo delegate representa las referencias a métodos con una lista de parámetros
determinada y un tipo de valor devuelto. Los delegados permiten tratar métodos
como entidades que se puedan asignar a variables y se puedan pasar como
parámetros. Los delegados son análogos a los tipos de función proporcionados
por los lenguajes funcionales. Son similares al concepto de punteros de función en
otros lenguajes, pero a diferencia de los punteros de función, los delegados están
orientados a objetos y presentan seguridad de tipos.
Los tipos class, struct, interface y delegate admiten parámetros genéricos,
mediante los cuales se pueden parametrizar con otros tipos.
Un tipo enum es un tipo distinto con constantes con nombre. Cada tipo enum tiene
un tipo subyacente, que debe ser uno de los ocho tipos enteros. El conjunto de
valores de un tipo enum es igual que el conjunto de valores del tipo subyacente.
C# admite matrices unidimensionales y multidimensionales de cualquier tipo. A
diferencia de los tipos enumerados anteriormente, los tipos de matriz no tienen
que ser declarados antes de usarlos. En su lugar, los tipos de matriz se crean
mediante un nombre de tipo entre corchetes. Por ejemplo, int[] es una matriz
unidimensional de int, int[,] es una matriz bidimensional de int y int[][] es una matriz
unidimensional de la matriz unidimensional de int.
Los tipos de valor NULL tampoco tienen que ser declarados antes de usarlos.
Para cada tipo de valor distinto de NULL T, existe un tipo de valor NULL
correspondiente T?, que puede tener un valor adicional, null. Por ejemplo, int? es
un tipo que puede contener cualquier número entero de 32 bits o el valor null.
El sistema de tipos de C# está unificado, de tal forma que un valor de cualquier
tipo puede tratarse como un object. Todos los tipos de C# directa o indirectamente
se derivan del tipo de clase object, y object es la clase base definitiva de todos los
tipos. Los valores de tipos de referencia se tratan como objetos mediante la
visualización de los valores como tipo object. Los valores de tipos de valor se
tratan como objetos mediante la realización de operaciones de conversión boxing
y operaciones de conversión unboxing. En el ejemplo siguiente, un valor int se
convierte en object y vuelve a int.
Cuando se convierte un valor de un tipo de valor al tipo object, se asigna una
instancia object, también denominada "box", para contener el valor, y el valor se
copia en dicho box. Por el contrario, cuando se convierte una referencia object en
un tipo de valor, se comprueba si la referencia object es un box del tipo de valor
correcto y, si la comprobación es correcta, se copia el valor del box.
El sistema de tipos unificado de C# conlleva efectivamente que los tipos de valor
pueden convertirse en objetos "a petición". Debido a la unificación, las bibliotecas
de uso general que utilizan el tipo object pueden usarse con tipos de referencia y
tipos de valor.
Hay varios tipos de variables en C#, entre otras, campos, elementos de matriz,
variables locales y parámetros. Las variables representan ubicaciones de
almacenamiento, y cada variable tiene un tipo que determina qué valores pueden
almacenarse en la variable, como se muestra a continuación.
Tipo de valor distinto a NULL
Un valor de ese tipo exacto
Tipos de valor NULL
Un valor null o un valor de ese tipo exacto
objeto
Una referencia null, una referencia a un objeto de cualquier tipo de referencia o
una referencia a un valor de conversión boxing de cualquier tipo de valor
Tipo de clase
Una referencia null, una referencia a una instancia de ese tipo de clase o una
referencia a una instancia de una clase derivada de ese tipo de clase
Tipo de interfaz
Una referencia null, una referencia a una instancia de un tipo de clase que
implementa dicho tipo de interfaz o una referencia a un valor de conversión boxing
de un tipo de valor que implementa dicho tipo de interfaz
Tipo de matriz
Una referencia null, una referencia a una instancia de ese tipo de matriz o una
referencia a una instancia de un tipo de matriz compatible
Tipo delegado
Una referencia null o una referencia a una instancia de un tipo delegado
compatible
Constantes
Las constantes son valores inmutables, y por tanto no se pueden cambiar.
const
Cuando se declara una constante con la palabra clave const, también se debe
asignar el valor. Tras esto, la constante queda bloqueada y no se puede cambiar.
Son implícitamente estáticas (static).
readonly
Debe compilarse con la opción /unsafe Para establecer esta opción del compilador
en el entorno de desarrollo de Visual Studio:
Abra la página Propiedades del proyecto.
Haga clic en la página de propiedades Generar.
Active la casilla de verificación Permitir código no seguro.
La opción /unsafe del compilador permite compilar el código que utiliza la palabra
clave unsafe.
Para compilar in.cs en modo no seguro, ejecute: csc /unsafe in.cs
Se pueden definir como no seguros los métodos, tipos y bloques de código.
UNSAFE
Podemos usar el modificador unsafe en la declaración de un tipo o un miembro así
toda la extensión textual del tipo o del miembro se considera, por lo tanto, como
contexto no seguro.
TIPOS DE PUNTEROS
En un contexto no seguro, un tipo puede ser un tipo de puntero, un tipo de valor o
un tipo de referencia.
Un puntero puede ser tipo sbyte, byte, short, ushort, int, uint, long, ulong, char,
float, double, decimal o bool, cualquier tipo de enum, cualquier tipo de puntero
(pointer), cualquier tipo de struct definido por el usuario que sólo contenga campos
de tipos no administrados.
Para declarar varios punteros en una misma declaración debe tener en cuenta que
únicamente debe escribir * junto al tipo subyacente, no debe escribirse como
prefijo en cada nombre de puntero.
Estructuras
Al igual que las clases, los structs son estructuras de datos que pueden contener
miembros de datos y miembros de función, pero a diferencia de las clases, los
structs son tipos de valor y no requieren asignación del montón. Una variable de
un tipo de struct almacena directamente los datos del struct, mientras que una
variable de un tipo de clase almacena una referencia a un objeto asignado
dinámicamente. Los tipos struct no admiten la herencia especificada por el usuario
y todos los tipos de struct se heredan implícitamente del tipo ValueType, que a su
vez se hereda implícitamente de object.
Los structs son particularmente útiles para estructuras de datos pequeñas que
tengan semánticas de valor. Los números complejos, los puntos de un sistema de
coordenadas o los pares clave-valor de un diccionario son buenos ejemplos de
structs. El uso de un struct en lugar de una clase para estructuras de datos
pequeñas puede suponer una diferencia sustancial en el número de asignaciones
de memoria que realiza una aplicación. Por ejemplo, el siguiente programa crea e
inicializa una matriz de 100 puntos. Si Point se implementa como una clase, se
crean instancias de 101 objetos distintos: uno para la matriz y uno por cada uno de
los 100 elementos.
Los constructores structs se invocan con el operador new, similar a un constructor
de clase. Sin embargo, en lugar de asignar dinámicamente un objeto en el montón
administrado y devolver una referencia a él, un constructor de structs simplemente
devuelve el valor del struct propiamente dicho (normalmente en una ubicación
temporal en la pila) y este valor se copia luego cuando es necesario.
Con las clases, es posible que dos variables hagan referencia al mismo objeto y,
que, por tanto, las operaciones en una variable afecten al objeto al que hace
referencia la otra variable. Con los struct, cada variable tiene su propia copia de
los datos y no es posible que las operaciones en una afecten a la otra.
Read( ) --> Lee los datos que ingresa el usuario a través del Teclado con formato
texto.
ReadLine( )--> A diferencia del método anterior lee y salta una línea.
WriteLine( )--> A diferencia del método anterior escribe en pantalla y salta una
línea.
Pre-Procesadores
El preprocesado es un paso previo a la compilación mediante el que es posible
controlar la forma en que se realizará ésta. El preprocesador es el módulo auxiliar
que utiliza el compilador para realizar estas tareas, y lo que finalmente el
compilador compila es el resultado de aplicar el preprocesador al fichero de texto
fuente, resultado que también es un fichero de texto.
A pesar de que C# no tenga un preprocesador de código dedicado (tal como lo
tiene C), aún podemos hacer uso de algunas instrucciones que modifican el
comportamiento del compilador, a estas palabras se les conoce como directivas.
En C# se han eliminado la mayoría de características negativas de versiones
previas, que provocaban errores difíciles de detectar (macros, directivas de
inclusión, etc.) y prácticamente sólo se usa para permitir realizar compilaciones
condicionales de código.
El preprocesador no interpreta de ninguna manera el código fuente del fichero,
sino que sólo interpreta de dicho fichero lo que se denominan directivas de
preprocesado. Estas directivas son líneas de texto del fichero fuente que se
caracterizan porque en ellas el primer carácter no blanco que aparece es una
almohadilla (carácter #)
La principal utilidad del preprocesador en C# es la de permitir determinar cuáles
regiones de código de un fichero fuente se han de compilar. Para ello, lo que se
hace es encerrar las secciones de código opcionales dentro de directivas de
compilación condicional, de modo que sólo se compilarán si determinados
identificadores de preprocesado están definidos. Es importante señalar que
cualquier definición de identificador ha de preceder a cualquier aparición de código
en el fichero fuente.
Para definir un identificador de preprocesado y que además permita que dicha
definición sólo sea válida en una compilación en concreto, se debe pasar al
compilador en su llamada la opción /d:<nombreIdentificador> (forma abreviada
de /define:<nombreIdentificador>), caso en que durante la compilación se
considerará que al principio de todos los ficheros fuente a compilar se encuentra
definido el identificador indicado. Las siguientes tres formas de llamar al
compilador son equivalentes y definen identificadores de preprocesado de
nombres PRUEBA y TRAZA durante la compilación de un fichero fuente de
nombre ejemplo.cs:
Si se trabaja con Visual Studio.NET en lugar de directamente con el compilador en
línea de comandos, entonces puede conseguir mismo efecto a través de View ->
Property Pages -> Configuration Options -> Build -> Conditional Compilation
Constants, donde nuevamente usado el punto y coma (;) o la coma (,) como
separadores, puede definir varias constantes. Para que todo funcione bien, antes
de seleccionar View ha de seleccionar en el Solution Explorer (se abre con View -
> Solution Explorer) el proyecto al que aplicar la definición de las constantes.
Finalmente, respecto al uso de #define sólo queda comentar que es posible definir
varias veces una misma directiva sin que ello provoque ningún tipo de error en el
compilador, lo que permite que podamos pasar tantos valores a la opción /d del
compilador como queramos sin temor a que entren en conflicto con identificadores
de preprocesado ya incluidos en las fuentes a compilar.
Eliminación de identificadores de preprocesado
Del mismo modo que es posible definir identificadores de preprocesado, también
es posible eliminar definiciones de este tipo de identificadores previamente
realizadas. En caso de que se intente eliminar con esta directiva un identificador
que no haya sido definido o cuya definición ya haya sido eliminada no se producirá
error alguno, sino que simplemente la directiva de eliminación será ignorada.
Al igual que ocurría con las directivas #define, no se puede incluir código fuente
antes de las directivas #undef, sino que, todo lo más, lo único que podrían
incluirse antes que ellas serían directivas de preprocesado.
Compilación condicional
Como se ha repetido varias veces a lo largo del tema, la principal utilidad del
preprocesador en C# es la de permitir la compilación de código condicional, lo que
consiste en sólo permitir que se compile determinadas regiones de código fuente
si las variables de preprocesado definidas cumplen alguna condición determinada.
Generación de avisos y errores
El preprocesador de C# también ofrece directivas que permiten generar avisos y
errores durante el proceso de preprocesado en caso de que ser interpretadas por
el preprocesador.
La directiva #warning lo que hace al ser procesada es provocar que el compilador
produzca un mensaje de aviso que siga el formato estándar usado por éste para
ello y cuyo texto descriptivo tenga el contenido indicado en <mensajeAviso>; y
#error hace lo mismo, pero provocando un mensaje de error en vez de uno de
aviso.
Usando directivas de compilación condicional se puede controlar cuando se han
de producir estos mensajes, cuando se han de procesar estas directivas. De
hecho, la principal utilidad de estas directivas es permitir controlar errores de
asignación de valores a los diferentes identificadores de preprocesado de un
código.
El preprocesador de C# considera que los mensajes asociados a directivas
#warning o #error son todo el texto que se encuentra tras el nombre de dichas
directivas y hasta el final de la línea donde éstas aparecen. Por tanto, todo
comentario que se incluya en una línea de este tipo será considerado como parte
del mensaje a mostrar, y no como comentario como tal.
Cambios en la numeración de líneas
Por defecto el compilador enumera las líneas de cada fichero fuente según el
orden normal en que estas aparecen en el mismo, y este orden es el que sigue a
la hora de informar de errores o de avisos durante la compilación. Sin embargo,
hay situaciones en las que interesa cambiar esta numeración.
El valor indicado en "<nombreFichero>" es opcional, y en caso de aparecer indica
el nombre que se ha de considerar que tiene el fichero a la hora de dar mensajes
de error.
Aunque en principio puede parecer que esta directiva es de escasa utilidad, lo
cierto es que suele venir bastante bien para la escritura de compiladores y otras
herramientas que generen código en C# a partir de código escrito en otros
lenguajes.
Depuración y Errores
El proceso de depuración y de corrección de los errores, siempre es
recomendable hacerlo a través del compilador, pues otorga herramientas más
agiles a través del código y evita que se formen problemas en cadena, por
cambiar, modificar y mejorar partes del programa.
Hay dos tipos básicos de configuración de compilación: Depuración y lanzamiento.
La configuración de Depuración genera un archivo ejecutable más lento y más
grande que permite una experiencia de depuración en tiempo de ejecución
interactiva y más completa. Nunca se debe enviar el archivo ejecutable de
Depuración. La configuración de Release crea un archivo ejecutable más rápido y
optimizado, adecuado para enviar (al menos desde la perspectiva del compilador).
La configuración de compilación predeterminada es Depuración.
Puede observar el proceso de compilación en la ventana de Salida, Aquí se
muestran los errores, las advertencias y las operaciones de compilación. Si tiene
errores (o advertencias por encima de un nivel configurado), se produce un error
de compilación. Se puede hacer clic en los errores y advertencias para ir a la línea
donde se produjeron.
Hay dos ventanas con pestañas en la ventana de resultados, debajo del editor de
compilación: la ventana Salida, que contiene la salida sin formato del compilador
(incluidos los mensajes de error), y la ventana Lista de errores, que proporciona
una lista, que se puede ordenar y filtrar, de todos los errores y advertencias.
Compilación y ejecución
El proceso de compilación y ejecución se realiza usualmente utilizando algún
compilador, en el caso de C Sharp el más común es Visual Studio. Para que el
proceso pueda ser realizado de forma correcta, se suele realizar una previa
limpieza de errores, usando preprocesadores y realizando distintas depuraciones,
donde el objetivo es dejar el código lo mas limpio posible para su compilación final
y posterior ejecución. Dicho proceso variara en tiempo y dificultad en dependencia
de que tan extenso o corto sea el código en cuestión.
Compilación y ejecución de línea de comandos
Se puede compilar el programa con la línea de comandos o mediante la interfaz
de algún compilador previamente descargado.
Para realizar el proceso desde un símbolo del sistema se debe pegar el código del
procedimiento anterior en cualquier editor de texto y guardar el archivo como un
archivo de texto. Asignarle al archivo el nombre que corresponda con su función y
recordar que los archivos de código fuente de C# usan la extensión .cs.
Conclusión
Bibliografía
https://www.genbeta.com/desarrollo/diez-dos-librerias-open-source-
recomendables-para-net
https://www.campusmvp.es/recursos/post/historia-del-lenguaje-c-sharp-pasado-
presente-y-evolucion.aspx
https://docs.microsoft.com/es-es/dotnet/csharp/tour-of-csharp/types-and-variables
https://docs.microsoft.com/es-es/dotnet/csharp/language-reference/operators/
https://miguelangelchirinos.wordpress.com/2012/07/10/punteros-en-c/
https://programacion.net/articulo/el_lenguaje_de_programacion_c_167/4
https://docs.microsoft.com/es-es/dotnet/csharp/programming-guide/inside-a-
program/hello-world-your-first-program