Académique Documents
Professionnel Documents
Culture Documents
C# es un lenguaje de programación bastante semejante en lo que a sintaxis se refiere a C++. Por otra parte,
C++ puede considerarse como un superconjunto de C.
Por lo tanto, este documento no pretende ser un estudio exhaustivo de los tipos de datos, estructuras de
control y librerías de C#, sino una muestra de un conjunto de ejemplos de código escritos en ambos lenguajes.
No se trata entonces de justificar todo cuanto se escribe, con cuidado de haber dado previamente las
definiciones necesarias, sino de mostrar de forma intuitiva las diferencias sintácticas de ambos lenguajes en
aquellos aspectos en que nos encontramos familiarizados en C. En otras palabras, con esto se quiere justificar
la posible falta de rigor expositivo.
Todo lo que aquí se muestra, se muestra de forma muy simple, procurando huir de elementos muy
diferenciales. La forma de proceder utilizada en C# para estos ejemplos no es la más adecuada, sino la más
aproximada a C, sin exponer en ningún caso otras alternativas mejores disponibles.
Se parte de la base de que el alumno “sabe C”, y este documento es una primera toma de contacto con el
nuevo lenguaje partiendo de ese supuesto. Por lo tanto, si el alumno no comprende los ejemplos propuestos
en C, debería repasar “un poco” los contenidos de las asignaturas previas.
Para simplificar el código al máximo y quedarnos con lo esencial en lo que a sintaxis se refiere, todos los
ejemplos están realizados en modo consola, tanto en C como en C#.
1. Hola mundo.
Hola mundo en C Hola mundo en C#
#include <stdio.h> using System;
El ejemplo de la izquierda muestra un típico programa ‘Hola mundo’ en C. Es necesaria la directiva #include
para poder usar la librería que define la función printf. La función main recibe el número de argumentos
pasados por la línea de comandos, y el array de cadenas de caracteres que los contiene.
El ejemplo de la derecha muestra un típico programa ‘Hola mundo’ escrito en C#. Es necesaria la línea using
System para acceder a la clase Console y al método Write. La función Main en este caso recibe
directamente un array de strings (cadenas de caracteres en C#), como en el caso anterior. El número de
argumentos está implícito en el array de C#.
En C#, además, es necesario un espacio de nombres (namespace Simple) y una clase (class CSimple),
para poder contener la función Main, que será el punto de entrada del programa al ejecutarse.
Desde un punto de vista práctico y, si se quiere, simplista, dentro de la clase CSimple pondremos todo lo que
pondríamos en el fichero fuente de C a excepción de las directivas include y define.
2. Declaración de variables elementales e impresión de información por pantalla.
Ejemplo en C
#include <stdio.h>
namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
int i = 4;
double k = 3.74;
string C = "Hola que tal";
Vemos que las declaraciones de variables (por ahora) son semejantes en ambos lenguajes. Se ha declarado
en C un array de caracteres, aunque no es un tipo simple, para comprobar su equivalencia con el tipo string
de C#.
En C# se ha hecho la impresión, usando el método Write de Console, de dos formas diferentes: La primera
usa {0}, {1}, etc. para indicar los argumentos a imprimir, mostrando su semejanza con los %d y %f, de C. La
segunda usa el operador de concatenación de strings (+), para unir los fragmentos que se desean imprimir.
namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
string Cadena;
int DatoEntero;
double DatoReal;
Vemos que toda la entrada por la consola se realiza a través de la función ReadLine de Console. ReadLine
lee hasta que llega un <CR> (ENTER) del teclado, y retorna siempre un string. Es como si utilizáramos
scanf (“%s”,Cad) siempre, y luego convirtiéramos la cadena de caracteres (con las funciones atoi y
atof).
Las operaciones de la clase Convert permiten convertir los strings leídos por el teclado al tipo de datos
deseado.
4. Operadores.
En general, los operadores de C son semejantes a los de C#. Tenemos la suma, la resta, el producto, la
división (+, -, *, /, %), también operadores de comparación (==, !=, >, …), operadores de bits, etc. La siguiente
tabla resume los operadores de C#.
Categorías Operadores
Aritméticos + - * / %
Lógicos (booleanos y bit a bit) & | ^ ! ~ && || false true
Concatenación de cadenas +
Incremento y decremento ++ --
Desplazamiento << >>
Relacionales == != < > <= >=
Asignación = += -= *= /= %= &= |= ^= <<= >>=
Acceso a miembros .
Indización []
Conversión de tipos explícita ()
Condicional ?:
Concatenación y eliminación de delegados + -
Creación de objetos new
Información de tipos is sizeof typeof
Control de excepciones de desbordamiento checked unchecked
Direccionamiento indirecto y dirección * -> [] &
Un if se escribe igual en C que en C#, un for y un while también, etc. Por el momento no debemos
prestarle más atención a este tema, aunque debemos saber que C# es más potente que C en este aspecto.
namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
int[] Numeros = new int[5];
int i;
Como vemos en este caso, el manejo de arrays es bastante semejante en ambos lenguajes. La diferencia
fundamental esta en la declaración, como puede verse claramente. En el caso de C#, un array ha de
“construirse”, con el operador new. Por ahora no vamos a ahondar en el tema de la “construcción”, basta con
“creer” que es así, y utilizarlo, y dentro de poco tiempo lo entenderemos mejor.
Ejemplo en C
#include <stdio.h>
namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
int[] Numeros = new int[] {1,2,3,4,5};
En el ejemplo anterior se muestra la declaración estática de un array, en ambos lenguajes. Vemos también la
declaración de la variable de control dentro del for, algo muy habitual en el estilo de programación de C#.
7. Declaración y llamadas a funciones.
Ejemplo en C
#include <stdio.h>
namespace Simple
{
class CSimple
{
static int UnaFuncion (int p, int q)
{
return (p+q);
}
[STAThread]
static void Main(string[] args)
{
int Resultado = UnaFuncion (7,2);
Console.WriteLine (Resultado);
}
}
}
Vemos que tanto la declaración y las llamadas a funciones son muy semejantes en ambos lenguajes. En C# se
puede inicializar una variable con el resultado de una expresión ejecutable, a diferencia de C (en C++ esto
también está permitido).
8. Paso de parámetros por referencia.
Ejemplo en C
#include <stdio.h>
UnaFuncion (&Dato);
printf ("%d\n",Dato);
}
Ejemplo en C#
using System;
namespace Simple
{
class CSimple
{
static void UnaFuncion (ref int p)
{
p = p + 2;
}
[STAThread]
static void Main(string[] args)
{
int Dato = 7;
UnaFuncion (ref Dato);
Console.WriteLine (Dato);
}
}
}
Un uso muy común de los punteros en C es el paso de parámetros por referencia. Sin embargo, en C# los
punteros son considerados como “algo no seguro”, y hay otras maneras de efectuar esta operación. Vemos en
el ejemplo que el parámetro se marca con el modificador ‘ref’, y que también se indica al invocar la función.
Para enviar parámetros por referencia, ya no son necesarios los punteros, y no han de utilizarse para ello.
El paso de estructuras por referencia también es diferente en C# con respecto a C. En C# es preferible utilizar
objetos (elementos de clases) en lugar de estructuras, y en este caso, el paso de parámetros es siempre por
referencia, aunque no se indique. Esto lo veremos en un ejemplo posterior.
9. Uso de punteros.
Ya se ha comentado anteriormente que no es necesario utilizar punteros en C#. Si aún así queremos seguir
utilizándolos (no en esta asignatura), es necesario marcar las funciones que los usen como “no seguras”.
Ejemplo en C#
using System;
namespace Simple
{
class CSimple
{
unsafe static void UnaFuncion (int* p)
{
*p = *p + 2;
}
[STAThread]
unsafe static void Main(string[] args)
{
int Dato = 7;
UnaFuncion (&Dato);
Console.WriteLine (Dato);
}
}
}
Para que el ejemplo anterior funcione, se ha de compilar el proyecto con el parámetro /unsafe, estableciendo
la propiedad de código no seguro de la siguiente manera:
typedef struct
{
char Nombre[80];
int Edad;
} Persona;
P.Edad = 39;
strcpy (P.Nombre,"Ruben Hidalgo");
printf ("%d - %s\n",P.Edad,P.Nombre);
}
Ejemplo en C#
using System;
namespace Simple
{
class CSimple
{
class Persona
{
public string Nombre;
public int Edad;
}
[STAThread]
static void Main(string[] args)
{
Persona P = new Persona();
P.Edad = 39;
P.Nombre = "Ruben Hidalgo";
Console.WriteLine ("{0} - {1}",P.Edad,P.Nombre);
}
}
}
En C# son preferibles las clases a las estructuras. En el ejemplo anterior vemos la declaración de la clase
Persona, semejante a la estructura equivalente de C. Los miembros de la clase deben ser públicos (public)
para que la clase sea “equivalente” a la estructura.
Salvo en la declaración de la variable de tipo Persona (revisar el ejemplo anterior que mostraba la declaración
de un array), el uso de una clase definida de este modo y una estructura es prácticamente el mismo.
11. Paso de estructuras/objetos por parámetro.
Ejemplo en C
#include <stdio.h>
#include <string.h>
typedef struct
{
char Nombre[80];
int Edad;
} Persona;
P.Edad = 39;
strcpy (P.Nombre,"Ruben Hidalgo");
printf ("%d - %s\n",P.Edad,P.Nombre);
TratarPersona (&P);
printf ("%d - %s\n",P.Edad,P.Nombre);
}
Ejemplo en C#
using System;
namespace Simple
{
class CSimple
{
class Persona
{
public int Edad;
public string Nombre;
}
[STAThread]
static void Main(string[] args)
{
Persona P = new Persona();
P.Edad = 39;
P.Nombre = "Ruben Hidalgo";
Console.WriteLine ("{0} - {1}",P.Edad,P.Nombre);
TratarPersona (P);
Console.WriteLine ("{0} - {1}",P.Edad,P.Nombre);
}
}
}
Tal como se comentaba en un punto anterior, el paso de objetos es siempre por referencia, y vemos en este
ejemplo que la edad de la persona pasada por parámetro queda modificada, tal como en el ejemplo
equivalente en C.
El paso de estructuras por valor funciona en C, siempre y cuando la estructura no contenga ningún puntero o
elemento dinámico, ya que entonces los resultados pueden ser impredecibles. Por este motivo C# no lo
soporta.
12. Arrays dinámicos.
Ejemplo en C
#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef struct
{
char Nombre[80];
int Edad;
} Persona;
typedef struct
{
Persona* Gente;
int NumPersonas;
} Personas;
Persona A;
A.Edad = 15;
strcpy (A.Nombre,"Manolo");
AgregarPersona (&K,&A);
A.Edad = 25;
strcpy (A.Nombre,"Filomeno");
AgregarPersona (&K,&A);
A.Edad = 48;
strcpy (A.Nombre,"Martirio");
AgregarPersona (&K,&A);
ImprimirPersonas (&K);
EliminarPersona (&K,1);
ImprimirPersonas (&K);
FinPersonas (&K);
}
C# considera estas operaciones como “no seguras”, y ofrece tipos de datos para realizar estas tareas de forma
segura. Además, dichos tipos de datos son mucho más fáciles de utilizar.
Ejemplo en C#
using System;
using System.Collections;
namespace Simple
{
class CSimple
{
class Persona
{
public int Edad;
public string Nombre;
}
[STAThread]
static void Main(string[] args)
{
ArrayList K = new ArrayList ();
P = new Persona();
P.Edad = 25;
P.Nombre = "Filomeno";
K.Add (P);
P = new Persona();
P.Edad = 48;
P.Nombre = "Martirio";
K.Add (P);
ImprimirPersonas (K);
K.RemoveAt (1);
ImprimirPersonas (K);
}
}
}
Vemos que un objeto ArrayList tiene las operaciones necesarias para agregar y eliminar elementos, igual
que el tratamiento implementado por malloc, realloc y free. Un objeto ArrayList “se inicializa solo” y
“se destruye solo”. Pero esto es un tema sobre el que incidiremos profundamente cuando veamos la
programación orientada al objeto, ahora no es relevante.
13. Acceso a ficheros de texto en modo lectura.
Ejemplo en C
#include <stdio.h>
#include <stdlib.h>
F = fopen ("C:\\Texto.txt","r");
while (!feof(F))
{
fscanf (F,"%c",&c);
printf ("%c",c);
}
fclose (F);
}
Ejemplo en C#
using System;
using System.IO;
namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
FileStream F = new FileStream ("C:\\Texto.txt",FileMode.Open,
FileAccess.Read);
while (F.Position < F.Length)
{
char c = (char) F.ReadByte ();
Console.Write (c);
}
F.Close ();
}
}
}
El ejemplo anterior muestra cómo leer carácter a carácter un fichero de texto. Aquí terminan las semejanzas
entre C y C# en este aspecto. La función fscanf permite ir leyendo datos separados por espacios,
convirtiendo los tipos de la forma deseada. Sin embargo, un objeto FileStream sólo permite leer caracteres o
bloques de ellos, no es capaz de ir leyendo de separador en separador.
Ejemplo en C#
static void Main(string[] args)
{
StreamReader F = new StreamReader ("C:\\Texto.txt");
for (;;)
{
string s = F.ReadLine ();
if (s == null) break;
Console.WriteLine ("Linea: " + s);
}
F.Close ();
}
El ejemplo anterior usa un objeto StreamReader para leer el fichero de texto, línea a línea.
Ejemplo en C#
using System;
using System.IO;
namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
StreamReader F = new StreamReader ("C:\\Texto.txt");
for (;;)
{
string s = F.ReadLine ();
if (s == null) break;
El ejemplo anterior va leyendo del fichero línea a línea, y utiliza el método Split del objeto string para
separar las palabras en un nuevo array de cadenas de caracteres. La salida de este programa muestra línea a
línea el fichero, y para cada línea muestra las palabras, separadas unas de otras.
A partir de aquí, y con las operaciones del objeto Convert (Convert.ToInt32, Convert.ToDouble, etc),
se puede emular el funcionamiento de un fscanf con formato.
14. Acceso a ficheros de texto en modo escritura.
Ejemplo en C
#include <stdio.h>
#include <stdlib.h>
F = fopen ("C:\\TextoSalida.txt","wt");
fprintf (F,"Esto es una cosa a escribir\n");
fprintf (F,"Esto es otra cosa, que tambien salta de linea\n");
fprintf (F,"Impresion de datos: %d %f %s\n",i,j,k);
fclose (F);
}
Ejemplo en C#
using System;
using System.IO;
namespace Simple
{
class CSimple
{
[STAThread]
static void Main(string[] args)
{
StreamWriter F = new StreamWriter ("C:\\TextoSalida.txt");
int i = 4;
double j = 45.67;
string k = "Hola";
F.WriteLine ("Impresion de datos: {0} {1} {2}",i,j,k);
F.Close ();
}
}
}
StreamWriter es el tipo de datos (objeto) de C# que permite imprimir en un fichero de texto de forma
semejante a fprintf en C.