C#
Desarrollador(es)
Microsoft
https://docs.microsoft.com/en-us/dotnet/csharp/, https://docs.microsoft.com/de-de/dotnet/csharp/, https://docs.microsoft.com/ja-jp/dotnet/csharp/, https://docs.microsoft.com/fr-fr/dotnet/csharp/ y https://docs.microsoft.com/it-it/dotnet/csharp/
Información general
Extensiones comunes .cs .csx .cshtml .razor
Paradigma Multiparadigma: estructurado, imperativo, orientado a objetos, dirigido por eventos, funcional, genérico, reflexivo
Apareció en 2000
Diseñado por Microsoft
Última versión estable 11.0 (14 de marzo de 2023 (1 año y 3 días))
Última versión en pruebas 12.0 (18 de agosto de 2023 (6 meses y 30 días))
Sistema de tipos Estático, dinámico, fuerte, seguro, nominal
Implementaciones Microsoft .NET, Microsoft .NET Core, Mono y DotGNU
Dialectos , Spec Sharp, Polyphonic C#
Influido por Java, C++, Eiffel, Modula-3, Pascal
Ha influido a D, F#, Java 5, Vala
Sistema operativo Multiplataforma

"C#" (pronunciado cii sharp en inglés) es un lenguaje de programación multiparadigma desarrollado y estandarizado por la empresa Microsoft como parte de su plataforma .NET, que después fue aprobado como un estándar por la ECMA (ECMA-334) e ISO (ISO/IEC 23270). C# es uno de los lenguajes de programación diseñados para la infraestructura de lenguaje común.

Su sintaxis básica deriva de C/C++ y utiliza el modelo de objetos de la plataforma .NET, similar al de Java, aunque incluye mejoras derivadas de otros lenguajes.

El nombre C Sharp fue inspirado por el signo , el cual se lee como sharp en inglés para notación musical. Es un juego de palabras, pues '"C#" significa, musicalmente hablando, "do sostenido", donde el símbolo # indica que una nota (en este caso do, representada por C) debe ser un semitono más alta. Esto es una metáfora de la superioridad de C# sobre su antecesor C++ y a su vez hace alusión a la misma metáfora que se ideó para dar nombre a C++.[1]​ Además, el símbolo # puede ser imaginado como la unión de cuatro símbolos +, continuando así con el sentido de progresión de los lenguajes C.

Aunque C# forma parte de la plataforma .NET, esta es una API, mientras que C# es un lenguaje de programación independiente diseñado para generar programas sobre dicha plataforma. Ya existe un compilador implementado que provee el marco Mono - DotGNU, el cual genera programas para distintas plataformas como Microsoft Windows, Unix, Android, iOS, Windows Phone, Mac OS y GNU/Linux.

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#.[2]​ La biblioteca de clases de la plataforma .NET fue migrada entonces al nuevo lenguaje, y 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++.

Tipos de datos

C# contiene veinte categorías generales de tipos de datos integrados: tipos de valor y tipos de referencia. El término tipo de valor indica que esos tipos contienen directamente sus valores. Tipos para definir números enteros:

Tipo de datos de enteros
Tipo Equivalente BCL Tamaño Intervalo Significado
byte System.Byte 8-bit (1-byte) 0 a 255 Entero sin signo
sbyte System.SByte 8-bit (1-byte) -128 a 127 Entero con signo
short System.Int16 16-bit (2-byte) -32.768 a 32.767 Entero corto con signo
ushort System.UInt16 16-bit (2-byte) 0 a 65.535 Entero corto sin signo
int System.Int32 32-bit (4-byte) -2.147.483.648 a 2.147.483.647 Entero medio con signo
uint System.UInt32 32-bit (4-byte) 0 a 4.294.967.295 Entero medio sin signo
long System.Int64 64-bit (8-byte) -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807 Entero largo con signo
ulong System.UInt64 64-bit (8-byte) 0 a 18.446.744.073.709.551.615 Entero largo sin signo
nint System.IntPtr 64-bit o 32-bit (4-byte o 8-byte) Depende de la plataforma en la que se ejecute Entero nativo con signo
nuint System.UIntPtr 64-bit o 32-bit (4-byte o 8-byte) Depende de la plataforma en la que se ejecute Entero nativo sin signo

Los tipos de coma flotante pueden representar números con componentes fraccionales. Existen dos clases de tipos de coma flotante: float y double. El tipo double es el más utilizado porque muchas funciones matemáticas de la biblioteca de clases de C# usan valores double. Quizá, el tipo de coma flotante más interesante de C# es decimal, dirigido al uso de cálculos monetarios. La aritmética de coma flotante normal está sujeta a una variedad de errores de redondeo cuando se aplica a valores decimales. El tipo decimal elimina estos errores y puede representar hasta 28 lugares decimales.

