Articulo de referencia

Sintaxis de C#

Un fragmento de código C# Este artículo describe la sintaxis del lenguaje de programación C# . Las características descritas son compatibles con .NET Framework y Mono . Lo esenc...

Un fragmento de código C#

Este artículo describe la sintaxis del lenguaje de programación C# . Las características descritas son compatibles con .NET Framework y Mono .

Lo esencial

Identificador

Un identificador es el nombre de un elemento de código. Puede contener letras, dígitos y guiones bajos ( _), y distingue entre mayúsculas y minúsculas ( FOOes diferente de foo). El lenguaje impone las siguientes restricciones a los nombres de los identificadores:

  • No pueden empezar con un dígito;
  • No pueden comenzar con un símbolo, excepto el signo arroba ( @), que se puede usar como prefijo de palabras clave para permitir que funcionen como identificadores;
  • No pueden contener más de 511 caracteres .

Los nombres de los identificadores pueden ir precedidos de @, lo que permite el uso de palabras clave reservadas como identificadores (por ejemplo, @class), pero @no se considera parte del nombre del identificador para la mayoría de los propósitos; @namey namese refieren al mismo identificador.

Microsoft ofreció convenciones de nomenclatura para identificadores en C#, recomendando el uso de PascalCase para los nombres de tipos y la mayoría de los miembros de tipo, y camelCase para variables y campos privados o internos. [ 1 ] Estas convenciones de nomenclatura no son impuestas por el lenguaje.

Palabras clave

Las palabras clave son palabras con significado sintáctico. [ 2 ] El lenguaje tiene dos tipos de palabras clave : contextuales y reservadas. Las palabras clave reservadas, como falseo , bytesolo pueden usarse como palabras clave. Las palabras clave contextuales, como whereo , fromse tratan como palabras clave solo en ciertas situaciones. [ 3 ] Si se necesita un identificador que coincida con una palabra clave reservada, se le antepone un signo @ para distinguirlo. Por ejemplo, @outse interpreta como un identificador, mientras que outes una palabra clave. Esta sintaxis facilita la reutilización del código .NET escrito en otros lenguajes. [ 4 ]

Las siguientes palabras clave de C# son palabras reservadas, de las cuales hay 77. [ 2 ]

  • abstract
  • as
  • base
  • bool
  • break
  • byte
  • case
  • catch
  • char
  • checked
  • class
  • const
  • continue
  • decimal
  • default
  • delegate
  • do
  • double
  • else
  • enum
  • event
  • explicit
  • extern
  • false
  • finally
  • fixed
  • float
  • for
  • foreach
  • goto
  • if
  • implicit
  • in
  • int
  • interface
  • internal
  • is
  • lock
  • long
  • namespace
  • new
  • null
  • object
  • operator
  • out
  • override
  • params
  • private
  • protected
  • public
  • readonly
  • ref
  • return
  • sbyte
  • sealed
  • short
  • sizeof
  • stackalloc
  • static
  • string
  • struct
  • switch
  • this
  • throw
  • true
  • try
  • typeof
  • uint
  • ulong
  • unchecked
  • unsafe
  • ushort
  • using
  • virtual
  • void
  • volatile
  • while

Palabras clave contextuales

Una palabra clave contextual se utiliza para proporcionar un significado específico en el código, pero no es una palabra reservada en C#. Algunas palabras clave contextuales, como partialy where, tienen significados en múltiples contextos. Las siguientes 42 palabras clave de C# son contextuales. [ 5 ]

  • add
  • allows
  • alias
  • and
  • ascending
  • args
  • async
  • await
  • by
  • descending
  • dynamic
  • equals
  • from
  • get
  • global
  • group
  • init
  • into
  • join
  • let
  • managed
  • nameof
  • nint
  • not
  • notnull
  • nuint
  • on
  • or
  • orderby
  • partial
  • record
  • remove
  • required
  • select
  • set
  • unmanaged
  • value
  • var
  • when
  • where
  • with
  • yield

Directivas del preprocesador

Although C# does not have a separate preprocessor, unlike C and C++ which use the C preprocessor, these 13 directives are processed as if there were one.

  • #nullable
  • #if
  • #elif
  • #else
  • #endif
  • #define
  • #undef
  • #region
  • #endregion
  • #error
  • #warning
  • #line
  • #pragma

Literals

  1. Strings are not null-terminated in C#, so null characters may appear anywhere in a string.

Digit separators

Starting in C# 7.0, the underscore symbol can be used to separate digits in number values for readability purposes. The compiler ignores these underscores.[6]

intbin=0b1101_0010_1011_0100;inthex=0x2F_BB_4A_F1;intdec=1_000_500_954;doublereal=1_500.200_2e-1_000;

Generally, separators may be put only between digit characters. It cannot be put at the beginning (_121) or the end of the value (121_ or 121.05_), next to the decimal in floating point values (10_.0), next to the exponent character (1.1e_1), or next to the type specifier (10_f).

Variables

Variables are identifiers associated with values. They are declared by writing the variable's type and name, and are optionally initialized in the same statement.[10][11]

Declare

intmyInt;// Declaring an uninitialized variable called 'myInt', of type 'int'

Assigning

int myInt ; // Declarando una variable no inicializada myInt = 35 ; // Asignando un valor a la variable

Inicializar

int myInt = 35 ; // Declarando e inicializando la variable

Se pueden declarar e inicializar varias variables del mismo tipo en una sola instrucción.

int a , b ; // Declaración de múltiples variables del mismo tipoint a = 2 , b = 3 ; // Declarar e inicializar múltiples variables del mismo tipo

Inferencia de tipo de variable local

Esta es una característica de C# 3.0 .

C# 3.0 introdujo la inferencia de tipos , lo que permite reemplazar el especificador de tipo de una declaración de variable por la palabra clave `type` var, si su tipo real se puede determinar estáticamente a partir del inicializador. Esto reduce la repetición, especialmente para tipos con múltiples parámetros de tipo genéricos , y se ajusta más al principio DRY ( Don't Repeat Yourself).

var myChars = new char [] { 'A' , 'Ö' }; // equivalente a: char [] myChars = new char [] { 'A' , 'Ö' };var myNums = new List < int > (); // equivalente a: List < int > myNums = new List < int > ();

Constantes

Las constantes son valores inmutables.

const

Al declarar una variable local o un campo con la constpalabra clave como prefijo, el valor debe especificarse al declararlo. Posteriormente, el valor no puede cambiar. Pueden declararse en el contexto como un campo o una variable local. [ 10 ] Las constantes son implícitamente estáticas.

const double PI = 3.14 ;

Esto muestra ambos usos de la palabra clave.

clase pública Foo { const doble privado X = 3 ;public Foo () { const int y = 2 ; } }

readonly

La readonlypalabra clave hace algo similar a los campos. Al igual que los campos marcados como tales, constsu valor no puede cambiar una vez inicializado. La diferencia es que se puede optar por inicializarlos en un constructor o con un valor que no se conoce hasta el tiempo de ejecución. [ 12 ] Esto funciona solo con campos. readonlyLos campos pueden ser miembros de una instancia o miembros estáticos de una clase.

bloques de código

Las llaves se utilizan para delimitar un bloque de código y un nuevo ámbito . Los miembros de una clase y el cuerpo de un método son ejemplos de lo que puede estar dentro de estas llaves en diversos contextos.{...}

Dentro del cuerpo de los métodos, se pueden usar llaves para crear nuevos ámbitos:

