stringtranslate.com

Sintaxis de C Sharp

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 en el 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 identificadores:

Los nombres de los identificadores pueden tener el prefijo de una arroba ( @), pero esto es insignificante; @namees el mismo identificador que name.

Microsoft ha publicado convenciones de nomenclatura para identificadores en C#, que recomienda el uso de PascalCase para los nombres de tipos y la mayoría de los miembros de tipos, y camelCase para variables y campos privados o internos. [1] Sin embargo, estas convenciones de nomenclatura no se aplican en el idioma.

Palabras clave

Las palabras clave son palabras reservadas predefinidas con un significado sintáctico especial. [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 fromsolo se tratan como palabras clave en determinadas situaciones. [3] Si se necesita un identificador que sea el mismo que una palabra clave reservada, puede tener el prefijo de una arroba para distinguirlo. Por ejemplo, @outse interpreta como un identificador, mientras que outcomo 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: [2]

Se utiliza una palabra clave contextual 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 especiales en múltiples contextos. Las siguientes palabras clave de C# son contextuales: [5]

Literales

  1. ^ Las cadenas no terminan en nulo en C#, por lo que pueden aparecer caracteres nulos en cualquier parte de una cadena.

Separadores de dígitos

A partir de C# 7.0, el símbolo de guión bajo se puede utilizar para separar dígitos en valores numéricos con fines de legibilidad. El compilador ignora estos guiones bajos.

contenedor int = 0 b1101_0010_1011_0100 ; int hexadecimal = 0 x2F_BB_4A_F1 ; int dec = 1 _000_500_954 ; doble real = 1 _500 . 200 _2e - 1 _000 ;            

Generalmente, sólo se puede poner entre caracteres numéricos. No se puede colocar al principio ( _121) o al final del valor ( 121_o 121.05_), al lado del decimal en valores de punto flotante ( 10_.0), al lado del carácter exponente ( 1.1e_1) o al lado del especificador de tipo ( 10_f).

variables

Las variables son identificadores asociados con valores. Se declaran escribiendo el tipo y el nombre de la variable y, opcionalmente, se inicializan en la misma declaración.

Declarar

int miInt ; // Declarando una variable no inicializada llamada 'myInt', de tipo 'int'  

Asignar

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

Inicializar

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

Se pueden declarar e inicializar múltiples variables del mismo tipo en una sola declaración.

ent a , b ; // Declarando múltiples variables del mismo tipo   int a = 2 , b = 3 ; // Declarando e inicializando 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 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 adhiere más estrechamente al principio DRY .

var myChars = nuevo carácter [] { 'A' , 'Ö' }; // o char[] myChars = new char[] {'A', 'Ö'};       var myNums = nueva Lista < int > (); // o Lista<int> myNums = nueva Lista<int>();     

Constantes

Las constantes son valores inmutables.

const

Al declarar una variable local o un campo con la constpalabra clave como prefijo se debe dar el valor cuando se declara. Después de eso, queda bloqueado y no puede cambiar. Pueden declararse en el contexto como un campo o una variable local. Las constantes son implícitamente estáticas.

constante doble PI = 3,14 ;    

Esto muestra ambos usos de la palabra clave.

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

readonly

La readonlypalabra clave hace algo similar a los campos. Me gustan los campos marcados porque constno pueden cambiar una vez inicializados. La diferencia es que puede optar por inicializarlos en un constructor o con un valor que no se conoce hasta el tiempo de ejecución. Esto sólo funciona en campos. readonlyLos campos pueden ser miembros de una instancia o miembros de una clase estática.

Bloques de código

Las llaves se utilizan para indicar un bloque de código y un nuevo alcance . Los miembros de la clase y el cuerpo de un método son ejemplos de lo que puede vivir dentro de estas llaves en diversos contextos.{ ... }

Dentro de los cuerpos de los métodos, puede usar llaves para crear nuevos ámbitos como este:

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

Estructura del programa

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

método principal

Ya sea una consola o una aplicación de interfaz gráfica, el programa debe tener algún tipo de punto de entrada. El punto de entrada de la aplicación C# es el método llamado Main. Solo puede haber uno y es un método estático en una clase. El método normalmente devuelve voidy se le pasan argumentos de línea de comandos como una matriz de cadenas.

static void Main ( string [] args ) { } // O El método principal se puede definir sin parámetros. vacío estático principal () { }     

El método principal también puede devolver un valor entero si se especifica.

static int Principal ( cadena [] args ) { retorno 0 ; }     

Asíncrono principal

Esta es una característica de C# 7.1.

Se pueden esperar tareas asincrónicas en el Mainmétodo declarándolo como tipo de retorno Task.

Tarea asíncrona estática principal ( cadena [] args ) { espera 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 ceremonia de tener que declarar la Programclase con un Mainmétodo.

En cambio, las declaraciones se pueden escribir directamente en un archivo específico, y ese archivo será el punto de entrada del programa. El código de otros archivos aún deberá definirse en clases.

Esto se introdujo para hacer que C# sea menos detallado y, por lo tanto, más accesible para que los principiantes comiencen.

usando Sistema ; Consola . WriteLine ( "¡Hola mundo!" );

Los tipos se declaran después de las declaraciones y estarán disponibles automáticamente en las declaraciones que se encuentran encima de ellos.

Espacios de nombres

Los espacios de nombres son parte de un nombre de tipo y se utilizan para agrupar y/o distinguir entidades nombradas de otras.

Sistema . . DirectoryInfo // DirectoryInfo está en el espacio de nombres System.IO 

Un espacio de nombres se define así:

espacio de nombres FooNamespace { // Miembros }  

usingdirectiva

La usingdirectiva carga un espacio de nombres específico desde un ensamblado al que se hace referencia. Generalmente se coloca en la parte superior (o encabezado) de un archivo de código, pero se puede colocar en otro lugar si se desea, por ejemplo, dentro de las clases. [ cita necesaria ]

usando Sistema ; usando System.Collections ;  

La directiva también se puede utilizar para definir otro nombre para un tipo o espacio de nombres existente. A veces esto resulta útil cuando los nombres son demasiado largos y menos legibles.

usando Red = Sistema . Neto ; usando DirInfo = Sistema . . Información del directorio ;      

using staticdirectiva

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

utilizando un sistema estático . Consola ;  WriteLine ( "¡Hola, mundo!" );

Operadores

Sobrecarga del operador

Algunos de los operadores existentes se pueden sobrecargar escribiendo un método de sobrecarga.

operador público estático Foo + ( Foo foo , barra barra ) { return new Foo ( foo . Valor + barra . Valor ); }           

Estos son los operadores sobrecargables :

Operadores de conversión

El operador de conversión no se puede sobrecargar, pero puede escribir un método de operador de conversión que se encuentre en la clase de destino. Los métodos de conversión pueden definir dos variedades de operadores, operadores de conversión implícitos y explícitos. El operador implícito emitirá sin especificar con el operador de conversión ( ) y el operador explícito requiere que se utilice.( )

Operador de conversión implícita

clase Foo { valor int público ;     operador implícito estático público Foo ( valor int ) { return new Foo ( valor ); } } // Conversión implícita Foo foo = 2 ;             

Operador de conversión explícita

clase Foo { valor int público ;     operador explícito estático público Foo ( valor int ) { return new Foo ( valor ); } } // Conversión explícita Foo foo = ( Foo ) 2 ;             

asoperador

El asoperador intentará realizar una conversión silenciosa a un tipo determinado. Si es posible, devolverá el objeto como el nuevo tipo y, en caso contrario, devolverá un valor nulo.

Flujo de flujo = Archivo . Abrir ( @"C:\Temp\data.dat" ); FileStream fstream = transmitir como FileStream ; // Devolverá un objeto.         Cadena str = transmitir como cadena ; // Devolverá nulo.      

Operador de fusión nula

Esta es una característica de C# 2.0 .

La siguiente:

devolver ifNotNullValue ?? de lo contrarioValor ;   

es una abreviatura de:

devolver ifNotNullValue ! = nulo ? ifNotNullValue : de lo contrarioValue ;       

Lo que significa que si el contenido de la variable ifNotNullValueno es nulo, se devolverá ese contenido; de lo contrario, otherwiseValuese devolverá el contenido de la variable.

C# 8.0 introduce la asignación de fusión nula , de modo que

variable ??= de lo contrarioValor ;  

es equivalente a

si ( la variable es nula ) variable = de lo contrarioValor ;      

Estructuras de Control

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

Estructuras condicionales

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

ifdeclaración

La ifdeclaración se ingresa cuando la condición dada es verdadera. Las declaraciones de casos de una sola línea no requieren llaves, aunque por convención se prefiere.

Declaración simple de una línea:

si ( yo == 3 ) ... ;     

Multilínea con bloque else (sin llaves):

si ( i == 2 ) ... más ...     

Convenciones de codificación recomendadas para una declaración if.

si ( i == 3 ) { ... } más si ( i == 2 ) { ... } más { ... }          

switchdeclaración

La switchconstrucción sirve como filtro para diferentes valores. Cada valor conduce a un "caso". No está permitido pasar por secciones de casos y, por lo tanto, la palabra clave breakse utiliza normalmente para finalizar un caso. Un incondicional returnen una sección de caso también se puede utilizar para finalizar un caso. Vea también cómo gotose puede utilizar la declaración para pasar de un caso al siguiente. Sin embargo, muchos casos pueden conducir al mismo código. El caso predeterminado maneja todos los demás casos que no maneja la construcción.

cambiar ( ch ) { caso 'A' : declaración ; ... romper ; caso 'B' : declaración ; romper ; case 'C' : // Una sección de cambio puede tener varias etiquetas de caso. caso 'D' : ... descanso ; predeterminado : ... romper ; }                    

Estructuras de iteración

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

whilebucle

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

do ... whilebucle

hacer {} mientras ( i == verdadero );   

forbucle

El forbucle consta de tres partes: declaración , condición y contraexpresión . Cualquiera de ellos puede omitirse ya que son opcionales.

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

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

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

foreachbucle

La foreachdeclaración se deriva de la fordeclaración y hace uso de un determinado patrón 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 será devuelto y accesible en el contexto del bloque de código. Cuando se haya ejecutado el bloque, se devolverá el siguiente elemento hasta que no queden elementos restantes.

foreach ( int i en intList ) { ... }     

Declaraciones de salto

Las declaraciones de salto se heredan de C/C++ y, en última instancia, de los lenguajes ensambladores a través de él. Simplemente representan las instrucciones de salto de un lenguaje ensamblador que controla el flujo de un programa.

Etiquetas y gotodeclaración

Las etiquetas reciben puntos en el código a los que se puede acceder mediante la gotodeclaración.

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

Tenga en cuenta que no es necesario colocar la etiqueta después de la gotodeclaración; puede estar antes que él en el archivo fuente.

La gotodeclaración se puede utilizar en switchdeclaraciones para saltar de un caso a otro o para pasar de un caso al siguiente.

switch ( n ) { caso 1 : Consola . WriteLine ( "Caso 1" ); romper ; Caso 2 : Consola . WriteLine ( "Caso 2" ); ir al caso 1 ; Caso 3 : Consola . WriteLine ( "Caso 3" ); caso 4 : // La compilación fallará aquí ya que los casos no pueden fallar en C#. Consola . WriteLine ( "Caso 4" ); ir por defecto ; // Esta es la forma correcta de pasar al siguiente caso. caso 5 : // Múltiples etiquetas para el mismo código están bien caso 6 : predeterminado : Consola . WriteLine ( "Predeterminado" ); romper ; // Incluso el valor predeterminado no debe llegar al punto final }                              

breakdeclaración

La breakdeclaración sale del bucle o switchdeclaración más cercana. La ejecución continúa en la declaración después de la declaración terminada, si corresponde.

int mi = 10 ; for ( int i = 0 ; i < e ; i ++ ) { while ( verdadero ) { break ; } // Llegará a este punto. }                 

continuedeclaración

La continuedeclaración interrumpe la iteración actual de la declaración de control actual y comienza la siguiente iteración.

int ch ; mientras (( ch = Consola . Leer ()) ! = - 1 ) { if ( ch == '' ) continuar ; // Salta el resto del ciclo while             // Resto del ciclo while ... } 

El whilebucle en el código anterior lee los caracteres llamando a , omitiendo las declaraciones 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 tiene una clase llamada de la cual se derivan todas las demás clases de excepción. Un objeto contiene toda la información sobre una excepción específica y también las excepciones internas que fueron causadas. Los programadores pueden definir sus propias excepciones derivando de la clase.System.ExceptionExceptionException

Se puede lanzar una excepción de esta manera:

lanzar nueva NotImplementedException ();  

try ... catch ... finallydeclaraciones

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

try { // Declaraciones que pueden generar excepciones ... } catch ( Exception ex ) { // Excepción detectada y manejada aquí ... } finalmente { // Declaraciones siempre ejecutadas después de los bloques try/catch ... }        

Las declaraciones dentro del trybloque se ejecutan y, si alguna de ellas genera una excepción, la ejecución del bloque se interrumpe y el bloque maneja la excepción catch. Puede haber varios catchbloques, en cuyo caso se ejecuta el primer bloque con una variable de excepción cuyo tipo coincida con el tipo de excepción lanzada.

Si ningún catchbloque coincide con el tipo de excepción lanzada, se interrumpe la ejecución del bloque externo (o método) que contiene la try ... catchdeclaración y la excepción se pasa fuera del bloque o método que lo contiene. La excepción se propaga hacia arriba a través de la pila de llamadas hasta que se encuentra un catchbloque coincidente dentro de uno de los métodos actualmente activos. Si la excepción se propaga hasta el método superior sin que se encuentre un bloque coincidente , se finaliza todo el programa y se escribe una descripción textual de la excepción en el flujo de salida estándar.Main()catch

Las declaraciones dentro del finallybloque siempre se ejecutan después de los bloques tryy catch, independientemente de si se produjo o no una excepción. Estos bloques son útiles para proporcionar código de limpieza.

Un catchbloque, un finallybloque o ambos deben seguir al trybloque.

Tipos

C# es un lenguaje de tipo estático como C y C++. Eso significa que cada variable y constante obtiene un tipo fijo cuando se declara. Hay dos tipos de tipos: tipos de valor y tipos de referencia .

Tipos de valor

Las instancias de tipos de valores residen en la pila, es decir, están vinculadas a sus variables. Si declara una variable para un tipo de valor, la memoria se asigna directamente. Si la variable sale del alcance, el objeto se destruye con ella.

Estructuras

Las estructuras se conocen más comúnmente como estructuras . Las estructuras son tipos de valores definidos por el usuario que se declaran mediante la structpalabra clave. Son muy similares a las clases pero son más adecuados para tipos livianos. Más adelante en este artículo se presentan algunas diferencias sintácticas importantes entre una clase y una estructura.

estructura Foo { ... }  

Los tipos de datos primitivos son todos estructuras.

Tipos predefinidos

Estos son los tipos de datos primitivos.

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 Temporada { Invierno = 0 , Primavera = 1 , Verano = 2 , Otoño = 3 , Otoño = Otoño // Otoño se llama Otoño en inglés americano. }                 

Las variables de enumeración se inicializan de forma predeterminada en cero. Se pueden asignar o inicializar a 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 resta entre variables del mismo tipo sin ninguna conversión específica, pero la multiplicación y división es algo más arriesgada y requiere una conversión explícita. También se requieren conversiones para convertir variables de enumeración hacia y desde tipos enteros. Sin embargo, la conversión no generará una excepción si la definición de tipo no especifica el valor.

temporada = ( temporada ) 2 ; // lanza 2 a un valor de enumeración de tipo Temporada. temporada = temporada + 1 ; // Agrega 1 al valor. temporada = temporada + temporada2 ; // Sumando los valores de dos variables de enumeración. valor int = ( int ) temporada ; // Convertir valor-enum a valor entero.                 temporada ++ ; // Temporada.Primavera (1) se convierte en Temporada.Verano (2). estación -- ; // Temporada.Verano (2) se convierte en Temporada.Primavera (1).  

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

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

Tipos de referencia

Las variables creadas para tipos de referencia son referencias administradas escritas. Cuando se llama 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 alcance, la referencia se rompe y cuando no quedan referencias, el objeto se marca como basura. El recolector de basura pronto la recogerá y destruirá.

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

matrices

Un tipo de matriz es un tipo de referencia que hace referencia a un espacio que contiene uno o más elementos de un determinado tipo. Todos los tipos de matrices derivan de una clase base común, . Cada elemento está referenciado por su índice al igual que en C++ y Java.System.Array

Una matriz en C# es lo que se llamaría una matriz dinámica en C++.

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 conveniente para la inicialización de matrices.

// Sintaxis larga int [] números = new int [ 5 ]{ 20 , 1 , 42 , 15 , 34 }; // Sintaxis corta int [] números2 = { 20 , 1 , 42 , 15 , 34 }; // Sintaxis inferida var números3 = 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 [,] números2 = nuevo int [ 3 , 3 ] { { 2 , 3 , 2 }, { 1 , 2 , 6 }, { 2 , 4 , 5 } };                

Ver también

Clases

Las clases son tipos de referencia autodescriptivos definidos por el usuario. Básicamente, todos los tipos en .NET Framework son clases, incluidas estructuras y enumeraciones, que son clases generadas por el compilador. Los miembros de la clase son privatepor defecto, pero se pueden declarar como publicvisibles fuera de la clase o protectedpara cualquier descendiente de la clase.

Instrumentos de cuerda

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

Las acciones realizadas en una cadena siempre devolverán una nueva cadena.

texto de cadena = "¡Hola mundo!" ; cadena substr = texto . Subcadena ( 0 , 5 ); cadena [] partes = texto . Dividir ( nuevo carácter []{ ' ' });             

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

var sb = nuevo StringBuilder (); alguienAgregar ( 'H' ); alguienAgregar ( "el" ); alguienAppendLine ( "¡lo!" );    

Interfaz

Las interfaces son estructuras de datos que contienen definiciones de miembros sin implementación real. Una variable de un tipo de interfaz es una referencia a una instancia de una clase que implementa esta interfaz. Consulte #Interfaces.

Delegados

C# proporciona punteros de función orientados a objetos con seguridad de tipos en forma de delegados .

class Programa { // Tipo de delegado: delegado int Operación ( int a , int b );         estático int Agregar ( int i1 , int i2 ) { return i1 + i2 ; }            estático int Sub ( int i1 , int i2 ) { retorno i1 - i2 ; }            static void Main () { // Crea una instancia del delegado y asígnale el método. Operación op = Agregar ;         // Llama al método al que apunta el delegado. int resultado1 = op ( 2 , 3 ); // 5       op = Sub ; int resultado2 = op ( 10 , 2 ); // 8 } }         

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

suma = delegado ( int a , int b ) { return a + b ; };           

Inicializando el delegado con expresión lambda.

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

Eventos

Los eventos son indicadores que pueden apuntar a múltiples métodos. Más exactamente, vinculan punteros de método a un identificador. Por lo tanto, esto puede verse como una extensión para los delegados . Por lo general, se utilizan como desencadenantes en el desarrollo de la interfaz de usuario. El formato utilizado en C# y el resto de Common Language Infrastructure se basa en el del clásico Visual Basic .

delegado void MouseEventHandler ( objeto remitente , MouseEventArgs e );     Botón de clase pública : Sistema . Ventanas . Control S . Controlar { evento privado MouseEventHandler _onClick ;         /* Función de activación imaginaria */ void Click () { _onClick ( this , new MouseEventArgs ( data )); } }       

Un evento requiere un controlador de eventos acompañado que se crea a partir de un delegado especial que en una biblioteca específica de la plataforma como Windows Presentation Foundation y Windows Forms generalmente toma dos parámetros: el remitente y los argumentos del evento . El tipo de objeto-argumento del evento deriva de la clase EventArgs que forma parte de la biblioteca base 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 para que se active cuando se active el evento.

clase pública Ventana Principal : Sistema . Ventanas . Control S . Ventana { Botón privado _button1 ;        public MainWindow () { _button1 = nuevo Botón (); _botón 1 . Texto = "¡Haz clic en mí!" ;          /* Suscríbete al evento */ _button1 . ClickEvent += Button1_OnClick ;    /* Sintaxis alternativa que se considera antigua:  _button1.MouseClick += new MouseEventHandler(Button1_OnClick); */ }  protegido void Button1_OnClick ( objeto remitente , MouseEventArgs e ) { MessageBox . Mostrar ( "¡Se hizo clic!" ); } }        

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

manejador de eventos privado _clickHandles = ( s , e ) => { };        evento público EventHandler Haga clic en { agregar { // Algún código para ejecutar cuando se agrega el controlador... ...   _clickHandles += valor ; } remove { // Algún código para ejecutar cuando se elimina el controlador... ...  _clickHandles -= valor ; } }  

Ver también

Tipos que aceptan valores NULL

Esta es una característica de C# 2.0 .

Los tipos que aceptan valores NULL se introdujeron en C# 2.0 en primer lugar para permitir que los tipos de valores sean nulos (útil cuando se trabaja con una base de datos).

¿En t? norte = 2 ; norte = nulo ;     Consola . WriteLine ( n . HasValue );

En realidad, esto es lo mismo que usar la estructura.Nullable<T>

Anulable < int > n = 2 ; norte = nulo ;     Consola . WriteLine ( n . HasValue );

Consejos

C# tiene y permite punteros a tipos seleccionados (algunas primitivas, enumeraciones, cadenas, punteros e incluso matrices y estructuras si contienen solo tipos a los que se puede apuntar [6] ) en un contexto inseguro: métodos y bloques de código marcados unsafe. Son sintácticamente iguales que los punteros en C y C++. Sin embargo, la verificación del tiempo de ejecución está deshabilitada dentro de unsafelos bloques.

static void Principal ( cadena [] args ) { inseguro { int a = 2 ; int * b = &a a ;              Consola . WriteLine ( "Dirección de a: {0}. Valor: {1}" , ( int ) &a a , a ); Consola . WriteLine ( "Dirección de b: {0}. Valor: {1}. Valor de *b: {2}" , ( int ) & b , ( int ) b , * b );       // Generará algo como: // Dirección de a: 71953600. Valor: 2 // Dirección de b: 71953596. Valor: 71953600. Valor de *b: 2 } }   

Sólo se requiere que las estructuras sean estructuras puras sin miembros de un tipo de referencia administrado, por ejemplo, una cadena o cualquier otra clase.

estructura pública MyStruct { carácter público char ; público int Entero ; }        estructura pública MyContainerStruct { byte público Byte ; pública MiEstructura MiEstructura ; }        

En uso:

MiContainerStruct x ; MyContainerStruct * ptr = & x ;    valor del byte = ptr -> Byte ;   

Dinámica

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

El tipo dynamices una característica que permite la búsqueda dinámica en tiempo de ejecución en C# de forma estática. Dinámico denota una variable con un objeto con un tipo que se resuelve en tiempo de ejecución, a diferencia del tiempo de compilación, como se hace normalmente.

Esta característica aprovecha Dynamic Language Runtime (DLR) y ha sido diseñada específicamente con el objetivo de interoperar con lenguajes tipados dinámicamente como IronPython y IronRuby (implementaciones de Python y Ruby para .NET).

El soporte dinámico también facilita la interoperación con objetos COM .

dinámico x = nuevo Foo (); X . Hacer algo (); // Se compilará y resolverá en tiempo de ejecución. Se lanzará una excepción si no es válida.     

tipos anónimos

Esta es una característica de C# 3.0 .

Los tipos anónimos son clases sin nombre generadas por el compilador. Solo son consumibles y, sin embargo, muy útiles en un escenario como el que tiene una consulta LINQ que devuelve un objeto selecty solo desea devolver algunos valores específicos. Luego puede definir un tipo anónimo que contenga campos de solo lectura generados automáticamente para los valores.

Al crear una instancia de otra declaración de tipo anónima con la misma firma, el compilador infiere automáticamente el tipo.

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

Boxeo y unboxing

El boxeo es la operación de convertir un valor de un tipo de valor en un valor de un tipo de referencia correspondiente. [7] El boxeo en C# está implícito.

Unboxing es la operación de convertir un valor de un tipo de referencia (previamente encuadrado) en un valor de un tipo de valor. [7] Unboxing en C# requiere una conversión de tipo explícita.

Ejemplo:

intfoo = 42 ;// Tipo de valor. barra de objeto = foo ; // foo está encuadrado en bar. int foo2 = ( int ) barra ; // Sin empaquetar y volver al tipo de valor.            

Programación orientada a objetos (POO)

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

Objetos

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

En C#, los objetos son referencias o valores. No se hace ninguna distinción sintáctica adicional entre los que están en el código.

Clase de objeto

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

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

Miembros
Algunos de los miembros de la clase Objeto:

Clases

Las clases son fundamentos de un lenguaje orientado a objetos como C#. Sirven como plantilla para los objetos. Contienen miembros que almacenan y manipulan datos de forma similar a la vida real.

Diferencias entre clases y estructuras.

Aunque las clases y estructuras son similares tanto en la forma en que se declaran como en la forma en que se utilizan, existen algunas diferencias significativas. Las clases son tipos de referencia y las estructuras son tipos de valores. Una estructura se asigna en la pila cuando se declara y la variable está vinculada a su dirección. Contiene directamente el valor. Las clases son diferentes porque la memoria se asigna como objetos en el montón. Las variables son más bien punteros administrados en la pila que apuntan a los objetos. Son referentes.

Las estructuras se diferencian de las clases en varios otros aspectos. Por ejemplo, aunque ambos ofrecen un constructor predeterminado implícito que no acepta argumentos, no puedes redefinirlo para estructuras. Definir explícitamente un constructor con parámetros diferentes suprimirá el constructor predeterminado implícito en las clases, pero no en las estructuras. Todos los campos de una estructura deben inicializarse en ese tipo de constructores. Las estructuras no tienen finalizadores y no pueden heredar de otra clase como lo hacen las clases. Implícitamente, están sellados y heredan de (que hereda de ). Las estructuras son más adecuadas para cantidades más pequeñas de datos.System.ValueTypeSystem.Object

Este es un breve resumen de las diferencias:

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

Declaración

Una clase se declara así:

class 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 se divide en archivos separados. Las diferentes partes de una clase parcial deben marcarse con palabra clave partial.

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

La razón habitual para utilizar clases parciales es dividir alguna clase en una parte mantenida por el programador y una parte mantenida por la herramienta, es decir, parte del código se genera automáticamente mediante una herramienta de diseño de interfaz de usuario o algo similar.

Inicialización

Antes de poder utilizar los miembros de la clase, necesita inicializar la variable con una referencia a un objeto. Para crearlo, llama al constructor apropiado usando la newpalabra clave. Tiene el mismo nombre que la clase.

var foo = nuevo Foo ();    

Para las estructuras, es opcional llamar explícitamente a un constructor porque el predeterminado se llama automáticamente. Sólo necesitas declararlo y se inicializará con valores estándar.

Inicializadores de objetos
Esta es una característica de C# 3.0 .

Proporciona una forma más conveniente de inicializar campos públicos y propiedades de un objeto. Las llamadas al constructor son opcionales cuando hay un constructor predeterminado.

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

Los inicializadores de colecciones brindan una sintaxis similar a una matriz para inicializar colecciones. El compilador simplemente generará llamadas al método Add. Esto funciona para clases que implementan la interfaz ICollection.

var lista = nueva Lista <int> { 2 , 5 , 6 , 6 } ;        // Igual a var lista = nueva Lista < int > (); lista . Añadir ( 2 ); lista . Añadir ( 5 ); lista . Añadir ( 6 ); lista . Añadir ( 6 );    

Accediendo a miembros

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

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

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

Acceso a un miembro de clase estática
Se accede a los miembros estáticos utilizando el nombre de la clase u otro tipo.

int r = cadena . Comparar ( foo , fooUpper );    

Acceder a un miembro a través de un puntero
En código inseguro , se accede a los miembros de un valor (tipo de estructura) al que hace referencia un puntero con el ->operador como en C y C++.

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

Modificadores

Los modificadores son palabras clave que se utilizan para modificar declaraciones de tipos y miembros de tipo. En particular, hay un subgrupo que contiene los modificadores de acceso.

modificadores de clase
Modificadores de miembros de clase
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 clase. Foo . Algo ();         
Modificadores de acceso

Los modificadores de acceso , o modificadores de herencia , establecen la accesibilidad de clases, métodos y otros miembros. publicSe puede llegar a algo marcado desde cualquier lugar. privateSolo se puede acceder a los miembros desde dentro de la clase en la que están declarados y estarán ocultos cuando se hereden. Los miembros con el protectedmodificador serán private, pero accesibles cuando se hereden. internalLas clases y los miembros solo serán accesibles desde el interior de la asamblea declarante.

Las clases y estructuras son implícitas internaly los miembros lo son privatesi no tienen un modificador de acceso.

clase pública Foo { public int Do () { retorno 0 ; }          barra de clase pública {    } }

Esta tabla define dónde se pueden utilizar los modificadores de acceso.

Constructores

Un constructor es un método especial que se llama automáticamente cuando se crea un objeto. Su propósito es inicializar los miembros del objeto. Los constructores tienen el mismo nombre que la clase y no devuelven nada explícitamente. Implícitamente, devolverán el objeto recién creado cuando se les llame a través del newoperador. Pueden tomar parámetros como cualquier otro método. El constructor sin parámetros es especial porque se puede especificar como una restricción necesaria para un parámetro de tipo genérico.

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

Los constructores pueden ser public, o private.protectedinternal

Incinerador de basuras

Se llama al destructor cuando el recolector de basura recolecta el objeto para realizar alguna limpieza manual. Hay un método destructor predeterminado llamado finalizeque puede anularse declarando el suyo propio.

La sintaxis es similar a la de los constructores. La diferencia es que el nombre está precedido por ~ y no puede contener ningún parámetro. No puede haber más de un destructor.

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

Los finalizadores son siempre private.

Métodos

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

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

Un método se llama usando .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 . Barra ( 7 , 2 );        Consola . Línea de escritura ( r );
refy outparámetros

Se pueden hacer explícitamente que los argumentos se pasen por referencia al llamar a un método con parámetros precedidos por palabras clave refo out. Estos punteros administrados resultan útiles al pasar variables que desea modificar dentro del método por referencia. La principal diferencia entre los dos es que outse debe haber asignado un parámetro dentro del método cuando el método regresa. refpuede 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 ; } intQ ;PassOut ( fuera Q );        
Parámetros opcionales
Esta es una característica de C# 4.0 .

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

Incremento vacío ( 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, lo que permite pasar selectivamente cualquier subconjunto determinado de parámetros opcionales para un método. La única restricción es que los parámetros con nombre deben colocarse después de los parámetros sin nombre. Los nombres de los parámetros se pueden especificar 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 ( nombre de cadena , modo FileMode = FileMode . Abrir , acceso a FileAccess = FileAccess . Leer ) { ... }            OpenFile ( "archivo.txt" ); // usa valores predeterminados tanto para "modo" como para "acceso" a OpenFile ( "file.txt" , modo : FileMode . Create ); // usa el valor predeterminado para "acceder" a OpenFile ( "file.txt" , acceso : FileAccess . Read ); // usar el valor predeterminado para "modo" OpenFile ( nombre : "archivo.txt" , acceso : FileAccess . Leer , modo : FileMode . Crear ); // 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 del método del componente COM, incluso aquellos que son opcionales. Por ejemplo:

nombre de archivo de objeto = "Prueba.docx" ; objeto faltante = Sistema . Reflexión . Desaparecido . Valor ;      doc . Guardar como ( nombre de archivo de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia , falta de referencia ) ; consola . writeline ( "Archivo guardado exitosamente" );                               

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

doc . Guardar como ( nombre de archivo de referencia ); 
extern

Una característica de C# es la capacidad de llamar a código nativo. La firma de un método simplemente se declara sin cuerpo y se marca como extern. El DllImportatributo también debe agregarse para hacer referencia al archivo DLL deseado.

[DllImport("win32.dll")] Pow doble externo estático ( doble a , doble b );      

Campos

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

clase Foo { doble foo ; }   

Los campos se pueden inicializar directamente cuando se declaran (a menos que se declaren en la estructura).

clase Foo { doble foo = 2,3 ; }     

Modificadores para campos:

Propiedades

Las propiedades aportan una sintaxis similar a la de un campo y la combinan con el poder de los métodos. Una propiedad puede tener dos descriptores de acceso: gety set.

Persona de clase pública { cadena privada _nombre ;      cadena Nombre { get { return _name ; } establecer { _nombre = valor ; } } }              // Usando una propiedad var persona = nueva Persona (); persona . Nombre = "Roberto" ;      

Modificadores de propiedades:

Modificadores para descriptores de acceso a propiedades:

Los modificadores predeterminados para los descriptores de acceso se heredan de la propiedad. Tenga en cuenta que los modificadores del descriptor de acceso 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 .

Una característica de C# 3.0 son las propiedades implementadas automáticamente. Usted define descriptores de acceso sin cuerpos y el compilador generará un campo de respaldo y el código necesario para los descriptores de acceso.

ancho doble público { obtener ; conjunto privado ; }       

Indexadores

Los indexadores agregan capacidades de indexación similares a matrices a los objetos. Se implementan de forma similar a las propiedades.

clase interna ListaInt { privado int [] _items ;      int this [ int índice ] { get { return _items [ índice ]; } establecer { _items [ índice ] = valor ; } } }               // Usando un indexador var list = new 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, pero los herederos pueden anularlos mediante la overridepalabra clave.

La implementación se elige por el tipo real de objeto y no por el tipo de variable.

operación de clase { public virtual int Do () { return 0 ; } }         clase NuevaOperación : Operación { anulación pública int Do () { retorno 1 ; } }           
new

Al sobrecargar un método no virtual con otra firma, newse puede utilizar la palabra clave. El método utilizado será elegido por el tipo de variable en lugar del tipo real del objeto.

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

Esto demuestra el caso:

operación var = nueva NuevaOperación ();    // Llamará a "double Do()" en NewOperation double d = operación . Hacer ();   Operación operación_ = operación ;   // Llamará a "int Do()" en Operación int i = operación_ . Hacer ();   
abstract

Las clases abstractas son clases que solo sirven como plantillas y no puedes inicializar un objeto de ese tipo. De lo contrario, es como una clase normal.

También puede haber miembros abstractos. Los miembros abstractos son miembros de clases abstractas que no tienen ninguna implementación. Deben ser anulados por cualquier clase no abstracta que herede el miembro.

clase abstracta Mamífero { public abstract void Caminar (); }      clase Humano : Mamífero { anulación pública void Caminar () {         } ... }
sealed

El sealedmodificador se puede combinar con los demás como modificador opcional para que las clases las hagan no heredables, o para que los métodos no permitan anularlos en clases derivadas.

clase interna sellada Foo { //... }    Barra de clase pública { Acción pública virtual vacía () { //... } }         clase pública Baz : Barra { acción nula de anulación pública sellada () { //... } }            

Interfaces

Las interfaces son estructuras de datos que contienen definiciones de miembros y no una implementación real. Son útiles cuando desea definir un contrato entre miembros de diferentes tipos que tienen diferentes implementaciones. Puede declarar definiciones de métodos, propiedades e indexadores. Los miembros de la interfaz son implícitamente públicos. Una interfaz puede implementarse implícita o explícitamente.

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

Implementando una interfaz

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

Implementación implícita

Al implementar implícitamente una interfaz, los miembros de la interfaz deben ser public.

Sumador de clase pública : IBinaryOperation { public double A { get ; colocar ; } público doble B { get ; colocar ; }                   público doble GetResult () { return A + B ; } }        Multiplicador de clase pública : IBinaryOperation { public double A { get ; colocar ; } público doble B { get ; colocar ; }                   público doble GetResult () { return A * B ; } }        

En uso:

IBinaryOperation op = nulo ; doble resultado ;    // Adder 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

También puede implementar miembros explícitamente. Los miembros de la interfaz que una clase implementa explícitamente son accesibles solo cuando el objeto se maneja como tipo de interfaz.

Sumador de clase pública : IBinaryOperation { double IBinaryOperation . A { obtener ; colocar ; } doble IBinaryOperation . B { obtener ; colocar ; }                 doble IBinaryOperation . GetResult () { retorno (( IBinaryOperation ) esto ). A + (( IBinaryOperation ) esto ). B ; } }       

En uso:

Sumador agregar = nuevo Sumador ();    // Estos miembros no son accesibles: // add.A = 2; // agregar.B = 3; // doble resultado = add.GetResult();// Transmitir al tipo de interfaz para acceder a ellos: IBinaryOperation add2 = add ; agregar2 . Un = 2 ; agregar2 . B = 3 ;       resultado doble = sumar2 . ObtenerResultado ();   

Nota:IBinaryOperation El compilador implementa automáticamente las propiedades de la clase que se extiende y se agrega automáticamente un campo de respaldo (consulte #Propiedades automáticas).

Ampliación de múltiples interfaces

Las interfaces y clases pueden ampliar múltiples interfaces.

clase MiClase : IInterfazA , IInterfazB { ... }     

Aquí hay una interfaz que extiende dos interfaces.

interfaz IInterfaceC : IInterfaceA , IInterfaceB { ... }     

Interfaces versus clases abstractas

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

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, que permiten diseñar clases y métodos que no especifican el tipo utilizado hasta que se crea una instancia de la clase o método. La principal ventaja es que se pueden usar parámetros de tipo genérico para crear clases y métodos que se pueden usar sin incurrir en el costo de conversiones en tiempo de ejecución u operaciones de boxeo, como se muestra aquí: [8]

// Declarar la clase genérica.clase pública Lista genérica <T> { void Agregar ( entrada T ) { } }       clase TestGenericList { clase privada EjemploClase { } static void Main () { // Declara una lista de tipo int. var lista1 = nueva ListaGenérica < int > ();                 // Declarar una lista de tipo cadena. var lista2 = nueva ListaGenérica <cadena> ( ) ;      // Declarar una lista de tipo EjemploClase. var lista3 = nueva ListaGenérica <ClaseEjemplo> ( ) ; } }      

En comparación con las plantillas de C++ , los genéricos de C# pueden proporcionar mayor seguridad, pero también tienen capacidades algo limitadas. [9] Por ejemplo, no es posible llamar a operadores aritméticos en un tipo genérico de C#. [10] A diferencia de las plantillas de C++, los tipos parametrizados de .NET se crean instancias en tiempo de ejecución en lugar de hacerlo el compilador; por lo tanto, pueden ser multilingües, mientras que las plantillas de C++ no. Admiten algunas características que las plantillas de C++ no admiten directamente, como restricciones de tipo en parámetros genéricos mediante el uso de interfaces. Por otro lado, C# no admite parámetros genéricos que no sean de tipo.

A diferencia de los genéricos en Java, los genéricos de .NET utilizan la reificación para crear tipos parametrizados objetos de primera clase en la máquina virtual de Common Language Infrastructure (CLI), lo que permite optimizaciones y preservación de la información de tipos. [11]

Usando genéricos

Clases genéricas

Las clases y estructuras pueden ser genéricas.

lista de clase pública <T> { ... public void Agregar ( elemento T ) { ... } }          var lista = nueva Lista <int> ( ) ; lista . Añadir ( 6 ); lista . Añadir ( 2 );    

Interfaces genéricas

interfaz IEnumerable <T> { ... }  

Delegados genéricos

delegar R Func < T1 , T2 , R > ( T1 a1 , T2 a2 );       

Métodos genéricos

public static T [] CombineArrays < T > ( T [] a , T [] b ) { T [] newArray = new T [ a . Longitud + b . Longitud ]; a . Copiar a ( nuevamatriz , 0 ); b . CopyTo ( newArray , a . Longitud ); devolver nuevamatriz ; }                   cadena [] a = nueva cadena [] { "a" , "b" , "c" }; cadena [] b = nueva cadena [] { "1" , "2" , "3" }; cadena [] c = CombineArrays ( a , b );                      doble [] da = nuevo doble [] { 1.2 , 2.17 , 3.141592 }; doble [] db = nuevo doble [] { 4.44 , 5.6 , 6.02 }; doble [] dc = CombineArrays ( da , db );                      // c es una matriz de cadenas que contiene { "a", "b", "c", "1", "2", "3"} // dc es una matriz doble que contiene { 1.2, 2.17, 3.141592, 4.44, 5.6, 6.02}

Parámetros de tipo

Los parámetros de tipo son nombres que se utilizan en lugar de tipos concretos al definir un nuevo genérico. Se pueden asociar con clases o métodos colocando el parámetro de tipo entre corchetes angulares . Al crear una instancia (o llamar) a un genérico, puede sustituir un tipo concreto por el parámetro de tipo que proporcionó en su declaración. Los parámetros de tipo pueden restringirse mediante el uso de la palabra clave y una especificación de restricción; se puede usar cualquiera de las seis restricciones separadas por comas: [12]< >where

Covarianza y contravarianza

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

Las interfaces genéricas y los delegados pueden tener sus parámetros de tipo marcados como covariantes o contravariantes , utilizando palabras clave outy in, respectivamente. Luego, estas declaraciones se respetan para las conversiones de tipos, tanto implícitas como explícitas, y tanto en tiempo de compilación como de ejecución. Por ejemplo, la interfaz existente se ha redefinido de la siguiente manera:IEnumerable<T>

interfaz IEnumerable < salida T > { IEnumerator < T > GetEnumerator (); }    

Por lo tanto, cualquier clase que se implemente para alguna clase también se considera compatible con todas las clases e interfaces que se extienden, directa o indirectamente. En la práctica, permite escribir código como:IEnumerable<Derived>DerivedIEnumerable<Base>BaseDerived

void PrintAll ( IEnumerable <objeto> objetos ) { foreach ( objeto o en objetos ) { System .Consola . Línea de escritura ( o ); } }          IEnumerable <cadena> cadenas = nueva Lista <cadena> ( ) ;ImprimirTodo ( cadenas ); // IEnumerable<cadena> se convierte implícitamente a IEnumerable<objeto>     

Para contravarianza, la interfaz existente se ha redefinido de la siguiente manera:IComparer<T>

interfaz pública IComparer < en T > { int Comparar ( T x , T y ); }        

Por lo tanto, cualquier clase que implemente para alguna clase también se considera compatible con todas las clases e interfaces que se extienden desde . Permite escribir código como:IComparer<Base>BaseIComparer<Derived>DerivedBase

IComparer <objeto> objectComparer = GetComparer ( ) ; IComparer <cadena> stringComparer = objectComparer ;      

Enumeradores

Un enumerador es un iterador. Los enumeradores normalmente se obtienen llamando al método de un objeto que implementa la interfaz. Las clases de contenedor normalmente implementan esta interfaz. Sin embargo, la declaración foreach en C# puede operar en cualquier objeto que proporcione dicho método, incluso si no implementa . Esta interfaz se amplió a una versión genérica en .NET 2.0 .GetEnumerator()IEnumerableIEnumerable

A continuación se muestra un uso simple de iteradores en C# 2.0:

// versión explícita IEnumerator < MyType > iter = lista . ObtenerEnumerador (); while ( iter . MoveNext ()) Consola . WriteLine ( iter . Actual );     // versión implícita de cada ( valor MyType en la lista ) Consola . WriteLine ( valor );     

Funcionalidad del generador

Esta es una característica de C# 2.0 .

.NET 2.0 Framework permitió a C# introducir un iterador que proporciona funcionalidad de generador , utilizando una construcción similar a Python . [13] Con a , la función mantiene automáticamente su estado durante la iteración.yield returnyieldyield return

// Método que toma una entrada iterable (posiblemente una matriz) // y devuelve todos los números pares. public static IEnumerable < int > GetEven ( IEnumerable < int > números ) { foreach ( int i en números ) { if ( i % 2 == 0 ) rendimiento retorno i ; } }                  // usando el método para generar solo números pares de la matriz static void Main () { int [] números = { 1 , 2 , 3 , 4 , 5 , 6 }; foreach ( int i en GetEven ( números )) Consola . Línea de escritura ( yo ); //salidas 2, 4 y 6 }                   

LINQ

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

LINQ, abreviatura de Language Integrated Queries, es una característica de .NET Framework que simplifica el manejo de datos. Principalmente agrega soporte que le permite consultar matrices, colecciones y bases de datos. También introduce carpetas, lo que facilita el acceso a las bases de datos y sus datos.

Sintaxis de consulta

La sintaxis de consulta LINQ se introdujo en C# 3.0 y permite escribir consultas similares a SQL en C#.

var lista = nueva Lista <int> { 2 , 7 , 1 , 3 , 9 } ;          resultado var = de i en la lista donde i > 1 seleccione i ;            

Las declaraciones se compilan en llamadas a métodos, en las que casi sólo se especifican los nombres de los métodos. Los métodos que se utilizan en última instancia están determinados por la resolución normal de sobrecarga. Por lo tanto, el resultado final de la traducción se ve afectado por los símbolos que están dentro del alcance.

Lo que se diferencia de SQL es que la declaración from viene primero y no al final como en SQL. Esto se debe a que parece más natural escribir así en C# [ cita requerida ] y admite "Intellisense" (finalización de código en el editor).

Métodos anónimos

Los métodos anónimos , o en su forma actual más comúnmente conocidos como "expresiones lambda", son una característica que permite a los programadores escribir funciones similares a cierres en línea en su código.

Hay varias formas de crear métodos anónimos. Antes de C# 3.0 había soporte limitado mediante el uso de delegados.

Delegados anónimos

Esta es una característica de C# 2.0 .

Los delegados anónimos son punteros de funciones que contienen métodos anónimos. El propósito es simplificar el uso de delegados simplificando el proceso de asignación de funciones. En lugar de declarar un método separado en el código, el programador puede usar la sintaxis para escribir el código en línea y el compilador generará una función anónima para él.

Func < int , int > f = delegado ( int x ) { return x * 2 ; };           

expresiones lambda

Esta es una característica de C# 3.0 .

Las expresiones lambda proporcionan una sintaxis simple para funciones en línea que son similares a los cierres. Las funciones con parámetros infieren el tipo de parámetros si no se especifica explícitamente otro.

// [argumentos] => [cuerpo-método]// Con parámetros n => n == 2 ( a , b ) => a + b ( a , b ) => { a ++ ; devolver a + b ; }                  // Con parámetros escritos explícitamente ( int a , int b ) => a + b       // Sin parámetros () => devolver 0   // Asignando lambda para delegar Func < int , int , int > f = ( a , b ) => a + b ;          

Las lambdas de declaraciones múltiples tienen cuerpos encerrados entre llaves y dentro de ellos se puede escribir código como en los métodos estándar.

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

Las expresiones Lambda se pueden pasar como argumentos directamente en llamadas a métodos similares a los delegados anónimos pero con una sintaxis más estética.

var lista = lista de cadenas . Donde ( n => n . Longitud > 2 );       

Las expresiones Lambda son esencialmente métodos generados por el compilador que se pasan a través de delegados. Estos métodos están reservados únicamente para el compilador y no se pueden utilizar en ningún otro contexto.

Métodos de extensión

Esta es una característica de C# 3.0 .

Los métodos de extensión son una forma de azúcar sintáctico que proporciona la ilusión de agregar nuevos métodos a la clase existente fuera de su definición. En la práctica, un método de extensión es un método estático que se puede llamar como si fuera un método de instancia; el receptor de la llamada está vinculado al primer parámetro del método, decorado con la palabra clave this:

clase estática pública StringExtensions { cadena estática pública izquierda ( esta cadena s , int n ) { retorno s . Subcadena ( 0 , n ); } }                cadena s = "foo" ; s . Izquierda ( 3 ); // igual que StringExtensions.Left(s, 3);    

Funciones locales

Esta es una característica de C# 7.0.

Las funciones locales se pueden definir en el cuerpo de otro método, constructor o captador y definidor de propiedades. Dichas funciones tienen acceso a todas las variables en el ámbito adjunto, incluidas las variables locales del método principal. Están dentro del alcance de todo el método, independientemente de si se invocan antes o después de su declaración. Los modificadores de acceso (público, privado, protegido) no se pueden utilizar con funciones locales. Además, no admiten la sobrecarga de funciones . Significa que no puede haber dos funciones locales en el mismo método con el mismo nombre incluso si las firmas no se superponen. [14] Después de una compilación, una función local se transforma en un método estático privado, pero cuando se define no se puede marcar como estática. [15]

En el ejemplo de código siguiente, el método Sum es una función local dentro del método Main. Por lo tanto, sólo se puede utilizar dentro de su método principal Main:

static void Principal ( cadena [] args ) { int Suma ( int x , int y ) { return x + y ; }               Consola . WriteLine ( Suma ( 10 , 20 )); Consola . Leer la clave (); }  

Misceláneas

Bloques de cierre

C# implementa bloques de cierre mediante la declaración de uso. La usingdeclaración acepta una expresión que da como resultado la implementación de un objeto IDisposabley el compilador genera código que garantiza la eliminación del objeto cuando usingse sale del alcance de la declaración. La usingdeclaración es azúcar sintáctica . Hace que el código sea más legible que el bloque equivalente.try ... finally

public void Foo () { usando ( var bar = Archivo . Open ( "Foo.txt" )) { // hacer un poco de trabajo throw new Exception (); // la barra aún se eliminará correctamente. } }              

Sincronización de hilos

C# proporciona la instrucción lock, que es otro ejemplo más de azúcar sintáctico beneficioso. Funciona marcando un bloque de código como sección crítica mediante exclusión mutua del acceso a un objeto proporcionado. Al igual que la usingdeclaración, funciona cuando el compilador genera un bloque en su lugar.try ... finally

StreamWriter estático privado _writer ;   public void ConcurrentMethod () { lock ( _writer ) { _writer . WriteLine ( "Línea 1." ); _escritor . WriteLine ( "Seguido de la línea 2." ); } }        

Atributos

Los atributos son entidades de datos que se almacenan como metadatos en el ensamblado compilado. Se puede agregar un atributo a tipos y miembros como propiedades y métodos. Los atributos se pueden utilizar para un mejor mantenimiento de las directivas del preprocesador.

[CompilerGenerated] clase pública $ AnonymousType $ 120 { [CompilerGenerated] nombre de cadena pública { get ; colocar ; } }          

.NET Framework viene con atributos predefinidos que se pueden utilizar. Algunos de ellos desempeñan un papel importante en tiempo de ejecución, mientras que otros son solo para decoración sintáctica en código como CompilerGenerated. Sólo marca que es un elemento generado por el compilador. También se pueden crear atributos definidos por el programador.

Un atributo es esencialmente una clase que hereda de la clase. Por convención, las clases de atributos terminan con "Atributo" en su nombre. Esto no será necesario al usarlo.System.Attribute

clase pública EdibleAttribute : Atributo { public EdibleAttribute () : base () {          } public EdibleAttribute ( bool isNotPoisonous ) { this . Es venenoso = ! no es venenoso ; }        public bool Es Venenoso { get ; colocar ; } }      

Mostrando el atributo en uso usando los parámetros opcionales del constructor.

[Comestible (verdadero)] clase pública Melocotón : Fruta { // Miembros, si los hay }     

Preprocesador

C# presenta "directivas de preprocesador" [16] (aunque no tiene un preprocesador real) basadas en el preprocesador de C que permiten a los programadores definir símbolos , pero no macros. También se proporcionan condicionales como #if, #endify .#else

Directivas como #regiondan sugerencias a los editores para plegar código . El #regionbloque debe finalizar con una #endregiondirectiva.

public class Foo { #region Constructores public Foo () {} public Foo ( int firstParam ) {} #endregion            #region Métodos public void IntBar ( int firstParam ) {} public void StrBar ( string firstParam ) {} public void BoolBar ( bool firstParam ) {} #endregion }                

Comentarios de código

C# utiliza una doble barra ( //) para indicar que el resto de la línea es un comentario.

public class Foo { // un comentario public static void Bar ( int firstParam ) {} // También un comentario }          

Los comentarios de varias líneas se pueden indicar mediante una barra diagonal/asterisco inicial ( /*) y un asterisco/barra diagonal final ( */).

public class Foo { /* Un  comentario de varias líneas */ public static void Bar ( int firstParam ) {} }         

Los comentarios no se anidan. Estos son dos únicos comentarios:

// Poder poner /* */ */ */ /* /*
/* Se puede poner /* /* /* pero termina en */

Para la documentación XML se utilizan comentarios de una sola línea que comienzan con tres barras. Sin embargo, esta es una convención utilizada por Visual Studio y no forma parte de la definición del lenguaje:

 /// <summary> /// Esta clase tiene mucha clase. /// </summary>  

Comentarios de documentación XML

Los comentarios de la documentación de C# [17] son ​​similares al Javadoc de Java , pero basados ​​en XML . Actualmente, el compilador de C# admite dos métodos de documentación .

Los comentarios de documentación de una sola línea, como los que se encuentran comúnmente en el código generado por Visual Studio , se indican en una línea que comienza con ///.

public class Foo { /// <summary>Un resumen del método.</summary> /// <param name="firstParam">Una descripción del parámetro.</param> /// <remarks>Observaciones sobre el método.</remarks> barra vacía estática pública ( int firstParam ) {} }           

Los comentarios de documentación de varias líneas, aunque estaban definidos en la especificación del lenguaje de la versión 1.0, no fueron compatibles hasta la versión .NET 1.1. [18] Estos comentarios se designan mediante una barra diagonal inicial/asterisco/asterisco ( /**) y un asterisco/barra diagonal final ( */). [19]

public class Foo { /** <summary>Un resumen del método.</summary>  * <param name="firstParam">Una descripción del parámetro.</param>  * <remarks>Observaciones sobre el método.</ comentarios> */ barra pública estática vacía ( int firstParam ) {} }         

Existen algunos criterios estrictos con respecto a los espacios en blanco y la documentación XML cuando se utiliza la /**técnica de barra diagonal/asterisco/asterisco ().

Este bloque de código:

/** * <summary> * Un resumen del método.</summary>*/

produce un comentario XML diferente al de este bloque de código: [19]

/** * <summary>  Un resumen del método.</summary>*/

La sintaxis de los comentarios de la documentación y su marcado XML se define en un anexo no normativo del estándar ECMA C#. El mismo estándar también define reglas para el procesamiento de dichos comentarios y su transformación a un documento XML simple con reglas precisas para el mapeo de identificadores de infraestructura de lenguaje común (CLI) a sus elementos de documentación relacionados. Esto permite que cualquier entorno de desarrollo integrado (IDE) de C# u otra herramienta de desarrollo encuentre documentación para cualquier símbolo en el código de una manera determinada y bien definida.

Sintaxis de espera asíncrona

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

A partir de .NET Framework 4 existe una biblioteca de tareas que facilita la escritura de aplicaciones paralelas y multiproceso a través de tareas.

C# 5.0 tiene soporte de lenguaje nativo para asincronía.

Considere este código que aprovecha la biblioteca de tareas directamente:

clase estática pública SomeAsyncCode { Tarea estática pública < XDocument > GetContentAsync () { var httpClient = nuevo HttpClient (); devolver httpCliente . GetStringAsync ( "https://www.contoso.com/" ). Continuar con (( tarea ) => { cadena respuestaBodyAsText = tarea . Resultado ; devolver XDocument . Parse ( respuestaBodyAsText ); }); } }                         var t = Algún código asincrónico . GetContentAsync (). Continuar con (( tarea ) => { var xmlDocument = tarea . Resultado ; });         t . Comenzar ();

Aquí está la misma lógica escrita en la sintaxis async-await :

clase estática pública SomeAsyncCode { Tarea asíncrona estática pública < XDocument > GetContentAsync () { var httpClient = nuevo HttpClient (); cadena respuestaBodyAsText = espera httpClient . GetStringAsync ( "https://www.contoso.com/" ); devolver XDocumento . Analizar ( respuestaBodyAsText ); } }                      var xmlDocument = espera SomeAsyncCode . GetContentAsync ();    // La tarea se iniciará durante la llamada con await.

dialectos

Especificaciones#

Spec# es un dialecto de C# que se desarrolla en paralelo con la implementación estándar de Microsoft. Amplía C# con características del lenguaje de especificación y es una posible característica futura del lenguaje C#. También agrega sintaxis para la API de contratos de código que se introdujo en .NET Framework 4.0 . Spec# está siendo desarrollado por Microsoft Research .

Este ejemplo muestra dos de las estructuras básicas que se utilizan al agregar contratos a su código.

static void Main ( cadena ! [] args ) requiere args . Longitud > 0 { foreach ( argumento de cadena en argumentos ) {              } }

Tipos no anulables

Spec# extiende C# con tipos que no aceptan valores NULL que simplemente verifican que las variables de los tipos que aceptan NULL que se han configurado como no NULL no sean nulas. Si es nulo, se lanzará una excepción.

 cadena ! aporte 

En uso:

Prueba pública ( cadena ! Entrada ) { ... }   

Condiciones previas

Las condiciones previas se verifican antes de ejecutar un método.

La prueba pública ( int i ) requiere i > 0 ; { este . yo = yo ; }         

Poscondiciones

Las poscondiciones son condiciones que se garantiza que serán correctas cuando se ha ejecutado un método.

public void Increment () garantiza i > 0 ; { yo ++ ; }       

Excepciones marcadas

Spec# agrega excepciones marcadas como las de Java .

public void DoSomething () lanza SomeException ; // AlgunaExcepción: ICheckedException { ... }      

Las excepciones marcadas son problemáticas, porque cuando una función de nivel inferior agrega un nuevo tipo de excepción, toda la cadena de métodos que utilizan este método en algún nivel inferior anidado también debe cambiar su contrato. Esto viola el principio abierto/cerrado . [20]

Ver también

Referencias

  1. ^ "Convenciones de codificación de C#". Microsoft aprende . segundo. Convenciones de nombres. Archivado desde el original el 16 de enero de 2023.
  2. ^ ab Wagner, Bill. "Palabras clave de C#". docs.microsoft.com . Consultado el 26 de agosto de 2022 .
  3. ^ Schildt, Herbert (30 de diciembre de 2008), C# 3.0: la referencia completa, ISBN 9780071588416
  4. ^ Deitel, Harvey M.; Deitel, Paul J. (21 de noviembre de 2005), C# para programadores, ISBN 9780132465915
  5. ^ Wagner, Bill. "Palabras clave de C#". docs.microsoft.com . Consultado el 26 de agosto de 2022 .
  6. ^ Tipos de puntero (Guía de programación de C#)
  7. ^ ab Archer, Parte 2, Capítulo 4: El sistema de tipos
  8. ^ "Genéricos (Guía de programación de C#)". Microsoft . Consultado el 7 de agosto de 2011 .
  9. ^ "Introducción a los genéricos de C#". Microsoft.
  10. ^ "Diferencias entre plantillas de C++ y genéricos de C#". Microsoft.
  11. ^ "Introducción a los genéricos de C#". Microsoft . Enero de 2005 . Consultado el 18 de junio de 2009 .
  12. ^ Restricciones en los parámetros de tipo (Guía de programación de C#) en Microsoft MSDN
  13. ^ "rendimiento". Referencia del lenguaje C# . Microsoft . Consultado el 26 de abril de 2009 .
  14. ^ ".NET Framework: novedades de C# 7.0". msdn.microsoft.com . Consultado el 8 de abril de 2017 .
  15. ^ "Reflexiones sobre las funciones locales de C# 7". Antón Sizikov . 15 de abril de 2016 . Consultado el 8 de abril de 2017 .
  16. ^ "Directivas del preprocesador de C#". Referencia del lenguaje C# . Microsoft . Consultado el 18 de junio de 2009 .
  17. ^ "Comentarios de la documentación". Microsoft aprende . 15 de junio de 2023.
  18. ^ Horton, Anson (11 de septiembre de 2006). "Preguntas frecuentes sobre los comentarios de la documentación XML de C#" . Consultado el 11 de diciembre de 2007 .
  19. ^ ab "Delimitadores para etiquetas de documentación". Referencia del programador de C# . Microsoft . Archivado desde el original el 20 de diciembre de 2008 . Consultado el 18 de junio de 2009 .
  20. ^ Martín, Robert C. (11 de agosto de 2008). "7 Manejo de errores, utilice excepciones no marcadas". Código limpio: un manual de artesanía de software ágil . Prentice Hall Internacional. ISBN 978-0132350884.
  1. Arquero, Tom (2001). Dentro de C# . Prensa de Microsoft. ISBN 0-7356-1288-9.
  2. Bart de Smet en Spec# Archivado el 29 de octubre de 2010 en Wayback Machine .

enlaces externos