Tipo de datos de coma flotante
Tipo Equivalente BCL Tamaño Intervalo Significado
float System.Single 32-bit (4-byte) ±1.401298E−45 a ±3.402823E+38 Coma flotante corto
double System.Double 64-bit (8-byte) ±4.94065645841246E−324 a
±1.79769313486232E+308
Coma flotante largo
decimal System.Decimal 128-bit (16-byte) −7.9228162514264337593543950335 a
+7.9228162514264337593543950335
Coma flotante monetario

Los caracteres en C# no tienen un tamaño de 8 bits como en muchos otros lenguajes de programación, sino que usan un tamaño de 16 bits. Este tipo de dato se llama char y utiliza la codificación Unicode. No existen conversiones automáticas de tipo entero a char.

Tipo de datos de caracteres
Tipo Equivalente BCL Tamaño Intervalo Significado
char System.Char 16-bit (2-byte) '\u0000' a '\uFFFF' Carácter unicode

Para los tipos de datos lógicos no existen conversiones automáticas de tipo entero a bool.

Tipo de datos lógicos
Tipo Equivalente BCL Tamaño Intervalo Significado
bool System.Boolean 8-bit (1-byte) true o false Verdadero o falso

Literales

En ocasiones, resulta más sencillo usar un sistema numérico en base 16 en lugar de 10, para tal caso C# permite especificar números enteros en formato hexadecimal, y se define anteponiendo 0x, por ejemplo: 0xFF, que equivale a 255 en decimal. Asimismo, también permite poner estos en formato binario anteponiendo 0b.

C# tiene caracteres denominados secuencias de escape para facilitar la escritura con el teclado de símbolos que carecen de representación visual.

C#, al igual que C++, define un tipo de cadena de caracteres. Dentro de la cadena de caracteres se pueden usar secuencias de escape. Una cadena de caracteres puede iniciarse con el símbolo @ seguido por una cadena entre comillas ("), en tal caso, las secuencias de escape no tienen efecto, y además la cadena puede ocupar dos o más líneas.

Enteros
decimal 245, [0..9]+
hexadecimal 0xF5, 0x[0..9, A..F, a..f]+
binario 0b01001111, 0b[0..1]+
entero largo 12L
entero largo sin signo 654UL
Coma flotante
float 23.5F, 23.5f; 1.72E3F, 1.72E3f, 1.72e3F, 1.72e3f
double 23.5, 23.5D, 23.5d, 1.72E3, 1.72E3D
decimal 9.95M
Caracteres
char 'a', 'Z', '\u0231'
Cadenas
String "Hello, world"; "C:\\Windows\\", @"C:\Windows\"
Secuencias de escape
Alerta (timbre) \a
Retroceso \b
Avance de página \f
Nueva línea \n
Retorno de carro \r
Tabulador horizontal \t
Tabulador vertical \v
Nulo \0
Comilla simple \'
Comilla doble \"
Barra inversa \\

Variables

Las variables son identificadores asociados a valores. Se declaran indicando el tipo de dato que almacenará y su identificador.

Un identificador puede:

Un identificador no puede:

Declarar una variable:

int miNumero; // Declaramos la variable, pero no la inicializamos con ningún valor.

Para asignar un valor a una variable, se indica el identificador de la misma, seguido del símbolo igual (=) y el valor que queremos que almacene:

miNumero = 5; // Asignamos el valor '5' a la variable creada.

Se puede declarar y asignar un valor al mismo tiempo:

int miNumero = 5; // Declaramos la variable, y asignamos el valor '5'.

También puedes declarar una variable sin especificar el tipo de dato, utilizando el mecanismo de inferencia mediante la palabra clave var donde el compilador determina el tipo de dato que se le asignará a la variable y solamente es permitida para variables locales, no para parámetros o datos miembro.

var cadena = "Esto es un string";

var numero1 = 5;

var numero2 = 4.5;

var numero3 = 4.5D;

var objeto = new Object();

var resultado = Math.Pow(5, 2);

Las conversiones de tipo de variables en C# se representan en la siguiente tabla en donde la fila es el origen y la columna el destino.