void HacerAlgo () { int a ;{ int b ; a = 1 ; }a = 2 ; b = 3 ; // Fallará porque la variable está declarada en un ámbito interno. }

Estructura del programa

Una aplicación AC# consta de clases y sus miembros. Las clases y otros tipos existen en espacios de nombres y pueden anidarse dentro de otras clases.

Método principal

Todo programa debe tener un punto de entrada. El punto de entrada de una aplicación C# es el Mainmétodo. Solo puede haber una declaración de este método, y es un método estático dentro de una clase. Generalmente, devuelve un valor voidy recibe argumentos de línea de comandos como una matriz de cadenas.

static void Main ( string [] args ) // string[] args se puede omitir si el programa no tiene ningún argumento de línea de comandos. { }

El método principal puede devolver un valor entero si así se especifica.

static int Main ( string [] args ) { return 0 ; }

Principal asíncrono

Esta es una característica de C# 7.1.

Las tareas asíncronas se pueden esperar en el Mainmétodo declarando el tipo de retorno del método como Task.

static async Task Main ( string [] args ) { await DoWorkAsync ( 42 ); }

Se admiten todas las combinaciones de Task, o Task<int>,y con o sin el parámetro.string[] args

Declaraciones de alto nivel

Esta es una característica de C# 9.0.

Al igual que en los lenguajes de scripting , las declaraciones de nivel superior eliminan la necesidad de declarar la Programclase con un Mainmétodo.

En cambio, las instrucciones se pueden escribir directamente en un archivo, que es el punto de entrada del programa. El código en otros archivos debe definirse en clases.

Esto se introdujo para que C# fuera menos verboso y, por lo tanto, más accesible.

usando el sistema ;Consola.WriteLine ( " ¡ Hola Mundo!" ) ;

Los tipos se declaran después de las instrucciones y están disponibles a partir de las instrucciones que se encuentran encima de ellas.

Espacios de nombres

Los espacios de nombres forman parte del nombre de un tipo y se utilizan para agrupar y/o distinguir entidades con nombre de otras.

System.IO.DirectoryInfo // DirectoryInfo se encuentra en el espacio de nombres System.IO .

Un espacio de nombres se define con la palabra clave namespace:

espacio de nombres FooNamespace { // Miembros }

Un espacio de nombres puede anidarse dentro de otro espacio de nombres de tres maneras:

espacio de nombres FooNamespace { // Miembros espacio de nombres BarNamespace { // Miembros } }

y:

espacio de nombres FooNamspace { // Miembros }espacio de nombres FooNamspace.BarNamespace { // Miembros }

En C# 10 y versiones posteriores, los espacios de nombres se pueden definir utilizando declaraciones con ámbito de archivo de la siguiente manera: [ 13 ]

espacio de nombres FooNamespace ; // aquí se omiten los corchetes y se usa un punto y coma.

usingdirectiva

Esta usingdirectiva carga un espacio de nombres específico desde un ensamblado referenciado. Generalmente se coloca al principio (o en la cabecera) de un archivo de código, pero también puede ubicarse en otro lugar, por ejemplo, dentro de una clase.

using System ; using System.Collections ;

Esta directiva también puede utilizarse para definir otro nombre para un espacio de nombres o tipo existente. Esto resulta útil cuando los nombres son largos.

using Net = System . Net ; using DirInfo = System . IO . DirectoryInfo ;

usingstaticdirectiva

Esta directiva carga los miembros estáticos de un tipo específico en el ámbito actual, haciéndolos accesibles directamente por el nombre del miembro.usingstatic

usando el sistema estático.Console ;WriteLine ( "Hola, mundo!" );

globalusingdirectiva

Desde C# 10, es legal escribir directivas para indicar una directiva una sola vez y aplicarla a todos los archivos fuente.globalusingusing

global usando System ; global usando Microsoft.Extensions.FileSystemGlobbing ;

Operadores

Sobrecarga del operador

Algunos operadores pueden sobrecargarse escribiendo un método de sobrecarga.

public static Foo operator + ( Foo foo , Bar bar ) { return new Foo ( foo . Value + bar . Value ); }

Estos son los operadores sobrecargables :

  • Los operadores de asignación ( etc.) son combinaciones de un operador binario y el operador de asignación ( ) y se evalúan utilizando los operadores ordinarios, que pueden sobrecargarse.+=,*==
  • Los operadores de conversión ( ) no se pueden sobrecargar, pero es posible definir operadores de conversión.()
  • El operador de indexación de matrices ( [ ]) no es sobrecargable, pero es posible definir nuevos indexadores.

Operadores de conversión

El operador de conversión no es sobrecargable, pero un método de operador de conversión puede residir en la clase de destino. Los métodos de conversión pueden definir dos tipos de operadores: implícitos y explícitos. El operador implícito realiza la conversión sin necesidad de especificar el operador de conversión ( ), mientras que el operador explícito requiere su uso.()

Operador de conversión implícita

clase Foo { público int Valor ;public static implicit operator Foo ( int value ) { return new Foo ( value ); } } // Conversión implícita Foo foo = 2 ;

Operador de conversión explícita

clase Foo { público int Valor ;public static explicit operator Foo ( int value ) { return new Foo ( value ); } } // Conversión explícita Foo foo = ( Foo ) 2 ;

asoperador

El asoperador intentará realizar una conversión implícita a un tipo determinado. Si es posible, devolverá el objeto con el nuevo tipo; de lo contrario, devolverá null.

Stream stream = File . Open ( @"C:\Temp\data.dat" ); FileStream fstream = stream as FileStream ; // Devolverá un objeto.String str = stream as String ; // Devolverá null.

operador de coalescencia nula

Esta es una característica de C# 2.0 .

La siguiente:

devolver ifNotNullValue ?? otherwiseValue ;

es una abreviatura de:

devolver ifNotNullValue != null ? ifNotNullValue : otherwiseValue ;

Esto significa que si el contenido de la variable ifNotNullValueno es nulo, se devuelve ese contenido; de lo contrario, otherwiseValuese devuelve el contenido de la variable.

C# 8.0 introduce la asignación de coalescencia nula , de tal manera que

variable ??= valor de otro modo ;

es equivalente a

si ( la variable es nula ) variable = valor de otro modo ;

Estructuras de control

C# hereda la mayoría de las estructuras de control de C/C++ y agrega otras como la foreachinstrucción.

Estructuras condicionales

Estas estructuras controlan el flujo del programa a través de determinadas condiciones.

ifdeclaración

La ifinstrucción se ejecuta cuando se cumple la condición dada. Las instrucciones case de una sola línea no requieren llaves de bloque, aunque por convención suelen ser las preferidas.

Una simple declaración de una sola línea:

si ( i == 3 ) ... ;

Varias líneas con bloque else (sin llaves):

si ( i == 2 ) ... si no ...

Convenciones de codificación recomendadas para una sentencia if.

si ( i == 3 ) { ... } si no si ( i == 2 ) { ... } si no { ... }

switchdeclaración

La switchestructura sirve como filtro para diferentes valores. Cada valor conduce a un "caso". No se permite pasar de una sección de caso a otra, por lo que la palabra clave breakse usa normalmente para finalizar un caso. Una condición incondicional returndentro de una sección de caso también puede usarse para finalizar un caso. gotoLas sentencias pueden usarse para pasar de un caso al siguiente. El caso predeterminado maneja todos los casos que no son manejados explícitamente por la estructura.

switch ( ch ) { case 'A' : instrucción ; ... break ; case 'B' : instrucción ; break ; case 'C' : // Una sección switch puede tener múltiples etiquetas case. case 'D' : ... break ; default : ... break ; }

Estructuras de iteración

Las instrucciones de iteración son instrucciones que se ejecutan repetidamente cuando una condición determinada se evalúa como verdadera.

whilebucle

mientras ( i == verdadero ) { ... }

do ... whilebucle

hacer {} mientras ( i == verdadero );

forbucle

El forbucle consta de tres partes: declaración , condición y expresión de contador . Todas son opcionales.

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

Es equivalente a este código representado con una whileinstrucción, excepto que aquí la ivariable no es local al bucle.

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

foreachbucle

La foreachinstrucción se deriva de la forinstrucción y utiliza un patrón determinado descrito en la especificación del lenguaje C# para obtener y utilizar un enumerador de elementos sobre los que iterar.

Cada elemento de la colección dada se devuelve en el contexto del bloque de código. Cuando se ejecuta el bloque, se devuelve el siguiente elemento hasta que no queden más.

para cada ( int i en intList ) { ... }

Sentencias de salto

Las instrucciones de salto se heredan de C/C++ y, a través de este, de los lenguajes ensamblador. Representan las instrucciones de salto de un lenguaje ensamblador que controlan el flujo de un programa.

Etiquetas y gotodeclaración

Las etiquetas son puntos en el código a los que se puede saltar mediante la gotoinstrucción.

inicio : ....... ir a inicio ;

Tenga en cuenta que la etiqueta no tiene por qué estar colocada después de la gotoinstrucción; puede estar antes de ella en el archivo fuente.

Esta gotoinstrucción se puede utilizar en switchsentencias para pasar de un caso a otro o para continuar de un caso al siguiente.

switch ( n ) { case 1 : Console . WriteLine ( "Caso 1" ); break ; case 2 : Console . WriteLine ( "Caso 2" ); goto case 1 ; case 3 : Console . WriteLine ( "Caso 3" ); case 4 : // La compilación fallará aquí ya que los casos no pueden continuar en C#. Console . WriteLine ( "Caso 4" ); goto default ; // Esta es la forma correcta de continuar al siguiente caso. case 5 : // Se permiten múltiples etiquetas para el mismo código case 6 : default : Console . WriteLine ( "Predeterminado" ); break ; // Incluso el valor predeterminado no debe llegar al punto final }

breakdeclaración

La breakinstrucción sale del bucle o switchinstrucción más cercana. La ejecución continúa en la instrucción posterior a la instrucción que finalizó, si la hay.

int e = 10 ; for ( int i = 0 ; i < e ; i ++ ) { while ( true ) { break ; } // Se detendrá en este punto. }

continuedeclaración

Esta continueinstrucción interrumpe la iteración actual de la instrucción de control actual y comienza la siguiente iteración.

int ch ; while (( ch = Console.Read ( )) != -1 ) { if ( ch == '' ) { continue ; // Salta el resto del bucle while }// Resto del bucle while ... }

El whilebucle en el código anterior lee caracteres llamando a , omitiendo las instrucciones en el cuerpo del bucle si los caracteres son espacios.GetChar()

Manejo de excepciones

El método de manejo de excepciones en tiempo de ejecución en C# se hereda de Java y C++.

La biblioteca de clases base incluye una clase de la que se derivan todas las demás clases de excepción. Un objeto contiene toda la información sobre una excepción específica, así como las excepciones internas que la originaron. Los programadores pueden definir sus propias excepciones derivando de esta clase.System.ExceptionExceptionException

Se puede generar una excepción de esta manera:

lanzar nueva NotImplementedException ();

try...catch...finallydeclaraciones

Las excepciones se gestionan dentro de bloques.try...catch

try { // Instrucciones que pueden generar excepciones ... } catch ( Exception ex ) { // Excepción capturada y manejada aquí ... } finally { // Instrucciones que siempre se ejecutan después de los bloques try/catch ... }

Las instrucciones dentro del trybloque se ejecutan y, si alguna de ellas genera una excepción, la ejecución del bloque se interrumpe y la excepción se gestiona catch. Puede haber varios catchbloques; en ese caso, se ejecuta el primer bloque que contenga una variable de excepción cuyo tipo coincida con el tipo de la excepción generada.

Si ningún bloque coincide con el tipo de la excepción lanzada, se interrumpe catchla ejecución del bloque (o método) externo que contiene la instrucción, y la excepción se propaga hacia arriba, fuera del bloque o método contenedor. La excepción se propaga hacia arriba a través de la pila de llamadas hasta que se encuentra un bloque coincidente dentro de uno de los métodos activos. Si la excepción se propaga hasta el método superior sin encontrar un bloque coincidente , se termina el programa completo y se escribe una descripción textual de la excepción en la salida estándar.try ... catchcatchMain()catch

Las instrucciones dentro del finallybloque siempre se ejecutan después de los bloques tryy catch, independientemente de si se produjo una excepción o no.

Después del bloque debe ir un catchbloque, otro finallybloque o ambos .try

Tipos

C# es un lenguaje de tipado estático, al igual que C y C++. Esto significa que cada variable y constante adquiere un tipo fijo al ser declarada. Existen dos tipos: tipos de valor y tipos de referencia .

Tipos de valor

Las instancias de tipos de valor residen en la pila, es decir, están vinculadas a sus variables. Si se declara una variable para un tipo de valor, la memoria se asigna directamente. Si la variable queda fuera de ámbito, se destruye.

Estructuras

Las estructuras se conocen comúnmente como structs . Las estructuras son tipos de valor definidos por el usuario que se declaran mediante la structpalabra clave `struct`. Son similares a las clases, pero más adecuadas para tipos ligeros. Algunas diferencias sintácticas importantes entre una clase y una estructura se presentan más adelante en este artículo .

struct Foo { ... }

Los tipos de datos primitivos son todos estructuras.

Tipos predefinidos

Nota:string ( ) no es una estructura y no es un tipo primitivo.System.String

Enumeraciones

Los tipos enumerados (declarados con enum) son valores con nombre que representan valores enteros.

enum Season { Winter = 0 , Spring = 1 , Summer = 2 , Autumn = 3 , Fall = Autumn // Autumn se llama Fall en inglés americano. }

Las variables de enumeración se inicializan por defecto a cero. Se les pueden asignar o inicializar con los valores con nombre definidos por el tipo de enumeración.

Temporada temporada ; temporada = Temporada . Primavera ;

Las variables de tipo enumeración son valores enteros. Se permite la suma y la resta entre variables del mismo tipo sin necesidad de una conversión explícita, pero la multiplicación y la división son más arriesgadas y requieren dicha conversión. También se requieren conversiones para convertir variables enumeraciones a y desde tipos enteros. Sin embargo, la conversión no genera una excepción si el valor no está especificado en la definición del tipo.

temporada = ( Temporada ) 2 ; // convierte 2 a un valor enum de tipo Temporada. temporada = temporada + 1 ; // Agrega 1 al valor. temporada = temporada + temporada2 ; // Agrega los valores de dos variables enum. int valor = ( int ) temporada ; // Convierte el valor enum a un valor entero.temporada ++ ; // Temporada.Primavera (1) se convierte en Temporada.Verano (2). temporada -- ; // Temporada.Verano (2) se convierte en Temporada.Primavera (1).

Los valores se pueden combinar utilizando el operador OR bit a bit |.

Color myColors = Color . Verde | Color . Amarillo | Color . Azul ;

Tipos de referencia

Las variables creadas para tipos de referencia son referencias gestionadas tipadas. Al llamar al constructor, se crea un objeto en el montón y se asigna una referencia a la variable. Cuando una variable de un objeto sale del ámbito, la referencia se rompe y, cuando no quedan referencias, el objeto se marca como basura. El recolector de basura lo recoge y lo destruye.

Una variable de referencia es nula cuando no hace referencia a ningún objeto.

Matrices

Un tipo de array es un tipo de referencia que alude a un espacio que contiene uno o más elementos de un tipo determinado. Todos los tipos de array derivan de una clase base común . Cada elemento se referencia mediante su índice, como en C++ y Java.System.Array

Un array en C# es lo que en C++ se denomina un array dinámico .

int [] números = nuevo int [ 2 ]; números [ 0 ] = 2 ; números [ 1 ] = 5 ; int x = números [ 0 ];
Inicializadores

Los inicializadores de matrices proporcionan una sintaxis práctica para la inicialización de matrices.

// Sintaxis larga int [] numbers = new int [ 5 ]{ 20 , 1 , 42 , 15 , 34 }; // Sintaxis corta int [] numbers2 = { 20 , 1 , 42 , 15 , 34 }; // Sintaxis inferida var numbers3 = new [] { 20 , 1 , 42 , 15 , 34 };
Matrices multidimensionales

Las matrices pueden tener más de una dimensión, por ejemplo, 2 dimensiones para representar una cuadrícula.

int [,] números = nuevo int [ 3 , 3 ]; números [ 1 , 2 ] = 2 ;int [,] numbers2 = new int [ 3 , 3 ] { { 2 , 3 , 2 }, { 1 , 2 , 6 }, { 2 , 4 , 5 } };

Véase también

Clases

Las clases son tipos de referencia autodescriptivos definidos por el usuario. Básicamente, todos los tipos en .NET Framework son clases, incluyendo estructuras y enumeraciones, que son clases generadas por el compilador. Los miembros de una clase son privatepúblicos por defecto, pero se pueden declarar para publicque sean visibles fuera de la clase o protectedpara que sean visibles para cualquier descendiente de la misma.

Instrumentos de cuerda

La clase, o , representa una secuencia inmutable de caracteres Unicode ( ).System.Stringstringchar

Las acciones realizadas sobre una cadena siempre devuelven una nueva cadena.

string text = " ¡ Hola Mundo!" ; string substr = text.Substring ( 0 , 5 ) ; string [] parts = text.Split ( new char []{ ' ' } );

Esta clase se puede utilizar cuando se desea una "cadena" mutable.System.StringBuilder

StringBuilder sb = new StringBuilder (); sb . Append ( 'H' ); sb . Append ( "el" ); sb . AppendLine ( "lo!" );

Interfaz

Las interfaces son estructuras de datos que contienen definiciones de miembros sin implementaciones. Una variable de un tipo de interfaz es una referencia a una instancia de una clase que implementa esta interfaz. Véase #Interfaces .

Delegados

C# proporciona punteros a funciones orientadas a objetos con seguridad de tipos en forma de delegados .

clase Programa { // Tipo de delegado: delegate int Operación ( int a , int b );static int Add ( int i1 , int i2 ) { return i1 + i2 ; }static int Sub ( int i1 , int i2 ) { return i1 - i2 ; }static void Main () { // Instanciar el delegado y asignarle el método. Operación op = Agregar ;// Llama al método al que apunta el delegado. int result1 = op ( 2 , 3 ); // 5op = Sub ; int result2 = op ( 10 , 2 ); // 8 } }

Inicializando el delegado con un método anónimo.

adición = delegado ( int a , int b ) { return a + b ; };

Inicializando el delegado con una expresión lambda.

suma = ( a , b ) => a + b ;

Eventos

Los eventos son punteros que pueden apuntar a múltiples métodos. Más precisamente, vinculan punteros de métodos a un identificador. Por lo tanto, esto puede considerarse una extensión de los delegados . Se utilizan habitualmente como disparadores en el desarrollo de interfaces de usuario. El formato utilizado en C# y el resto de la Infraestructura de Lenguaje Común se basa en el Visual Basic clásico .

usando System.Windows.Controls ;delegate void MouseEventHandler ( object sender , MouseEventArgs e );clase pública Botón : Control { evento privado MouseEventHandler _onClick ;/* Función de activación imaginaria */ void Click () { _onClick ( this , new MouseEventArgs ( data )); } }

Un evento requiere un controlador de eventos asociado , el cual se crea a partir de un delegado especial que, en una biblioteca específica de la plataforma, como Windows Presentation Foundation y Windows Forms, suele aceptar dos parámetros: el remitente y los argumentos del evento . El tipo del objeto de argumentos del evento deriva de la clase EventArgs, que forma parte de la biblioteca base de la CLI.

Una vez declarado en su clase, la única forma de invocar el evento es desde dentro del propietario. Se puede implementar un método de escucha externo que se activará cuando se dispare el evento.

public class MainWindow : System . Windows . Controls . Window { private Button _button1 ;public MainWindow () { _button1 = new Button (); _button1 . Text = "¡Haz clic aquí!" ;/* Suscribirse al evento */ _button1 . ClickEvent += Button1_OnClick ;/* Sintaxis alternativa que se considera antigua:  _button1.MouseClick += new MouseEventHandler(Button1_OnClick); */ }protected void Button1_OnClick ( object sender , MouseEventArgs e ) { MessageBox.Show ( " ¡ Clic!" ); } }

También es posible la implementación de eventos personalizados:

private EventHandler _clickHandles = ( s , e ) => { };evento público EventHandler Click { agregar { // Código que se ejecutará cuando se agregue el controlador... ..._clickHandles += valor ; } remove { // Código que se ejecutará cuando se elimine el controlador... ..._clickHandles -= valor ; } }

Véase también

Tipos anulables

Esta es una característica de C# 2.0 .

Los tipos anulables se introdujeron en C# 2.0, en primer lugar, para permitir que los tipos de valor fueran nulos (algo útil al trabajar con una base de datos).

int? n = 2 ; n = null ;Console.WriteLine ( n.HasValue ) ;

Esto es lo mismo que usar la estructura.Nullable<T>

Nullable < int > n = 2 ; n = null ;Console.WriteLine ( n.HasValue ) ;

Consejos

C# tiene y permite punteros a tipos seleccionados (algunos tipos primitivos, enumeraciones, cadenas, punteros e incluso matrices y estructuras si contienen solo tipos a los que se puede apuntar [ 14 ] ) en contextos no seguros: métodos y bloques de código marcados unsafe. Estos son sintácticamente iguales a los punteros en C y C++. Sin embargo, la comprobación en tiempo de ejecución está deshabilitada dentro de unsafelos bloques.

static void Main ( string [] args ) { unsafe { int a = 2 ; int * b = &a a ;Console.WriteLine ( " Dirección de a: {0}. Valor: {1}" , ( int ) & a a , a ); Console . WriteLine ( "Dirección de b: {0}. Valor: {1}. Valor de *b: {2}" , ( int ) & b , ( int ) b , * b );// Generará una salida similar a esta: // Dirección de a: 71953600. Valor: 2 // Dirección de b: 71953596. Valor: 71953600. Valor de *b: 2 } }

Las estructuras solo deben ser estructuras puras, sin miembros de un tipo de referencia administrado, por ejemplo, una cadena o cualquier otra clase.

public struct MyStruct { public char Character ; public int Integer ; }public struct MyContainerStruct { public byte Byte ; public MyStruct MyStruct ; }

En uso:

MyContainerStruct x ; MyContainerStruct * ptr = & x ;valor de byte = puntero -> Byte ;

Dinámica

Esta es una característica de C# 4.0 y .NET Framework 4.0 .

El tipo dynamicpermite la búsqueda dinámica en tiempo de ejecución en C# de forma estática. Dinámico indica una variable con un objeto cuyo tipo se resuelve en tiempo de ejecución, en lugar de en tiempo de compilación.

Esta función aprovecha el Dynamic Language Runtime (DLR) y está diseñada con el objetivo de interoperar con lenguajes de tipado dinámico como IronPython e IronRuby (implementaciones de Python y Ruby para .NET).

La compatibilidad dinámica también facilita la interoperabilidad con objetos COM .

dynamic x = new Foo (); x . DoSomething (); // Se compilará y se resolverá en tiempo de ejecución. Se lanzará una excepción si no es válido.

Tipos anónimos

Esta es una característica de C# 3.0 .

Los tipos anónimos son clases sin nombre generadas por el compilador. Son útiles, aunque no imprescindibles, en situaciones como consultas LINQ que devuelven un objeto selectpara obtener valores específicos. Para ello, se define un tipo anónimo que contiene campos de solo lectura generados automáticamente para dichos valores.

Al instanciar otra declaración de tipo anónimo con la misma firma, el compilador infiere automáticamente el tipo.

var carl = new { Name = "Carl" , Age = 35 }; // El nombre del tipo solo lo conoce el compilador. var mary = new { Name = "Mary" , Age = 22 }; // Mismo tipo que la expresión anterior

Empaquetado y desempaquetado

El boxing es la operación de convertir un valor de un tipo de valor en un valor de un tipo de referencia correspondiente. [ 15 ] El boxing en C# es implícito.

El desempaquetado es la operación de convertir un valor de un tipo de referencia (previamente empaquetado) en un valor de un tipo de valor. [ 15 ] El desempaquetado en C# requiere una conversión de tipo explícita.

Ejemplo:

int foo = 42 ; // Tipo de valor. object bar = foo ; // foo se convierte en bar. int foo2 = ( int ) bar ; // Se vuelve a convertir en tipo de valor.

Programación orientada a objetos

C# ofrece soporte directo para la programación orientada a objetos .

objetos

Se crea un objeto con el tipo como plantilla y se denomina instancia de ese tipo en particular.

En C#, los objetos son referencias o valores. En el código no se hace distinción sintáctica entre ellos.

Clase de objeto

Todos los tipos, incluso los tipos de valor en su forma encapsulada, heredan implícitamente de la clase base. Esta clase contiene los métodos más comunes compartidos por todos los objetos. Algunos de estos métodos son y pueden ser sobrescritos.System.Objectvirtual

Las clases heredan de forma directa o indirecta a través de otra clase base.System.Object

Miembros Algunos miembros de la clase Object:

  • Equals- Admite comparaciones entre objetos.
  • Finalize- Realiza operaciones de limpieza antes de que un objeto sea recuperado automáticamente. (Destructor predeterminado)
  • GetHashCode- Obtiene el número correspondiente al valor del objeto para admitir el uso de una tabla hash .
  • GetType- Obtiene el tipo de la instancia actual.
  • ToString- Crea una cadena de texto legible para humanos que describe una instancia de la clase. Normalmente devuelve el nombre del tipo.

Clases

Las clases son fundamentales en un lenguaje orientado a objetos como C#. Sirven como plantilla para los objetos y contienen miembros que almacenan y manipulan datos de forma similar a como lo haríamos en la vida real.

Diferencias entre clases y estructuras

Aunque las clases y las estructuras son similares tanto en su declaración como en su uso, presentan diferencias significativas. Las clases son tipos de referencia y las estructuras son tipos de valor. Una estructura se asigna en la pila al declararse y la variable se vincula a su dirección. Contiene directamente el valor. Las clases son diferentes porque la memoria se asigna en el montón. Las variables son referencias, punteros administrados en la pila que apuntan a los objetos.

Las estructuras se diferencian de las clases en otros aspectos. Por ejemplo, si bien ambas ofrecen un constructor predeterminado implícito que no recibe argumentos, este no puede redefinirse para las estructuras. Definir explícitamente un constructor con parámetros diferentes anula el constructor predeterminado implícito en las clases, pero no en las estructuras. Todos los campos de una estructura deben inicializarse en dichos constructores. Las estructuras no tienen finalizadores y no pueden heredar de otra clase como lo hacen las clases. Implícitamente, son selladas y heredan de (que a su vez hereda de ). Las estructuras son más adecuadas para pequeñas cantidades de datos.System.ValueTypeSystem.Object

  1. Generado solo si no se proporcionó ningún otro constructor.
  2. Siempre se genera automáticamente y no puede ser escrito por el programador.

Declaración

Declaración de clase:

clase Foo { // Declaraciones de miembros }
Clase parcial
Esta es una característica de C# 2.0 .

Una clase parcial es una declaración de clase cuyo código está dividido en archivos separados. Las diferentes partes de una clase parcial deben estar marcadas con la palabra clave partial.

// File1.cs partial class Foo { ... }// File2.cs clase parcial Foo { ... }

La razón habitual para utilizar clases parciales es dividir una clase en partes mantenidas por el programador y partes mantenidas por la herramienta; es decir, parte del código es generado automáticamente por una herramienta de diseño de interfaz de usuario o similar.

Inicialización

Antes de poder usar los miembros de la clase, inicialice la variable con una referencia a un objeto. Para crearlo, llame al constructor correspondiente usando la newpalabra clave. Tiene el mismo nombre que la clase.

Foo foo = new Foo (); StreamReader sr = new StreamReader ( "MyFile.txt" );

Para las estructuras, es opcional llamar explícitamente a un constructor, ya que el predeterminado se llama automáticamente. Debe declararse y se inicializa con valores estándar.

Para reducir el código repetitivo, si el tipo se especifica en una declaración (es decir, sin usar var), el constructor se puede invocar simplemente con new().

Foo foo = nuevo (); StreamReader sr = nuevo ( "MyFile.txt" );
Inicializadores de objetos
Esta es una característica de C# 3.0 .

Proporciona una forma más práctica de inicializar los campos y propiedades públicas de un objeto. Las llamadas al constructor son opcionales si existe un constructor predeterminado.

Persona persona = nueva Persona { Nombre = "John Doe" , Edad = 39 };// Igual a Persona persona = nueva Persona (); persona . Nombre = "John Doe" ; persona . Edad = 39 ;
Inicializadores de colección
Esta es una característica de C# 3.0 .

Los inicializadores de colecciones proporcionan una sintaxis similar a la de los arrays para inicializar colecciones. El compilador genera llamadas al método Add. Esto funciona para las clases que implementan la interfaz ICollection.

Lista < int > lista = nueva Lista < int > { 2 , 5 , 6 , 6 };// Igual a List < int > list = new List < int > (); list . Add ( 2 ); list . Add ( 5 ); list . Add ( 6 ); list . Add ( 6 );

Acceso a los miembros

Se accede a los miembros de una instancia y a los miembros estáticos de una clase mediante el .operador.

Acceso a un miembro de instancia Se puede acceder a los miembros de instancia a través del nombre de una variable.

cadena foo = "Hola" ; cadena fooUpper = foo . ToUpper ();

Acceso a un miembro estático de una clase Los miembros estáticos se acceden utilizando el nombre de la clase u otro tipo.

int r = String.Compare ( foo , fooUpper ) ;

Acceso a un miembro a través de un puntero En código no seguro , se accede a los miembros de un valor (tipo struct) referenciado por un puntero con el ->operador como en C y C++.

PUNTO p ; p . X = 2 ; p . Y = 6 ; PUNTO * ptr = & p ; ptr -> Y = 4 ;

Modificadores

Los modificadores son palabras clave que se utilizan para modificar las declaraciones de tipos y miembros de tipo. Cabe destacar un subgrupo que contiene los modificadores de acceso.

modificadores de clase
  • abstract- Especifica que una clase sirve únicamente como clase base. Se implementa en una clase que hereda de ella. Es necesario para que las clases tengan métodos abstractos.
  • sealed- Especifica que una clase no puede ser heredada.
modificadores de miembros de clase
  • abstract- Declara que un método estará disponible en todas las clases derivadas no abstractas.
  • const- Especifica que una variable es un valor constante que debe inicializarse al ser declarada.
  • event- Declara un evento.
  • extern- Especifica que una firma de método sin cuerpo utiliza una importación de DLL .
  • override- Especifica que la declaración de un método o propiedad es una sobrescritura de un miembro virtual o una implementación de un miembro de una clase abstracta.
  • readonly- Declara un campo al que solo se le pueden asignar valores como parte de la declaración o en un constructor de la misma clase.
  • unsafe- Especifica un contexto no seguro que permite el uso de punteros.
  • virtual- Especifica que una clase derivada puede sobrescribir la declaración de un método o propiedad.
  • volatile- Especifica un campo que puede ser modificado por un proceso externo e impide que un compilador optimizador intente adivinar la persistencia del valor actual del campo.
staticmodificador

El staticmodificador indica que un miembro pertenece a la clase y no a un objeto específico. Las clases marcadas como estáticas solo pueden contener miembros estáticos. A los miembros estáticos a veces se les denomina miembros de clase, ya que se aplican a la clase en su conjunto y no a sus instancias.

public class Foo { public static void Something () { ... } } // Llamando al método de la clase. Foo . Something ();
Modificadores de acceso

Los modificadores de acceso , o modificadores de herencia , establecen la accesibilidad de las clases, los métodos y otros miembros. Los elementos marcados publicpueden ser alcanzados desde cualquier lugar. privateLos miembros solo pueden ser alcanzados desde la clase en la que se declaran y se ocultan cuando se heredan. Los miembros con el protectedmodificador son private, pero accesibles cuando se heredan. internalLas clases y los miembros solo son accesibles desde dentro del ensamblado que los declara.

Las clases y estructuras son implícitamente accesibles internal, y los miembros también lo son privatesi no tienen un modificador de acceso.

clase pública Foo { entero público Do () { return 0 ; }clase pública Bar {} }

Constructores

Un constructor es un método especial que se llama al crear un objeto. Su propósito es inicializar los miembros del objeto. Los constructores tienen el mismo nombre que la clase y no devuelven ningún valor. Implícitamente, devuelven el objeto recién creado cuando se llaman mediante el newoperador `new`. Pueden recibir parámetros, como cualquier otro método. El constructor sin parámetros es especial porque puede especificarse como una restricción necesaria para un parámetro de tipo genérico.

clase Foo { Foo () { ... } }

Los constructores pueden ser public, private, protectedo internal.

Incinerador de basuras

El destructor se llama cuando el recolector de basura recoge el objeto para realizar una limpieza manual. El método destructor predeterminado finalizepuede ser sobrescrito.

La sintaxis es similar a la de los constructores. La diferencia radica en que el nombre va precedido de un ~ y no puede incluir parámetros. Solo se puede declarar un destructor.

clase Foo { ...~ Foo () { ... } }

Los finalizadores siempre son private.

Métodos

Al igual que en C y C++, las funciones agrupan código reutilizable. La principal diferencia radica en que, como en Java, deben residir dentro de una clase. Por lo tanto, una función se denomina método . Un método tiene un valor de retorno, un nombre y, generalmente, parámetros que se inicializan al ser llamado con argumentos. Puede pertenecer a una instancia de una clase o ser estático.

clase Foo { int Bar ( int a , int b ) { return a % b ; } }

Se llama a un método. utilizando una notación sobre una variable específica o, como en el caso de los métodos estáticos, el nombre de un tipo.

Foo foo = nuevo Foo (); int r = foo . Bar ( 7 , 2 );Consola.WriteLine ( r ) ;
refy outparámetros

Los argumentos se pasan por referencia al llamar a un método con parámetros precedidos por las palabras clave ` refor` o `require` out. Estos punteros administrados son útiles al pasar variables que se modificarán dentro del método. La principal diferencia entre ambos es que un outparámetro debe haber sido asignado dentro del método antes de que este finalice. ` refor` puede o no asignar un nuevo valor, pero la variable del parámetro debe inicializarse antes de llamar a la función.

void PassRef ( ref int x ) { if ( x == 2 ) { x = 10 ; } } int Z = 7 ; PassRef ( ref Z );void PassOut ( out int x ) { x = 2 ; } int Q ; PassOut ( out Q );
Parámetros opcionales
Esta es una característica de C# 4.0 .

C# 4.0 introduce parámetros opcionales con valores predeterminados, como en C++. Por ejemplo:

void Incremento ( ref int x , int dx = 1 ) { x += dx ; }int x = 0 ; Incremento ( ref x ); // dx toma el valor predeterminado de 1 Incremento ( ref x , 2 ); // dx toma el valor 2

Además, para complementar los parámetros opcionales, es posible especificar explícitamente los nombres de los parámetros en las llamadas a métodos, para pasar selectivamente un subconjunto de parámetros opcionales a un método. La única restricción es que los parámetros con nombre deben colocarse después de los parámetros sin nombre. Se pueden especificar nombres de parámetros tanto para parámetros opcionales como obligatorios, y se pueden usar para mejorar la legibilidad o reordenar arbitrariamente los argumentos en una llamada. Por ejemplo:

Stream OpenFile ( string name , FileMode mode = FileMode . Open , FileAccess access = FileAccess . Read ) { ... }OpenFile ( "file.txt" ); // usar valores predeterminados para "mode" y "access" OpenFile ( " file.txt" , mode : FileMode.Create ) ; // usar valor predeterminado para "access" OpenFile ( "file.txt" , access : FileAccess.Read ) ; // usar valor predeterminado para "mode" OpenFile ( name : "file.txt" , access : FileAccess.Read , mode : FileMode.Create ) ; // nombrar todos los parámetros para mayor legibilidad, // y usar un orden diferente al de la declaración del método

Los parámetros opcionales facilitan la interoperabilidad con COM. Anteriormente, C# tenía que pasar todos los parámetros al método del componente COM, incluso aquellos que eran opcionales. Por ejemplo:

objeto fileName = "Test.docx" ; objeto missing = System . Reflection . Missing . Value ;doc.SaveAs ( ref fileName , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing , ref missing ) ; console.writeline ( " Archivo guardado correctamente" );

Con soporte para parámetros opcionales, el código se puede acortar como

doc.SaveAs ( ref fileName ) ;
extern

C# puede llamar a código nativo. La firma de un método se declara sin cuerpo y se marca como extern. El DllImportatributo hace referencia a la DLL deseada.

[DllImport("win32.dll")] static extern double Pow ( double a , double b );

Campos

Los campos, o variables de instancia , pueden declararse dentro del cuerpo de la clase para almacenar datos.

clase Foo { doble foo ; }

Los campos se pueden inicializar directamente al declararlos (a menos que se declaren en una estructura).

clase Foo { doble foo = 2.3 ; }

Modificadores para campos:

  • const- Convierte el campo en una constante.
  • private- Hace que el campo sea privado (por defecto).
  • protected- Hace que el campo esté protegido.
  • public- Hace público el campo.
  • readonly- Permite que el campo se inicialice solo una vez en un constructor.
  • static- Convierte el campo en un miembro estático, es decir, una variable de clase .

Propiedades

Las propiedades aportan una sintaxis similar a la de los campos y las combinan con métodos. Una propiedad puede tener dos accesores: gety set.

clase pública Persona { cadena privada _nombre ;cadena Nombre { obtener { devolver _nombre ; } establecer { _nombre = valor ; } } }// Usando una propiedad var person = new Person (); person . Name = "Robert" ;

Modificadores para propiedades:

  • private- Hace que la propiedad sea privada (por defecto).
  • protected- Protege la propiedad.
  • public- Hace pública la propiedad.
  • static- Convierte la propiedad en un miembro estático.

Modificadores para accesores de propiedades:

  • private- Hace que el accesor sea privado.
  • protected- Protege el dispositivo de acceso.
  • public- Hace público el accesor.

Los modificadores predeterminados para los accesores se heredan de la propiedad. Tenga en cuenta que los modificadores del accesor solo pueden ser iguales o más restrictivos que el modificador de la propiedad.

Propiedades automáticas
Esta es una característica de C# 3.0 .

Las propiedades de implementación automática utilizan métodos de acceso sin cuerpo, de modo que el compilador genera un campo de respaldo y el código necesario.

public double Width { obtener ; privado establecer ; }

Indexadores

Los indexadores añaden a los objetos capacidades de indexación similares a las de los arrays. Se implementan de forma similar a las propiedades.

clase interna IntList { privado int [] _items ;int this [ int index ] { obtener { devolver _items [ index ]; } establecer { _items [ index ] = valor ; } } }// Usando un indexador IntList lista = nueva IntList (); lista [ 2 ] = 2 ;

Herencia

Las clases en C# solo pueden heredar de una clase. Una clase puede derivar de cualquier clase que no esté marcada como sealed.

clase A { }clase B : A {}
virtual

Los métodos marcados virtualproporcionan una implementación que puede ser sobrescrita por los herederos mediante el uso de la overridepalabra clave.

La implementación se elige en función del tipo de objeto y no del tipo de variable.

clase Operación { público virtual int Hacer () { return 0 ; } }clase NuevaOperación : Operación { public override int Hacer () { return 1 ; } }
new

Al sobrecargar un método no virtual con otra firma, newse puede usar la palabra clave. El método utilizado se elige según el tipo de variable en lugar del tipo de objeto.

clase Operación { public int Hacer () { return 0 ; } }clase NuevaOperación : Operación { public new double Do () { return 4.0 ; } }

Esto demuestra el caso:

NuevaOperación op = nueva NuevaOperación ();// Llamará a "double Do()" en NewOperation double d = op . Do ();Operación op_ = op ;// Llamará a "int Do()" en Operation int i = op_ . Do ();
abstract

Las clases abstractas son clases que sirven como plantillas y no se pueden inicializar. Por lo demás, son como una clase ordinaria.

Los miembros abstractos son miembros de clases abstractas que no tienen implementación. Deben ser sobrescritos por las clases no abstractas que heredan del miembro.

clase abstracta Mamífero { público abstracto void Caminar (); }clase Humano : Mamífero { public override void Walk () {}... }
sealed

Este sealedmodificador se puede combinar con otros para que no sean heredables, o para que los métodos no permitan su sobrescritura en las clases derivadas.

clase interna sellada Foo { //... }public class Bar { public virtual void Action () { //... } }public class Baz : Bar { public sealed override void Action () { //... } }

Interfaces

Las interfaces son estructuras de datos que contienen definiciones de miembros, pero no implementaciones. Se utilizan para definir un contrato entre miembros de diferentes tipos con implementaciones distintas. Se pueden proporcionar definiciones para métodos, propiedades e indexadores. Los miembros de una interfaz son implícitamente públicos. Una interfaz puede implementarse de forma implícita o explícita.

interfaz IBinaryOperation { double A { obtener ; establecer ; } double B { obtener ; establecer ; }doble ObtenerResultado (); }

Implementación de una interfaz

Una interfaz es implementada por una clase o extendida por otra interfaz de la misma manera que una clase se deriva de otra clase utilizando la :notación.

Implementación implícita

Cuando se implementa implícitamente una interfaz, los miembros de la interfaz deben ser public.

public class Adder : IBinaryOperation { public double A { get ; set ; } public double B { get ; set ; }public double GetResult () { return A + B ; } }public class Multiplier : IBinaryOperation { public double A { get ; set ; } public double B { get ; set ; }public double GetResult () { return A * B ; } }

En uso:

IBinaryOperation op = null ; double result ;// El sumador implementa la interfaz IBinaryOperation.op = nuevo sumador (); op . Un = 2 ; op . B = 3 ;resultado = op . ObtenerResultado (); // 5// Multiplier también implementa la interfaz.op = nuevo multiplicador (); op . Una = 5 ; op . B = 4 ;resultado = op . ObtenerResultado (); // 20

Implementación explícita

Los miembros implementados explícitamente solo son accesibles cuando el objeto se trata como un tipo de interfaz.

public class Adder : IBinaryOperation { double IBinaryOperation . A { get ; set ; } double IBinaryOperation . B { get ; set ; }double IBinaryOperation.GetResult ( ) { return (( IBinaryOperation ) this ) .A + ( ( IBinaryOperation ) this ) .B ; } }

En uso:

Adder add = nuevo Adder ();// Estos miembros no son accesibles: // add.A = 2; // add.B = 3; // double result = add.GetResult();// Convierta al tipo de interfaz para acceder a ellos: IBinaryOperation add2 = add ; add2 . A = 2 ; add2 . B = 3 ;double resultado = add2.GetResult ( ) ;

Las propiedades de la clase que extiende IBinaryOperationson implementadas automáticamente por el compilador y se agrega automáticamente un campo de respaldo (ver #Propiedades automáticas ).

Ampliación de múltiples interfaces

Se permite que las interfaces y las clases extiendan múltiples interfaces.

clase MyClass : IInterfaceA , IInterfaceB { ... }

Una interfaz que extiende dos interfaces.

interfaz IInterfaceC : IInterfaceA , IInterfaceB { ... }

Interfaces frente a clases abstractas

Las interfaces y las clases abstractas son similares. A continuación se describen algunas diferencias importantes:

  • Una clase abstracta puede tener variables miembro, así como métodos o propiedades no abstractos. Una interfaz no puede.
  • Una clase o clase abstracta solo puede heredar de una única clase o clase abstracta.
  • Una clase o clase abstracta puede implementar una o más interfaces.
  • Una interfaz solo puede extender otras interfaces.
  • Una clase abstracta puede tener métodos y propiedades no públicos (también abstractos). Una interfaz solo puede tener miembros públicos.
  • Una clase abstracta puede tener constantes, métodos estáticos y miembros estáticos. Una interfaz no puede.
  • Una clase abstracta puede tener constructores. Una interfaz no.

Medicamentos genéricos

Esta es una característica de C# 2.0 y .NET Framework 2.0 .

Los genéricos (o tipos parametrizados, polimorfismo paramétrico ) utilizan parámetros de tipo, lo que permite diseñar clases y métodos que no especifican el tipo utilizado hasta que la clase o el método se instancia. La principal ventaja es que se pueden usar parámetros de tipo genéricos para crear clases y métodos que se pueden usar sin incurrir en el costo de las conversiones en tiempo de ejecución o las operaciones de empaquetado, como: [ 16 ]

// Declarar la clase genérica.public class GenericList < T > { void Add ( T input ) { } }clase TestGenericList { clase privada ExampleClass { } static void Main () { // Declarar una lista de tipo int. GenericList < int > list1 = new GenericList < int > ();// Declara una lista de tipo cadena. GenericList < string > list2 = new GenericList < string > ();// Declara una lista de tipo ExampleClass. GenericList < ExampleClass > list3 = new GenericList < ExampleClass > (); } }

When compared with C++ templates, C# generics can provide enhanced safety, but also have somewhat limited capabilities.[17] For example, it is not possible to call arithmetic operators on a C# generic type.[18] Unlike C++ templates, .NET parameterized types are instantiated at runtime rather than by the compiler; hence they can be cross-language whereas C++ templates cannot. They support some features not supported directly by C++ templates such as type constraints on generic parameters by use of interfaces. On the other hand, C# does not support non-type generic parameters.

Unlike generics in Java, .NET generics use reification to make parameterized types first-class objects in the Common Language Infrastructure (CLI) Virtual Machine, which allows for optimizations and preservation of the type information.[19]

Using generics

Generic classes

Classes and structs can be generic.

publicclassList<T>{...publicvoidAdd(Titem){...}}varlist=newList<int>();list.Add(6);list.Add(2);

Generic interfaces

interfaceIEnumerable<T>{...}

Generic delegates

delegateRFunc<T1,T2,R>(T1a1,T2a2);

Generic methods

publicstaticT[]CombineArrays<T>(T[]a,T[]b){T[]newArray=newT[a.Length+b.Length];a.CopyTo(newArray,0);b.CopyTo(newArray,a.Length);returnnewArray;}string[]a=newstring[]{"a","b","c"};string[]b=newstring[]{"1","2","3"};string[]c=CombineArrays(a,b);double[]da=newdouble[]{1.2,2.17,3.141592};double[]db=newdouble[]{4.44,5.6,6.02};double[]dc=CombineArrays(da,db);// c is a string array containing { "a", "b", "c", "1", "2", "3"}// dc is a double array containing { 1.2, 2.17, 3.141592, 4.44, 5.6, 6.02}

Type-parameters

Type-parameters are names used in place of concrete types when defining a new generic. They may be associated with classes or methods by placing the type parameter in angle brackets <>. When instantiating (or calling) a generic, one can then substitute a concrete type for the type-parameter one gave in its declaration. Type parameters may be constrained by use of the where keyword and a constraint specification, any of the six comma separated constraints may be used:[20]

Covariance and contravariance

This is a feature of C# 4.0 and .NET Framework 4.0.

Generic interfaces and delegates can have their type parameters marked as covariant or contravariant, using keywords out and in, respectively. These declarations are then respected for type conversions, both implicit and explicit, and both compile-time and run-time. For example, the existing interface IEnumerable<T> has been redefined as follows:

interfaceIEnumerable<outT>{IEnumerator<T>GetEnumerator();}

Therefore, any class that implements IEnumerable<Derived> for some class Derived is also considered to be compatible with IEnumerable<Base> for all classes and interfaces Base that Derived extends, directly, or indirectly. In practice, it makes it possible to write code such as:

voidPrintAll(IEnumerable<object>objects){foreach(objectoinobjects){System.Console.WriteLine(o);}}IEnumerable<string>strings=newList<string>();PrintAll(strings);// IEnumerable<string> is implicitly converted to IEnumerable<object>

For contravariance, the existing interface IComparer<T> has been redefined as:

publicinterfaceIComparer<inT>{intCompare(Tx,Ty);}

Therefore, any class that implements IComparer<Base> for some class Base is also considered to be compatible with IComparer<Derived> for all classes and interfaces Derived that are extended from Base. It makes it possible to write code such as:

IComparer<object>objectComparer=GetComparer();IComparer<string>stringComparer=objectComparer;

Enumerators

An enumerator is an iterator. Enumerators are typically obtained by calling the GetEnumerator() method of an object implementing the IEnumerable interface. Container classes typically implement this interface. However, the foreach statement in C# can operate on any object providing such a method, even if it doesn't implement IEnumerable. This interface was expanded into generic version in .NET 2.0.

The following shows a simple use of iterators in C# 2.0:

// explicit versionIEnumerator<MyType>iter=list.GetEnumerator();while(iter.MoveNext()){Console.WriteLine(iter.Current);}// implicit versionforeach(MyTypevalueinlist){Console.WriteLine(value);}

Generator functionality

This is a feature of C# 2.0.

The .NET 2.0 Framework allowed C# to introduce an iterator that provides generator functionality, using a yieldreturn construct similar to yield in Python.[21] With a yieldreturn, the function automatically keeps its state during the iteration.

// Method that takes an iterable input (possibly an array)// and returns all even numbers.publicstaticIEnumerable<int>GetEven(IEnumerable<int>numbers){foreach(intiinnumbers){if(i%2==0){yieldreturni;}}}// using the method to output only even numbers from the arraystaticvoidMain(){int[]numbers={1,2,3,4,5,6};foreach(intiinGetEven(numbers)){Console.WriteLine(i);// outputs 2, 4 and 6}}

LINQ

This is a feature of C# 3.0 and .NET Framework 3.0.

LINQ, short for Language Integrated Queries, is a .NET Framework feature which simplifies the handling of data. Mainly it supports querying of arrays, collections, and databases. It introduces binders, which makes it easier to access to databases and their data.

Query syntax

LINQ query syntax was introduced in C# 3.0 and supports SQL-like queries.

List<int>list=newList<int>{2,7,1,3,9};IEnumerable<int>result=fromiinlistwherei>1selecti;// Equivalent to:IEnumerable<int>result=list.Where(i=>i>1).Select(i=>i)

The statements are compiled into method calls, whereby almost only the names of the methods are specified. Which methods are ultimately used is determined by normal overload resolution. Thus, the result of the translation is affected by the symbols in scope.

What differs from SQL is that the from-statement comes first and not last as in SQL. This seems more natural writing in C# and supports "Intellisense" (Code completion in the editor).

Anonymous methods

Anonymous methods, (lambda expressions), is a feature that allows programmers to write inline closure-like functions.

C# supports various ways to create anonymous methods.

Anonymous delegates

This is a feature of C# 2.0.

Anonymous delegates are function pointers that hold anonymous methods. The purpose is to make it simpler to use delegates by simplifying function assignment. Instead of declaring a separate method in code the programmer can use the syntax to write the code inline and the compiler generates an anonymous function for it.

Func<int,int>f=delegate(intx){returnx*2;};

Lambda expressions

This is a feature of C# 3.0.

Lambda expressions provide a simple syntax for inline functions that are similar to closures. Functions with parameters infer the type of the parameters if other is not explicitly specified.

// [arguments] => [method-body]// With parametersn=>n==2(a,b)=>a+b(a,b)=>{a++;returna+b;}// With explicitly typed parameters(inta,intb)=>a+b// No parameters()=>return0// Assigning lambda to delegateFunc<int,int,int>f=(a,b)=>a+b;

Multi-statement lambdas have bodies enclosed by braces and inside of them code can be written as in standard methods.

(a,b)=>{a++;returna+b;}

Lambda expressions can be passed as arguments directly in method calls similar to anonymous delegates but with a cleaner syntax.

varlist=stringList.Where(n=>n.Length>2);

Lambda expressions are compiler-generated methods passed via delegates. These methods are reserved for the compiler and cannot be used in any other context.

Extension methods

This is a feature of C# 3.0.

Extension methods are syntactic sugar providing the illusion of adding new methods to an existing class outside its definition. In practice, an extension method is a static method that is callable as if it were an instance method; the receiver of the call is bound to the first parameter of the method, decorated with keyword this:

publicstaticclassStringExtensions{publicstaticstringLeft(thisstrings,intn){returns.Substring(0,n);}}strings="foo";s.Left(3);// same as StringExtensions.Left(s, 3);

Local functions

This is a feature of C# 7.0.

Local functions can be defined in the body of another method, constructor or property's getter and setter. Such functions have access to all variables in the enclosing scope, including parent method local variables. They are in scope for the entire method, regardless of whether they are invoked before or after their declaration. Access modifiers (public, private, protected) cannot be used with local functions. They do not support function overloading, which means two local functions in the same method cannot have the same name even if the signatures don't overlap.[22] After a compilation, a local function is transformed into a private static method, but when defined it cannot be marked static.[23]

In the example below, the Sum method is a local function inside Main method. So it can be used only inside its parent method Main:

staticvoidMain(string[]args){intSum(intx,inty){returnx+y;}Console.WriteLine(Sum(10,20));Console.ReadKey();}

Miscellaneous

Closure blocks

C# implements closure blocks by means of the using statement. The using statement accepts an expression which results in an object implementing IDisposable, and the compiler generates code that guarantees the object's disposal when exiting the scope of the using-statement. The using statement is syntactic sugar. It makes the code more readable than the equivalent try...finally block.

publicvoidFoo(){using(FileStreambar=File.Open("Foo.txt")){// do some workthrownewException();// bar will still get properly disposed.}}

Thread synchronization

C# provides the lock statement, which is another example of beneficial syntactic sugar. It works by marking a block of code as a critical section by mutual exclusion of access to a provided object. Like the using statement, the compiler generates a try...finally block in its place.

privatestaticStreamWriter_writer;publicvoidConcurrentMethod(){lock(_writer){_writer.WriteLine("Line 1.");_writer.WriteLine("Followed by line 2.");}}

Attributes

Attributes are entities of data that are stored as metadata in the compiled assembly, similar to Java annotations. An attribute can be added to types and members like properties and methods. Attributes can be used for better maintenance of preprocessor directives.

[CompilerGenerated]publicclass$AnonymousType$120{[CompilerGenerated]publicstringName{get;set;}}

The .NET Framework comes with predefined attributes that can be used. Some of them serve an important role at runtime, while others are just for syntactic decoration in code like CompilerGenerated. It only marks that it is a compiler-generated element. Programmer-defined attributes can also be created.

An attribute is essentially a class that inherits from the System.Attribute class. By convention, attribute classes end with "Attribute" in their name. This is not required when using it.

publicclassEdibleAttribute:Attribute{publicEdibleAttribute():base(){}publicEdibleAttribute(boolisNotPoisonous){this.IsPoisonous=!isNotPoisonous;}publicboolIsPoisonous{get;set;}}

Showing the attribute in use using the optional constructor parameters.

[Edible(true)]publicclassPeach:Fruit{// Members if any}

Preprocessor

C# features "preprocessor directives"[24] (though it does not have an actual preprocessor) based on the C preprocessor that allows programmers to define symbols, but not macros. C# does not use the C preprocessor, but evaluates the code directly as if the preprocessor were there. Conditionals such as #if, #endif, and #else are provided, but code inclusion directives like #include and #embed are not supported.

Directives such as #region give hints to editors for code folding. The #region block must be terminated with a #endregion directive.

publicclassFoo{#region ConstructorspublicFoo(){}publicFoo(intfirstParam){}#endregion#region MethodspublicvoidIntBar(intfirstParam){}publicvoidStrBar(stringfirstParam){}publicvoidBoolBar(boolfirstParam){}#endregion}

Code comments

C# utilizes a double slash (//) to indicate the rest of the line is a comment.

publicclassFoo{// a commentpublicstaticvoidBar(intfirstParam){}// Also a comment}

Multi-line comments can be indicated by a starting slash/asterisk (/*) and ending asterisk/forward slash (*/).

publicclassFoo{/* A multi-line comment */publicstaticvoidBar(intfirstParam){}}

Comments do not nest. These are two single comments:

// Can put /* */ */ */ /* /*
/* Can put /* /* /* but it ends with */

Single-line comments beginning with three slashes are used for XML documentation. This, however, is a Visual Studio convention and is not part of the language definition:

/// <summary>/// This class is very classy./// </summary>

XML documentation comments

C#'s documentation comments[25] are similar to Java's Javadoc, but based on XML. Two methods of documentation are supported by the compiler.

Single-line documentation comments, such as those commonly found in Visual Studio generated code, are indicated on a line beginning with ///.

publicclassFoo{/// <summary>A summary of the method.</summary>/// <param name="firstParam">A description of the parameter.</param>/// <remarks>Remarks about the method.</remarks>publicstaticvoidBar(intfirstParam){}}

Multi-line documentation comments, while defined in the version 1.0 language specification, were not supported until the .NET 1.1 release.[26] These comments are designated by a starting forward slash/asterisk/asterisk (/**) and ending asterisk/forward slash (*/).[27]

publicclassFoo{/** <summary>A summary of the method.</summary> * <param name="firstParam">A description of the parameter.</param> * <remarks>Remarks about the method.</remarks> */publicstaticvoidBar(intfirstParam){}}

Stringent criteria governs white space and XML documentation when using the forward slash/asterisk/asterisk (/**) technique.

This code block:

/** * <summary> * A summary of the method.</summary>*/

produces a different XML comment than this code block:[27]

/** * <summary> A summary of the method.</summary>*/

Syntax for documentation comments and their XML markup is defined in a non-normative annex of the ECMA C# standard. The same standard also defines rules for processing of such comments, and their transformation to a plain XML document with precise rules for mapping of Common Language Infrastructure (CLI) identifiers to their related documentation elements. This allows any C# integrated development environment (IDE) or other development tool to find documentation for any symbol in the code in a certain well-defined way.

Async-await syntax

This is a feature of C# 5.0 and .NET Framework 4.0.

As of .NET Framework 4 a task library makes it easier to write parallel and multi-threaded applications through tasks.

C# 5.0 has native language support for asynchrony, e.g:

publicstaticclassSomeAsyncCode{publicstaticTask<XDocument>GetContentAsync(){HttpClienthc=newHttpClient();returnhc.GetStringAsync("https://www.contoso.com/").ContinueWith((task)=>{stringresponseBodyAsText=task.Result;returnXDocument.Parse(responseBodyAsText);});}}vart=SomeAsyncCode.GetContentAsync().ContinueWith((task)=>{XDocumentxmlDocument=task.Result;});t.Start();

The same logic written in the async-await syntax:

publicstaticclassSomeAsyncCode{publicstaticasyncTask<XDocument>GetContentAsync(){HttpClienthc=newHttpClient();stringresponseBodyAsText=awaithc.GetStringAsync("https://www.contoso.com/");returnXDocument.Parse(responseBodyAsText);}}XDocumentxmlDocument=awaitSomeAsyncCode.GetContentAsync();// The Task will be started on call with await.

Dialects

Spec#

Spec# is a dialect of C# developed in parallel with the official implementation. It extends C# with specification language features and presents a possible future feature to the C# language. It adds syntax for the code contracts API that was introduced in .NET Framework 4.0. Spec# is developed by Microsoft Research.

This sample shows two of the basic structures that are used when adding contracts to code.

staticvoidMain(string![]args)requiresargs.Length>0{foreach(stringarginargs){}}
  • ! is used to make a reference type non-nullable, e.g. it is not possible to set the value to null. This in contrast of nullable types which allow value types to be set as null.
  • requires indicates a condition that must be followed in the code. In this case the length of args is not allowed to be zero or less.

Non-nullable types

Spec# extends C# with non-nullable types that checks so the variables of nullable types that has been set as non-nullable are not null. If is null then an exception is thrown.

string!input

In use:

publicTest(string!input){...}

Preconditions

Preconditions are checked before a method is executed.

publicTest(inti)requiresi>0;{this.i=i;}

Postconditions

Postconditions are conditions that are ensured to be correct when a method has been executed.

publicvoidIncrement()ensuresi>0;{i++;}

Checked exceptions

Spec# adds checked exceptions like those in Java.

// SomeException extends ICheckedExceptionpublicvoidDoSomething()throwsSomeException{...}

Checked exceptions may be inconvenient, because when a lower-level function adds a new exception type, the whole chain of methods using this method at some nested lower level must also change its contract. This violates the open/closed principle.[28] However, checked exceptions are designed to be immediately handled rather than continuously thrown up the stack.

See also

References

  1. "C# Coding Conventions". Microsoft Learn. sec. Naming conventions. Archived from the original on January 16, 2023.
  2. 12Wagner, Bill. "C# Keywords". docs.microsoft.com. Retrieved August 26, 2022.
  3. Schildt, Herbert (December 30, 2008), C# 3.0: The Complete Reference, ISBN 9780071588416
  4. Deitel, Harvey M.; Deitel, Paul J. (November 21, 2005), C# for programmers, ISBN 9780132465915
  5. Wagner, Bill. "C# Keywords". docs.microsoft.com. Retrieved August 26, 2022.
  6. 12Wagner, Bill. "Integral numeric types". learn.microsoft.com. Retrieved October 27, 2024.
  7. Wagner, Bill. "Floating-point numeric types". learn.microsoft.com. Retrieved October 27, 2024.
  8. Wagner, Bill. "char". learn.microsoft.com. Retrieved October 27, 2024.
  9. 12Wagner, Bill. "Strings and string literals". learn.microsoft.com. Retrieved October 27, 2024.
  10. 12Wagner, Bill. "Declaration statements". learn.microsoft.com. Retrieved October 27, 2024.
  11. Wagner, Bill. "Assignment operators". learn.microsoft.com. Retrieved October 27, 2024.
  12. Wagner, Bill. "readonly". learn.microsoft.com. Retrieved October 27, 2024.
  13. "Exploring C# 10: Save Space with File-Scoped Namespaces". Dave Brock. October 5, 2021. Retrieved November 19, 2024.
  14. Pointer types (C# Programming Guide)
  15. 12Archer, Part 2, Chapter 4:The Type System
  16. "Generics (C# Programming Guide)". Microsoft. Retrieved August 7, 2011.
  17. "An Introduction to C# Generics". Microsoft.
  18. "Differences Between C++ Templates and C# Generics". Microsoft.
  19. "An Introduction to C# Generics". Microsoft. January 2005. Retrieved June 18, 2009.
  20. Restricciones en los parámetros de tipo (Guía de programación de C#) en Microsoft MSDN
  21. "yield" . Referencia del lenguaje C# . Microsoft . Consultado el 26 de abril de 2009 .
  22. ".NET Framework - Novedades en C# 7.0" . msdn.microsoft.com . Consultado el 8 de abril de 2017 .
  23. "Reflexiones sobre las funciones locales de C# 7" . Anton Sizikov . 15 de abril de 2016. Consultado el 8 de abril de 2017 .
  24. "Directivas del preprocesador de C#" . Referencia del lenguaje C# . Microsoft . Consultado el 18 de junio de 2009 .
  25. "Comentarios sobre la documentación" . Microsoft Learn . 15 de junio de 2023.
  26. Horton, Anson (11 de septiembre de 2006). "Preguntas frecuentes sobre comentarios de la documentación XML de C#" . Consultado el 11 de diciembre de 2007 .
  27. 1 2 "Delimitadores para etiquetas de documentación" . Referencia del programador de C# . Microsoft . Archivado del original el 20 de diciembre de 2008. Recuperado el 18 de junio de 2009 .
  28. Martin, Robert C. (11 de agosto de 2008). "7 Manejo de errores, uso de excepciones no verificadas". Clean Code: A Handbook of Agile Software Craftsmanship . Prentice Hall International. ISBN 978-0132350884.
  1. Archer, Tom (2001). Inside C# . Microsoft Press. ISBN 0-7356-1288-9.
  2. Bart de Smet en Spec# Archivado el 29 de octubre de 2010 en Wayback Machine
  • Marco de trabajo .NET de Microsoft
  • Proyecto Mono