Leyenda
Rojo Conversión incompatible (I).
Verde Conversión automática o implícita (A).
Azul Conversión explícita (E).
Conversiones de tipo de datos
byte sbyte short ushort int uint long ulong float double decimal char bool
byte E A A A A A A E E E E I
sbyte E A E A E A A E E E E I
short E E E A A A A E E E E I
ushort E E E A A A A E E E E I
int E E E E E A A E E E E I
uint E E E E E A A E E E E I
long E E E E E E E E E E E I
ulong E E E E E E E E E E E I
float E E E E E E E E A E I I
double E E E E E E E E E E I I
decimal E E E E E E E E E E I I
char E E E A A A A A A A A I
bool I I I I I I I I I I I I
long valor = 123; // Conversión implícita
long valor = (long)123; // Conversión explícita

Además de realizarse dentro de una asignación, las conversiones de tipos también tienen lugar dentro de una expresión, pues en cada operación ambos operandos deben de ser del mismo tipo. Si la conversión es del tipo implícito se efectúa el siguiente algoritmo en dicho orden:

  1. Si un operando es decimal, el otro operando se transforma a decimal.
  2. Si un operando es double, el otro operando se transforma a double.
  3. Si un operando es float, el otro operando se transforma a float.
  4. Si un operando es ulong, el otro operando se transforma a ulong.
  5. Si un operando es long, el otro operando se transforma a long.
  6. Si un operando es uint, y si el otro operando es de tipo sbyte, short o int, los dos se transforman a long.
  7. Si un operando es uint, el otro operando se transforma a uint.
  8. Si no es ninguno de los casos anteriores, los dos operandos se transforman a int.

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).

const double PI = 3.1415;

readonly

A diferencia de const, no requiere que se asigne el valor al mismo tiempo que se declara. Pueden ser miembros de la instancia o miembros estáticos de la clase (static).

readonly double E;
E = 2.71828;

Operadores

Categoría Operadores
Aritméticos + - * / %
Lógicos ! && ||
A nivel de bits & | ^ ~
Concatenación +
Incremento, decremento ++ --
Desplazamiento << >>
Relacional == != < > <= >=
Asignación = ^= <<= >>= ??=
Acceso a miembro .
Indexación [ ]
Conversión ( )
Condicional ? : ??
Creación de objeto new
Información de tipo as is sizeof typeof

Instrucciones de control

if-else

if (i == 2) 
{
    // ...
}
else if (i == 3) 
{
    // ...
}
else 
{
    // ...
}

switch

switch (i) 
{
    case 1:
        ...
        break;
    case 2:
    case 3:
        ...
        break;
    default:
        ...
        break;
}

for

for (int i = 0; i < 10; ++i) 
{
    // ...
}

while

while (i < 10) 
{
    // ...
}

do-while

do 
{
    // ...
} while (true);

foreach

foreach (char c in charList) 
{
    // ...
}

Métodos

ref

void PassRef(ref int x) 
{
    if (x == 2) 
    {
        x = 10;
    }
}

int z = 0;
PassRef(ref z);

out

void PassOut(out int x) 
{
    x = 2;
}

int z;
PassOut(out z);

params

int MaxVal(char c, params int[] nums)
{
    // ...
}

int a = 1;
MaxVal('a', 23, 3, a, -12); // El primer parámetro es obligatorio, seguidamente se pueden poner tantos números enteros como se quiera

Sobrecarga de métodos

int Suma(int x, int y) 
{
    return x + y;
}
int Suma(int x, int y, int z) 
{
    return x + y + z;
}
int Suma(params int[] numeros) 
{
    int Sumatoria = 0;
    foreach(int c in numeros)
        Sumatoria += c;
    return Sumatoria;
}

Suma(1, 2); // Llamará al primer método.
Suma(1, 2, 3); // Llamará al segundo método.
Suma(1, 2, 3, 4, 5, 6) // Llamará al tercer método.

Main

public static void Main(string[] args) 
{
    // ...
}

Matrices

Declarar una matriz:

int[] intArray = new int[5];

Declarar e inicializar una matriz (el tamaño de la matriz se puede omitir):

int[] intArray = new int[] {1, 2, 3, 4, 5};

Acceder a un elemento:

intArray[2]; // Retornará el valor '3'

Declarar una matriz multidimensional:

int[,] intMultiArray = new int[3, 2]; // 3 filas y 2 columnas

Declarar e inicializar una matriz multidimensional (el tamaño de la matriz se puede omitir):

int[,] intMultiArray = new int[,] { {1, 2}, {3, 4}, {5, 6} };

Acceder a un elemento de una matriz multidimensional:

intMultiArray[2, 0]; // Retornará el valor '5'

Más información en: Tutorial de matrices (C#) (en inglés).

Clases y objetos

Declarar una clase:

class Clase 
{
}

Iniciar una clase (también llamado crear un objeto de la clase o instanciar una clase):

Clase c = new Clase();

Constructor (como si fuera un método, pero con el nombre de su clase):

class Clase 
{
    Clase() 
    {
        // ...
    }
}

Destructor (como si fuera un método, precedido del símbolo '~'):

class Clase 
{
    ~Clase() 
    {
        // ...
    }
}

this:

class Clase 
{
    int i = 1;
    Clase() 
    {
        this.Arrancar(); // Llamará al método 'Arrancar' del objeto
    }
    void Arrancar()  
    {
        // ...
    }
}

static:

class Clase 
{
    static int i = 1;
}
Clase.i; // Retornará el valor '1'. No hace falta crear un objeto, ya que al ser 'static', pertenece a la clase.

operator:

class Clase 
{
    static int operator +(int x, int y) 
    {
        // Sobrecarga el operador '+'
        // ...
    }
    static int operator -(int x, int y) 
    {
        // Sobrecarga el operador '-'
        // ...
    }
    static int operator int(byte x) 
    {
        // Sobrecarga la conversión de tipo 'byte' a 'int'
        // ...
    }
}

Comparación de objetos:

class Clase 
{
}
Clase c1 = new Clase();
Clase c2 = new Clase();
bool b = c1 == c2; // Retornará 'false', ya que son dos objetos distintos

Cadenas de caracteres

Declarar una cadena de caracteres (como si fuera una variable de un tipo de dato como int o double):

string texto = "Cadena de caracteres";
string texto = new System.String("Cadena de caracteres"); // Equivalente al anterior

Longitud de una cadena:

string texto = "Cadena de caracteres";
int i = texto.Length; // Retornará '20'

Comparar dos cadenas:

bool b = "texto" == "texto"; // Retornará 'true', ya que ambas cadenas contienen "texto"

Concatenar cadenas:

string texto = "Cadena de" + " caracteres"; // Nótese el espacio antes de "caracteres", si no se pusiera, aparecería junto: "decaracteres"

La clase System.String, y una instancia de la misma, string, poseen algunos métodos para trabajar con cadenas, como:

static string Copy(string str): devuelve una copia de str.

string texto1 = "abc";
string texto2 = "xyz";
string texto2 = System.String.Copy(texto1); // 'texto2' ahora contiene "abc"

int CompareTo(string str): devuelve menor que cero si la cadena que llama es menor que str, mayor que cero si la cadena que llama es mayor que str, y cero si las cadenas son iguales.

string texto = "abc";
int i = texto.CompareTo("abc"); // Retornará '0'

int IndexOf(string str): devuelve el índice de la primera coincidencia de la subcadena especificada en str, o -1 en caso de error.

string texto = "abcdefabcdef";
int i = texto.IndexOf("e"); // Retornará '4'
int j = texto.IndexOf("def"); // Retornará '3', que es donde se encuentra el carácter 'd', seguido de 'e' y 'f'

int LastIndexOf(string str): devuelve el índice de la última coincidencia de la subcadena especificada en str, o -1 en caso de error.

string texto = "abcdefabcdef";
int i = texto.LastIndexOf("e"); // Retornará '10'
int j = texto.LastIndexOf("def"); // Retornará '9', que es donde se encuentra el último carácter 'd', seguido de 'e' y 'f'

string ToLower: devuelve una copia de la cadena en minúsculas.

string texto = "ABC";
string texto = texto.ToLower(); // Retornará "abc"

string ToUpper: devuelve una copia de la cadena en mayúsculas.

string texto = "abc";
string texto = texto.ToUpper(); // Retornará "ABC"

string Substring: devuelve una subcadena, indicando la posición de inicio y la longitud que se desea.

string texto = "Cadena de caracteres";
string texto = texto.Substring(10, 8); // Retornará "caracter"

Más información en: String (Clase) (System) (en inglés).

LINQ (Language-Integrated Query)

Language-Integrated Query (LINQ) es el nombre de un conjunto de tecnologías que consiste en la integración directa de funciones de consulta en el lenguaje C# (también en Visual Basic y potencialmente en cualquier otro lenguaje de .NET). Con LINQ, ahora una consulta es una construcción de lenguaje de primera clase, igual que las clases, los métodos, los eventos, etc.

Para un programador que escribe consultas, la parte integrada en el lenguaje más visible de LINQ es la expresión de consulta. Las expresiones de consulta se escriben en la sintaxis de consulta declarativa que se introdujo en C# 3.0. Al usar sintaxis de consulta, se pueden realizar incluso operaciones complejas de filtrado, ordenación y agrupamiento en orígenes de datos con código mínimo. Los mismos patrones de expresión de consulta básicos se usan para consultar y transformar datos de bases de datos SQL, conjuntos de datos de ADO.NET, secuencias y documentos XML, y colecciones de .NET.

En el siguiente ejemplo se muestra la operación de consulta completa. La operación completa incluye la creación de un origen de datos, la definición de la expresión de consulta y la ejecución de la consulta en una instrucción foreach.

class LINQQueryExpressions
{
    static void Main()
    {

        // Specify the data source. 
        int[] scores = new int[] { 97, 92, 81, 60 };

        // Define the query expression.
        IEnumerable<int> scoreQuery =
            from score in scores
            where score > 80
            select score;

        // Execute the query. 
        foreach (int i in scoreQuery)
        {
            Console.Write(i + " ");
        }            
    }
}
// Output: 97 92 81

https://msdn.microsoft.com/es-es/library/bb397676(v=vs.120).aspx

Ejemplos

Ejemplo básico "Hola mundo":

using System;

public class Ejemplo 
{
    public static void Main(string[] args) 
    {
        Console.WriteLine("Hola mundo");
    }
}

Suma y concatenación:

using System;

public class Ejemplo 
{
    public static void Main(string[] args) 
    {
        int x = 10;
        int y = 20;
        Console.WriteLine("El resultado es: " + (x + y)); // Imprimirá en pantalla: "El resultado es: 30"
    }
}

Uso de clases, métodos, propiedades y sobrecarga:

using System;

public class Coche 
{
    private int numPuertas;
    public int NumPuertas 
    {
        get 
        {
            return this.numPuertas;
        }
        set 
        {
            this.numPuertas = value; // 'value' es una variable que se asigna automáticamente al asignar un valor a la propiedad,
                                     // para poder trabajar con dicho valor.
        }
    }

    public Coche(int numPuertas) 
    {
        this.NumPuertas = numPuertas;
    }

    // Sobrecarga: si se instancia la clase sin indicar ningún parámetro, se inicializa 'numPuertas' con el valor '2'
    public Coche() : this(2) 
    {
    }
}

public class Ejemplo 
{
    public static void Main(string[] args) 
    {
        Coche coche = new Coche(); // Se usa el segundo constructor
        coche.NumPuertas = 4;
        Console.WriteLine("El número de puertas es: " + coche.NumPuertas);// Imprimirá en pantalla: "El número de puertas es: 4"
    }
}

Ejemplo de Vectores o Arreglos paralelos:

Problema 1:

Desarrollar un programa que permita cargar 5 nombres de personas y sus edades respectivas. Luego de realizar la carga por teclado de todos los datos imprimir los nombres de las personas mayores de edad (mayores o iguales a 18 años).

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PruebaVector10
{
    class PruebaVector10
    {
        private string[] nombres;
        private int[] edades;

        public void Cargar() 
        {
            nombres=new string[5];
            edades=new int[5];
            for(int f=0;f < nombres.Length;f++) 
            {
                Console.Write("Ingrese nombre:");
                nombres[f]=Console.ReadLine();
                Console.Write("Ingrese edad:");
                string linea;
                linea = Console.ReadLine();
                edades[f]=int.Parse(linea);            
            }
        }

        public void MayoresEdad() 
        {
            Console.WriteLine("Personas mayores de edad.");
	    for(int f=0;f < nombres.Length;f++) 
	    {
                if (edades[f] >= 18) 
                {
                    Console.WriteLine(nombres[f]);
                }
	    }
            Console.ReadKey();
        }

        static void Main(string[] args)
        {
            PruebaVector10 pv = new PruebaVector10();
            pv.Cargar();
            pv.MayoresEdad();
        }
    }
}

firma: RSRR

Controles comunes personalizados

Cabe destacar que los controles comunes que ofrece la plataforma .NET se pueden personalizar y/o editar para satisfacer las diferentes necesidades de los desarrolladores. El tomar la decisión de crear un control personalizado llega cuando se desea hacer una componente en donde se tiene el control total sobre su aspecto funcional y visual; con la posibilidad de no cargar las funcionalidades innecesarias para el desarrollo.

Los casos comunes en donde se suelen usar estas características son:

Compiladores e IDE's

En la actualidad existen los siguientes compiladores o IDE para el lenguaje C#:

Metas del diseño del lenguaje

El estándar ECMA-334 lista las siguientes metas en el diseño para C#:

Referencias

  1. «Why was C++ called that instead of C+?». Inglés. 10 de febrero de 2015. Consultado el 12 de junio de 2019. 
  2. Visual C# Developer Center, Frequently Asked Questions About Visual C# .NET 2003 (en inglés)

En castellano

En inglés