Este artículo describe la sintaxis del lenguaje de programación C # . Las funciones descritas son compatibles con .NET Framework y Mono .
Lo esencial
Identificador
Un identificador es el nombre de un elemento en el código . Hay ciertas convenciones de nomenclatura estándar que se deben seguir al seleccionar nombres para elementos.
Un identificador puede:
- empezar con un guión bajo: _
- contener un guión bajo: _
- contener un dígito: 0123456789
- contener tanto mayúsculas y minúsculas letras Unicode. El caso es sensible ( FOO es diferente de foo )
- comience con un signo @ (pero esto es insignificante;
@name
es el mismo identificador quename
).
Un identificador no puede:
- empezar con un dígito
- empezar con un símbolo, a menos que sea una palabra clave (marque Palabras clave )
- contener más de 511 caracteres
- contener el signo @ después de su primer carácter
Palabras clave
Las palabras clave son palabras reservadas predefinidas con un significado sintáctico especial. El lenguaje tiene dos tipos de palabras clave: contextual y reservada. Las palabras clave reservadas como false
o byte
solo se pueden utilizar como palabras clave. Las palabras clave contextuales como where
o from
solo se tratan como palabras clave en determinadas situaciones. [1] Si se necesita un identificador que sería el mismo que una palabra clave reservada, puede tener el prefijo @ para distinguirlo. Esto facilita la reutilización del código .NET escrito en otros lenguajes. [2]
Palabras clave de C #, palabras reservadas | |||
---|---|---|---|
abstract | as | base | bool |
break | by 2 | byte | case |
catch | char | checked | class |
const | continue | decimal | default |
delegate | do | double | descending 2 |
explicit | event | extern | else |
enum | false | finally | fixed |
float | for | foreach | from 2 |
goto | group 2 | if | implicit |
in | int | interface | internal |
into 2 | is | lock | long |
new | null | namespace | object |
operator | out | override | orderby 2 |
params | private | protected | public |
readonly | ref | return | switch |
struct | sbyte | sealed | short |
sizeof | stackalloc | static | string |
select 2 | this | throw | true |
try | typeof | uint | ulong |
unchecked | unsafe | ushort | using |
var 2 | virtual | volatile | void |
while | where 1 [3] 2 | yield 1 | |
1, 2 Estas son palabras clave contextuales; por lo tanto (a diferencia de las palabras clave reales), es posible definir variables y tipos usando estos nombres, pero actúan como palabras clave cuando aparecen en posiciones específicas en el código. Las palabras clave contextuales se introdujeron en C # 2.0, y todas las palabras clave que se introducirán en el futuro del lenguaje serán contextuales. |
Usando una palabra clave como identificador:
string @out ; // @out es un identificador ordinario, distinto de la palabra clave 'out', // que conserva su significado especial
Literales
Enteros | |
---|---|
decimal | 23456, [0..9]+ |
hexadecimal | 0xF5, 0x[0..9, A..F, a..f]+ |
binario | 0b010110001101, 0b[0,1]+ |
Valores de coma flotante | |
flotador | 23.5F, 23.5f; 1.72E3F, 1.72E3f, 1.72e3F, 1.72e3f |
doble | 23.5, 23.5D, 23.5d; 1.72E3, 1.72E3D, ... |
decimal | 79228162514264337593543950335m, -0.0000000000000000000000000001m, ... |
Caracteres | |
carbonizarse | 'a', 'Z', '\u0231' |
Instrumentos de cuerda | |
cuerda | "Hello, world" "C:\\Windows\\" , @"C:\Windows\" [las cadenas textuales (precedidas por @) pueden incluir caracteres de salto de línea y retorno de carro]
|
El personaje se escapa en cadenas | |
Carácter Unicode | \u seguido del punto de código unicode hexadecimal |
Carácter nulo 1 | \0 |
Pestaña | \t |
Retroceso | \b |
Retorno de carro | \r |
Alimentación de formulario | \f |
Barra invertida | \\ |
Una frase | \' |
Cotización doble | \" |
Linea de alimentación | \n |
1 Las cadenas en C # no tienen terminación nula |
Separadores de dígitos
- Esta es una característica de C # 7.0.
El símbolo de subrayado separa los dígitos de los valores numéricos para facilitar la lectura. El compilador ignora estos guiones bajos.
int bin = 0 b1101_0010_1011_0100 ; int hex = 0 x2F_BB_4A_F1 ; int dec = 1 _000_500_954 ; doble real = 1 _500 . 200 _2e - 1 _000 ;
Generalmente, se puede colocar solo entre caracteres de dígitos. No se puede colocar al principio ( _121
) o al final del valor ( 121_
o 121.05_
), al lado del decimal en valores de coma flotante ( 10_.0
), al lado del carácter exponente ( 1.1e_1
) y 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 myInt ; // Declarando una variable no inicializada llamada 'myInt', de tipo 'int'
Asignar
int myInt ; // Declarando una variable no inicializada myInt = 35 ; // Asignar un valor a la variable
Inicializar
int myInt = 35 ; // Declarando e inicializando la variable
Se pueden declarar e inicializar múltiples variables del mismo tipo en una declaración.
int a , b ; // Declarar 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, permitiendo que el especificador de tipo de una declaración de variable sea reemplazado 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 = new char [] { 'A' , 'Ö' }; // o char [] myChars = new char [] {'A', 'Ö'};var myNums = new List < int > (); // o Lista myNums = nueva Lista ();
Ver también
Constantes
Las constantes son valores inmutables.
const
Al declarar una variable local o un campo con la const
palabra clave como prefijo, se debe dar el valor cuando se declara. Después de eso, está bloqueado y no se puede cambiar. Pueden declararse en el contexto como un campo o una variable local. Las constantes son implícitamente estáticas.
const doble PI = 3,14 ;
Esto muestra ambos usos de la palabra clave.
clase Foo { const doble X = 3 ; Foo () { constante int Y = 2 ; } }
readonly
La readonly
palabra clave hace algo similar a los campos. Como los campos marcados como const
no pueden cambiar una vez inicializados. La diferencia es que puede optar por inicializarlos en un constructor o en un valor que no se conoce hasta el tiempo de ejecución. Esto solo funciona en campos. readonly
Los 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 varios contextos.{ ... }
Dentro de los cuerpos de los métodos, puede usar las llaves para crear nuevos ámbitos de la siguiente manera:
void doSomething () { int una ; { int b ; a = 1 ; } a = 2 ; b = 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 se pueden anidar dentro de otras clases.
Main
método
Ya sea una consola o una aplicación de interfaz gráfica, el programa debe tener un punto de entrada de algún tipo. El punto de entrada de la aplicación C # es el Main
método. Solo puede haber uno y es un método estático en una clase. El método generalmente devuelve void
y se pasa 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. static void Main () { }
Un Main
método también puede devolver un valor entero si se especifica.
static int Main ( string [] args ) { return 0 ; }
Async Main
Esta es una característica de C # 7.1.
Las tareas asincrónicas se pueden esperar en el Main
método declarándolo para devolver el tipo Task
.
asincrónica estática Principal de la tarea ( cadena [] argumentos ) { espera DoWorkAsync ( 42 ); }
Se admiten todas las combinaciones de Task
, o Task
y con o sin el string[] args
parámetro.
Declaraciones de nivel superior
Esta es una característica de C # 9.0.
Al igual que en los lenguajes de secuencias de comandos, las declaraciones de nivel superior eliminan la ceremonia de tener que declarar la Program
clase con un Main
mé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 en otros archivos aún tendrá que 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 el sistema ;Consola . WriteLine ( "¡Hola mundo!" );
Los tipos se declaran después de las declaraciones y estarán disponibles automáticamente a partir de las declaraciones anteriores.
Espacios de nombres
Los espacios de nombres son parte de un nombre de tipo y se utilizan para agrupar y / o distinguir entidades con nombre de otras.
Sistema . IO . DirectoryInfo // DirectoryInfo está en el espacio de nombres System.IO
Un espacio de nombres se define así:
espacio de nombres FooNamespace { // Miembros }
using
directiva
La using
directiva carga un espacio de nombres específico de un ensamblado al que se hace referencia. Por lo general, 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 clases.
usando el sistema ; usando System.Collections ;
La directiva también se puede utilizar para definir otro nombre para un espacio de nombres o tipo existente. A veces, esto es útil cuando los nombres son demasiado largos y menos legibles.
usando Net = System . Net ; usando DirInfo = System . IO . DirectoryInfo ;
using static
directiva
La directiva carga los miembros estáticos de un tipo especificado en el ámbito actual, haciéndolos accesibles directamente por el nombre del miembro.using static
usando el sistema estático . Consola ;WriteLine ( "¡Hola, mundo!" );
Operadores
Categoría de operador | Operadores |
---|---|
Aritmética | + , - , * , / ,% |
Lógico (booleano y bit a bit) | & , | , ^ , ! , ~ , && , || , true ,false |
Concatenación de cadenas | + |
Incremento, decremento | ++ , -- |
Cambiar | << , >> |
Relacional (condicional) | == , != , < , > , <= ,>= |
Asignación | = , += , -= , *= , /= , %= , &= , |= , ^= , <<= ,>>= |
Acceso de miembros | . , ?. ,?[] |
Indexación | [] |
Elenco | () |
Condicional (ternario) | ?: |
Concatenación y eliminación de delegados | + , - |
Creación de objetos | new |
Clasificar información | as , is , sizeof ,typeof |
Control de excepción de desbordamiento | checked , unchecked |
Indirección y dirección | * , -> , [] ,& |
Juntarse | ?? |
Expresión lambda | => |
Sobrecarga del operador
Algunos de los operadores existentes pueden sobrecargarse escribiendo un método de sobrecarga.
operador público estático Foo + ( Foo foo , barra de barra ) { devolver nuevo Foo ( foo . Valor + barra . Valor ); }
Estos son los operadores que se pueden descargar:
Operadores | |
---|---|
+ , - , ! , ~ , ++ , -- , true ,false | Operadores unarios |
+ , - , * , / , % , & , | , ^ , << ,>> | Operadores binarios |
== , != , < , > , <= ,>= | Operadores de comparación , deben sobrecargarse en pares |
- Los operadores de asignación ( etc.) son combinaciones de un operador binario y el operador de asignación ( ) y se evaluarán utilizando los operadores ordinarios, que pueden estar sobrecargados.
+=, *=
=
- Los operadores de conversión ( ) no se pueden sobrecargar, pero puede definir operadores de conversión.
( )
- El
[ ]
operador de indexación de matriz ( ) no se puede sobrecargar, pero puede definir nuevos indexadores.
Ver también
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 viva 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 use.( )
Operador de conversión implícito
class Foo { public int Value ; operador implícito estático público Foo ( valor int ) { devolver nuevo Foo ( valor ); } } // Conversión implícita Foo foo = 2 ;
Operador de conversión explícito
class Foo { public int Value ; operador explícito estático público Foo ( valor int ) { devolver nuevo Foo ( valor ); } } // Conversión explícita Foo foo = ( Foo ) 2 ;
as
operador
El as
operador intentará realizar una conversión silenciosa a un tipo determinado. Devolverá el objeto como el nuevo tipo si es posible y, de lo contrario, devolverá un valor nulo.
Stream stream = Archivo . Abrir ( @ "C: \ Temp \ data.dat" ); FileStream fstream = transmitir como FileStream ; // Devolverá un objeto.String str = flujo como String ; // Devolverá nulo.
Operador de fusión nulo
- Esta es una característica de C # 2.0 .
La siguiente:
return ifNotNullValue ?? elseValue ;
es la abreviatura de:
return ifNotNullValue ! = null ? ifNotNullValue : otherwiseValue ;
Lo que significa que si el contenido de la variable ifNotNullValue
no es nulo, ese contenido se devolverá; de lo contrario, se devolverá el contenido de la variable otherwiseValue
.
C # 8.0 introduce la asignación de fusión nula , de modo que
variable ?? = elseValue ;
es equivalente a
if ( variable es nula ) variable = elseValue ;
Estructuras de Control
C # hereda la mayoría de las estructuras de control de C / C ++ y también agrega otras nuevas como la foreach
declaración.
Estructuras condicionales
Estas estructuras controlan el flujo del programa a través de determinadas condiciones.
if
declaración
La if
declaración se ingresa cuando la condición dada es verdadera. Las declaraciones de casos de una sola línea no requieren llaves de bloque, aunque se prefiere principalmente por convención.
Declaración simple de una línea:
si ( i == 3 ) ... ;
Multilínea con else-block (sin llaves):
si ( i == 2 ) ... si no ...
Convenciones de codificación recomendadas para una declaración if.
if ( i == 3 ) { ... } else if ( i == 2 ) { ... } else { ... }
switch
declaración
La switch
construcción sirve como filtro para diferentes valores. Cada valor conduce a un "caso". No se permite pasar por secciones de casos y, por lo tanto, la palabra clave break
se usa normalmente para finalizar un caso. Una return
sección incondicional en un caso también se puede utilizar para finalizar un caso. Vea también cómo goto
se puede usar la declaración para pasar de un caso a otro. Sin embargo, muchos casos pueden llevar al mismo código. El caso predeterminado maneja todos los demás casos no manejados por la construcción.
switch ( 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.
while
círculo
while ( i == verdadero ) { ... }
do ... while
círculo
hacer {} while ( i == verdadero );
for
círculo
El for
ciclo 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 while
declaración, excepto que aquí la i
variable no es local al bucle.
int i = 0 ; while ( i < 10 ) { // ... i ++; }
foreach
círculo
La foreach
declaración se deriva de la for
declaración y hace uso de un cierto patrón descrito en la especificación del lenguaje de C # para obtener y usar un enumerador de elementos para 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 bloqueo, se devolverá el siguiente elemento hasta que no queden elementos.
foreach ( int i en intList ) { ... }
Saltar declaraciones
Las sentencias Jump 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 goto
declaración
A las etiquetas se les dan puntos en el código a los que se puede saltar mediante la goto
instrucción.
inicio : ....... ir a inicio ;
Tenga en cuenta que no es necesario colocar la etiqueta después de la goto
declaración; puede estar antes en el archivo fuente.
La goto
declaración se puede utilizar en switch
declaraciones para saltar de un caso a otro o 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" ); case 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 : // Varias etiquetas para el mismo código son correctas caso 6 : predeterminado : Consola . WriteLine ( "Predeterminado" ); romper ; // Incluso el valor predeterminado no debe llegar al punto final }
break
declaración
La break
declaración sale del bucle o switch
declaración más cercana . La ejecución continúa en la declaración después de la declaración terminada, si la hubiera.
int e = 10 ; for ( int i = 0 ; i < e ; i ++) { while ( true ) { break ; } // Romperá hasta este punto. }
continue
declaración
La continue
declaración interrumpe la iteración actual de la declaración de control actual y comienza la siguiente iteración.
int ch ; while (( ch = Console . Read ()) ! = - 1 ) { if ( ch == '' ) continue ; // Omite el resto del ciclo while // Resto del ciclo while ... }
El while
bucle en el código anterior lee caracteres llamando , 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 que 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.Exception
Exception
Exception
Se puede lanzar una excepción de esta manera:
lanzar nueva NotImplementedException ();
try ... catch ... finally
declaraciones
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 { // Las declaraciones siempre se ejecutan después de los bloques try / catch ... }
Las instrucciones dentro del try
bloque se ejecutan y, si alguna de ellas genera una excepción, la ejecución del bloque se interrumpe y el catch
bloque maneja la excepción . Puede haber varios catch
bloques, en cuyo caso se ejecuta el primer bloque con una variable de excepción cuyo tipo coincide con el tipo de la excepción lanzada.
Si ningún catch
bloque coincide con el tipo de excepción lanzada, la ejecución del bloque externo (o método) que contiene la declaración se interrumpe y la excepción se pasa hacia arriba y 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 actualmente activos. Si la excepción se propaga hasta el método superior sin que se encuentre un bloque coincidente , se termina todo el programa y se escribe una descripción textual de la excepción en el flujo de salida estándar.try ... catch
catch
Main()
catch
Las declaraciones dentro del finally
bloque siempre se ejecutan después de los bloques try
y catch
, independientemente de que se haya lanzado una excepción o no. Estos bloques son útiles para proporcionar código de limpieza.
Un catch
bloque, un finally
bloque o ambos deben seguir al try
bloque.
Tipos
C # es un lenguaje de tipado 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 valor 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 se sale de su 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 struct
palabra clave. Son muy similares a las clases pero son más adecuadas para tipos ligeros. Algunas diferencias sintácticas importantes entre ay class
a struct
se presentan más adelante en este artículo .
struct Foo { ... }
Los tipos de datos primitivos son todos estructuras.
Tipos predefinidos
Estos son los tipos de datos primitivos.
Tipos primitivos | |||||
---|---|---|---|---|---|
Escribe un nombre | Equivalente de BCL | Valor | Distancia | Tamaño | Valor por defecto |
sbyte | System.SByte | entero | −128 hasta +127 | 8 bits (1 byte) | 0 |
short | System.Int16 | entero | −32,768 hasta +32,767 | 16 bits (2 bytes) | 0 |
int | System.Int32 | entero | −2,147,483,648 a +2,147,483,647 | 32 bits (4 bytes) | 0 |
long | System.Int64 | entero | −9,223,372,036,854,775,808 hasta +9,223,372,036,854,775,807 | 64 bits (8 bytes) | 0 |
byte | System.Byte | entero sin signo | 0 a 255 | 8 bits (1 byte) | 0 |
ushort | System.UInt16 | entero sin signo | 0 hasta 65,535 | 16 bits (2 bytes) | 0 |
uint | System.UInt32 | entero sin signo | 0 a 4,294,967,295 | 32 bits (4 bytes) | 0 |
ulong | System.UInt64 | entero sin signo | 0 hasta 18,446,744,073,709,551,615 | 64 bits (8 bytes) | 0 |
decimal | System.Decimal | número decimal firmado | −79,228,162,514,264,337,593,543,950,335 hasta +79,228,162,514,264,337,593,543,950,335 | 128 bits (16 bytes) | 0.0 |
float | System.Single | número de coma flotante | ± 1,401298E − 45 hasta ± 3,402823E + 38 | 32 bits (4 bytes) | 0.0 |
double | System.Double | número de coma flotante | ± 4.94065645841246E − 324 hasta ± 1.79769313486232E + 308 | 64 bits (8 bytes) | 0.0 |
bool | System.Boolean | Booleano | true o false | 8 bits (1 byte) | false |
char | System.Char | carácter Unicode único | '\u0000' mediante '\uFFFF' | 16 bits (2 bytes) | '\u0000' |
Nota: string
( ) no es una estructura y no es un tipo primitivo.System.String
Enumeraciones
Los tipos enumerados ( enums
) son valores con nombre que representan valores enteros.
enum Season { Winter = 0 , Spring = 1 , Summer = 2 , Autumn = 3 , Fall = Autumn // El otoño se llama Otoño en inglés americano. }
enum
las variables se inicializan por defecto a cero. Se pueden asignar o inicializar a los valores nombrados definidos por el tipo de enumeración.
Temporada de temporada ; season = Season . Primavera ;
enum
las variables de tipo son valores enteros. Se permite la suma y resta entre variables del mismo tipo sin un elenco específico, pero la multiplicación y la división son algo más arriesgadas y requieren un elenco explícito. También se requieren conversiones para convertir enum
variables hacia y desde tipos enteros. Sin embargo, la conversión no generará una excepción si el valor no está especificado por la enum
definición de tipo.
temporada = ( Temporada ) 2 ; // convierte 2 en un valor de enumeración de tipo Season. temporada = temporada + 1 ; // Agrega 1 al valor. temporada = temporada + season2 ; // Sumando los valores de dos variables de enumeración. int valor = ( int ) temporada ; // Conversión de enum-value a un valor entero.temporada ++; // Season.Spring (1) se convierte en Season.Summer (2). temporada -; // Season.Summer (2) se convierte en Season.Spring (1).
Los valores se pueden combinar utilizando el operador OR bit a bit .
Color myColors = Color . Verde | Color . Amarillo | Color . azul ;
Ver también
- Enumeración (programación)
Tipos de referencia
Las variables creadas para los tipos de referencia se escriben como referencias administradas. 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 lo recogerá y lo destruirá.
Una variable de referencia es null
cuando no hace referencia a ningún objeto.
Matrices
Un tipo de matriz es un tipo de referencia que se refiere a un espacio que contiene uno o más elementos de cierto tipo. Todos los tipos de matriz se 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 [] 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 [,] números2 = nuevo int [ 3 , 3 ] { { 2 , 3 , 2 }, { 1 , 2 , 6 }, { 2 , 4 , 5 } };
Ver también
- Matriz irregular
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 private
por defecto, pero se pueden declarar como public
visibles fuera de la clase o protected
como visibles para cualquier descendiente de la clase.
String
clase
La clase, o simplemente , representa una secuencia inmutable de caracteres Unicode ( ).System.String
string
char
Las acciones realizadas en una cadena siempre devolverán una nueva cadena.
string text = "¡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
StringBuilder sb = nuevo StringBuilder (); sb . Adjuntar ( 'H' ); sb . Append ( "el" ); sb . AppendLine ( "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. Ver #Interfaces .
Delegados
C # proporciona punteros de función orientados a objetos con seguridad de tipos en forma de delegados .
class Program { // Tipo de delegado : delegate int Operation ( 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 () { // 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 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 expresión lambda.
suma = ( a , b ) => a + b ;
Eventos
Los eventos son indicadores que pueden apuntar a varios métodos. Más exactamente, enlazan punteros de método a un identificador. Por lo tanto, esto puede verse como una extensión para los delegados . Normalmente se utilizan como desencadenantes en el desarrollo de la interfaz de usuario. El formulario utilizado en C # y el resto de Common Language Infrastructure se basa en el del Visual Basic clásico .
delegado void MouseEventHandler ( remitente de objeto , MouseEventArgs e ); Botón de clase pública : Sistema . Windows . Controles . Control { evento MouseEventHandler OnClick ; / * Función de disparo imaginario * / void click () { this . OnClick ( esto , nuevo MouseEventArgs ( datos )); } }
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 en Windows Presentation Foundation y Windows Forms generalmente toma dos parámetros: el remitente y los argumentos del evento . El tipo de evento argumento-objeto se 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 el interior del propietario. Se puede implementar un método de escucha en el exterior para que se active cuando se active el evento.
MainWindow de clase pública : System . Windows . Controles . Ventana { botón privado button1 ; pública MainWindow () { botón 1 = nuevo botón (); botón 1 . Text = "¡Haz clic en mí!" ; / * Suscríbete al evento * / button1 . ClickEvent + = button1_OnClick ; / * Sintaxis alternativa que se considera antigua: button1.MouseClick + = new MouseEventHandler (button1_OnClick); * / } protected void button1_OnClick ( remitente del objeto , MouseEventArgs e ) { MessageBox . Mostrar ( "¡Haga clic!" ); } }
La implementación de eventos personalizados también es posible:
Private EventHandler clickHandles = ( s , e ) => { }; evento público EventHandler Haga clic en { agregar { // Algún código para ejecutar cuando se agregue el controlador ... ...clickHandles + = valor ; } remove { // Algún código para ejecutar cuando se elimina el controlador ... ...clickHandles - = valor ; } }
Ver también
- Programación impulsada por eventos
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 valor sean null
(útiles cuando se trabaja con una base de datos).
¿En t? n = 2 ; n = nulo ;Consola . WriteLine ( n . HasValue );
En realidad, esto es lo mismo que usar la estructura.Nullable<T>
< Int > n = 2 que acepta valores NULL ; n = nulo ;Consola . WriteLine ( n . HasValue );
Punteros
C # tiene y permite punteros a tipos seleccionados (algunas primitivas, enumeraciones, cadenas, punteros e incluso matrices y estructuras si contienen solo tipos que pueden ser señalados [4] ) en un contexto inseguro: métodos y bloques de código marcados unsafe
. Estos son sintácticamente los mismos que los punteros en C y C ++. Sin embargo, la verificación en tiempo de ejecución está deshabilitada dentro de los unsafe
bloques.
static void Main ( string [] args ) { inseguro { int a = 2 ; int * b = & a ; Consola . WriteLine ( "Dirección de a: {0}. Valor: {1}" , ( int ) & 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 } }
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 = ptr -> Byte ;
Ver también
- Puntero (programación)
Dinámica
- Esta es una característica de C # 4.0 y .NET Framework 4.0 .
Type dynamic
es una característica que permite la búsqueda dinámica en tiempo de ejecución en C # de manera 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 el Dynamic Language Runtime (DLR) y ha sido diseñada específicamente con el objetivo de interoperar [ aclaración necesaria ] con lenguajes tipados dinámicamente como IronPython y IronRuby (Implementaciones de Python y Ruby para .NET).
El soporte dinámico también facilita la interoperabilidad [se necesita aclaració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 que genera el compilador. Solo son consumibles y, sin embargo, muy útiles en un escenario como en el que tiene una consulta LINQ que devuelve un objeto select
y 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ónimo 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 { Name = "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. [5] 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. [5] El desempaquetado en C # requiere una conversión de tipo explícita.
Ejemplo:
int foo = 42 ; // Tipo de valor. barra de objeto = foo ; // foo está encajado en la barra. int foo2 = ( int ) bar ; // Desempaquetado de nuevo al tipo de valor.
Programación orientada a objetos (OOP)
C # tiene soporte directo para 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 más distinción sintáctica entre los que están en código.
object
clase
Todos los tipos, incluso los tipos de valor en su forma en caja, heredan implícitamente de la clase, la última clase base de todos los objetos. Esta clase contiene los métodos más comunes compartidos por todos los objetos. Algunos de estos son y pueden anularse.System.Object
virtual
Las clases heredan directa o indirectamente a través de otra clase base.System.Object
Miembros
Algunos de los miembros de la Object
clase:
Equals
- Soporta comparaciones entre objetos.Finalize
- Realiza operaciones de limpieza antes de que un objeto se recupere 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 por humanos que describe una instancia de la clase. Por lo general, devuelve el nombre del tipo.
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 una manera similar a la de la vida real.
Ver también
- Clase (ciencias de la computación)
- Estructura (informática)
Diferencias entre clases y estructuras
Aunque las clases y estructuras son similares tanto en la forma en que se declaran como en cómo se utilizan, existen algunas diferencias significativas. Las clases son tipos de referencia y las estructuras son tipos de valor. 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 referencias.
Las estructuras requieren algo más de trabajo que las clases. Por ejemplo, necesita crear explícitamente un constructor predeterminado que no tome argumentos para inicializar la estructura y sus miembros. El compilador creará uno predeterminado para las clases. Todos los campos y propiedades de una estructura deben haberse inicializado antes de crear una instancia. Las estructuras no tienen finalizadores y no pueden heredar de otra clase como lo hacen las clases. Sin embargo, heredan de , que heredan de . Las estructuras son más adecuadas para construcciones de datos más pequeñas.System.ValueType
System.Object
Este es un breve resumen de las diferencias:
Constructor predeterminado | Finalizador | Inicialización de miembros | Herencia | |
---|---|---|---|---|
Clases | no requerido (generado automáticamente 1 ) | sí | no requerido | sí (si la clase base no lo es sealed ) |
Estructuras | requerido (generado automáticamente 2 ) | No | requerido | No soportado |
1 Generado solo si no se proporcionó ningún constructor 2 Siempre generado automáticamente y no puede ser escrito por el programador |
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 una palabra clave partial
.
// File1.cs clase parcial Foo { ... } // File2.cs clase parcial Foo { ... }
Inicialización
Antes de poder usar los miembros de la clase, debe inicializar la variable con una referencia a un objeto. Para crearlo, llama al constructor apropiado usando la new
palabra clave. Tiene el mismo nombre que la clase.
Foo foo = nuevo Foo ();
Para las estructuras , es opcional llamar explícitamente a un constructor porque el predeterminado se llama automáticamente. Solo necesita declararlo y se inicializa con valores estándar.
Inicializadores de objetos
- Esta es una característica de C # 3.0 .
Proporciona una forma más conveniente de inicializar los campos públicos y las propiedades de un objeto. Las llamadas al constructor son opcionales cuando hay un constructor predeterminado.
Persona persona = nueva Persona { Nombre = "Juan Pérez" , Edad = 39 };// Igual a persona person = new Person (); persona . Nombre = "John Doe" ; persona . Edad = 39 ;
Inicializadores de colección
- Esta es una característica de C # 3.0 .
Los inicializadores de colección proporcionan una sintaxis similar a una matriz para inicializar colecciones. El compilador simplemente generará 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 > (); lista . Agregar ( 2 ); lista . Agregar ( 5 ); lista . Agregar ( 6 ); lista . Agregar ( 6 );
Acceso a 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 la
instancia Se puede acceder a los miembros de la instancia a través del nombre de una variable.
string foo = "Hola" ; string fooUpper = foo . ToUpper ();
Acceso a un miembro de clase
estática Se accede a los miembros estáticos mediante 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) referenciado por un puntero con el ->
operador al igual que 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 declaraciones de tipos y miembros de tipos. En particular, hay un subgrupo que contiene los modificadores de acceso.
Modificadores de clase
abstract
- Especifica que una clase solo sirve como clase base. Debe implementarse en una clase heredada.sealed
: Especifica que una clase no se puede heredar.
Modificadores de miembros de clase
const
- Especifica que una variable es un valor constante que debe inicializarse cuando se declara.event
- Declara un evento.extern
- Especifica que una firma de método sin cuerpo usa una importación de DLL.override
- Especifica que una declaración de método o propiedad es una invalidación 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 inseguro, que permite el uso de punteros.virtual
- Especifica que una clase derivada puede invalidar un método o una declaración de propiedad.volatile
- Especifica un campo que puede ser modificado por un proceso externo y evita que un compilador de optimización modifique el uso del campo.
static
modificador
El static
modificador 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. Los miembros estáticos a veces se denominan miembros de clase, ya que se aplican a la clase como un todo y no a sus instancias.
public class Foo { public static void Something () { ... } } // Llamando al método de la clase. Foo . Algo ();
Modificadores de acceso
Los modificadores de acceso , o modificadores de herencia , establecen la accesibilidad de clases, métodos y otros miembros. Se public
puede acceder a algo marcado desde cualquier lugar. private
solo se puede acceder a los miembros desde dentro de la clase en la que están declarados y se ocultarán cuando se hereden. Los miembros con el protected
modificador serán private
accesibles cuando se hereden. internal
las clases y los miembros solo serán accesibles desde el interior de la asamblea declarante.
Las clases y estructuras son implícitas internal
y los miembros lo son implícitamente private
si no tienen un modificador de acceso.
public class Foo { public int Do () { return 0 ; } Bar de clase pública { } }
Esta tabla define dónde se pueden utilizar los modificadores de acceso.
Tipos no anidados | Miembros (incl. Tipos anidados) | |
---|---|---|
public | sí | sí |
protected internal | No | sí |
protected | No | sí |
internal | sí (predeterminado) | sí |
private protected | No | sí |
private | No | sí (predeterminado) |
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. Pueden tomar parámetros como cualquier otro método.
clase Foo { Foo () { ... } }
Los constructores pueden ser public
, private
, protected
o internal
.
Ver también
- Constructor (informática)
Incinerador de basuras
Se llama al destructor cuando el recolector de basura está recolectando el objeto para realizar una limpieza manual. Hay un método destructor predeterminado llamado finalize
que puede anularse declarando el suyo.
La sintaxis es similar a la de los constructores. La diferencia es que el nombre está precedido por un ~ y no puede contener ningún parámetro. No puede haber más de un destructor.
clase Foo { ... ~ Foo () { ... } }
Los finalizadores siempre lo son private
.
Ver también
- Destructor (informática)
Métodos
Como en C y C ++, hay 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 tanto, una función se denomina método . Un método tiene un valor de retorno, un nombre y, por lo general, algunos parámetros se inicializan cuando se llama con algunos argumentos. Puede pertenecer a una instancia de una clase o ser un miembro estático.
class Foo { int Bar ( int a , int b ) { return a % b ; } }
Un método se llama usando .
notación en 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 . WriteLine ( r );
Ver también
- Método (ciencias de la computación)
ref
y out
parámetros
Se puede hacer explícitamente que los argumentos se pasen por referencia al llamar a un método con parámetros precedidos por palabras clave ref
o out
. Estos punteros administrados son útiles al pasar variables que desea modificar dentro del método por referencia. La principal diferencia entre los dos es que out
se debe haber asignado un parámetro dentro del método en el momento en que el método regresa, mientras que ref no necesita asignar un valor.
nulo PassRef ( ref int x ) { if ( x == 2 ) x = 10 ; } int Z ; PassRef ( ref Z );anular PassOut ( out int x ) { x = 2 ; } int Q ; 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 de forma selectiva cualquier subconjunto dado 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 para los parámetros opcionales y obligatorios, y se pueden usar para mejorar la legibilidad o reordenar arbitrariamente los argumentos en una llamada. Por ejemplo:
Corriente OpenFile ( string nombre , FileMode modo = FileMode . Abrir , FileAccess acceso = FileAccess . Leer ) { ... }OpenFile ( "archivo.txt" ); // usa valores predeterminados para "modo" y "acceso" OpenFile ( "file.txt" , modo : FileMode . Create ); // use el valor predeterminado para "acceso" OpenFile ( "archivo.txt" , acceso : FileAccess . Leer ); // use el valor predeterminado para "modo" OpenFile ( nombre : "archivo.txt" , acceso : FileAccess . Read , modo : FileMode . Create ); // nombra todos los parámetros para mayor legibilidad, // y usa 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 los que son opcionales. Por ejemplo:
objeto fileName = "Test.docx" ; objeto perdido = Sistema . Reflexión . Falta . Valor ;doc . Guardar como ( ref nomArchivo , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante , ref faltante ) ; consola . Writeline ( "Archivo guardado con éxito" );
Con soporte para parámetros opcionales, el código se puede acortar como
doc . SaveAs ( ref nomArchivo );
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 DllImport
atributo también debe agregarse para hacer referencia al archivo DLL deseado.
[DllImport ("win32.dll")] static extern double Pow ( doble a , doble b );
Campos
Los campos, o variables de clase , 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 de campos:
const
- Hace del campo una constante.private
- Hace que el campo sea privado (predeterminado).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
- Hace que el campo sea un miembro estático.
Propiedades
Las propiedades traen una sintaxis similar a un campo y las combinan con el poder de los métodos. Una propiedad puede tener dos accesos: get
y set
.
class Person { nombre de la cadena ; string Name { get { return name ; } establecer { nombre = valor ; } } }// Usando una propiedad Person person = new Person (); persona . Nombre = "Robert" ;
Modificadores de propiedades:
private
- Hace que la propiedad sea privada (predeterminado).protected
- Hace que la propiedad esté protegida.public
- Hace pública la propiedad.static
- Hace que la propiedad sea un miembro estático.
Modificadores para accesores de propiedad:
private
- Hace que el descriptor de acceso sea privado.protected
- Hace que el descriptor de acceso esté protegido.public
- Hace público el descriptor de acceso.
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 los descriptores de acceso sin cuerpos y el compilador generará un campo de respaldo y el código necesario para los descriptores de acceso.
public double Width { get ; conjunto privado ; }
Indexadores
Los indexadores agregan capacidades de indexación similares a matrices a los objetos. Se implementan de forma similar a las propiedades.
class IntList { int [] elementos ; int this [ int index ] { get { return this . artículos [ índice ]; } establecer { esto . elementos [ índice ] = valor ; } } }// Usando un indexador IntList 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 {}
Ver también
- Herencia (informática)
virtual
Los métodos marcados virtual
proporcionan una implementación, pero los herederos pueden anularlos mediante la override
palabra 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 ; } }class NewOperation : Operation { public override int Do () { return 1 ; } }
new
Al sobrecargar un método no virtual con otra firma, new
se puede utilizar la palabra clave . El método utilizado será elegido por el tipo de variable en lugar del tipo real del objeto.
Clase Operación { public int Do () { return 0 ; } }class NewOperation : Operación { public new double Do () { return 4.0 ; } }
Esto demuestra el caso:
Operación NewOperation = new NewOperation ();// Llamará a "double Do ()" en NewOperation double d = operación . Hacer ();Operación operación_ = operación ;// Llamará a "int Do ()" en Operation int i = operation_ . 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 ordinaria.
También puede haber miembros abstractos. Los miembros abstractos son miembros de clases abstractas que no tienen ninguna implementación. Deben ser reemplazados por la clase que hereda el miembro.
clase abstracta Mamífero { paseo vacío abstracto público (); } class Human : Mammal { public override void Walk () { } ... }
sealed
El sealed
modificador se puede combinar con los demás como un modificador opcional para las clases para hacerlas no heredables.
clase sellada interna _FOO { }
Interfaces
Las interfaces son estructuras de datos que contienen definiciones de miembros y no implementación real. Son útiles cuando desea definir un contrato entre miembros de diferentes tipos que tienen diferentes implementaciones. Puede declarar definiciones para 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 { double A { get ; establecer ; } doble B { obtener ; establecer ; } doble GetResult (); }
Implementando una interfaz
Una interfaz es implementada por una clase o extendida por otra interfaz de la misma manera que deriva una clase de otra clase usando la :
notación.
Implementación implícita
Cuando se implementa implícitamente una interfaz, los miembros de la interfaz tienen que serlo public
.
Sumador de clase pública : IBinaryOperation { public double A { get ; establecer ; } público doble B { obtener ; establecer ; } public double GetResult () { return A + B ; } } Multiplicador de clase pública : IBinaryOperation { public double A { get ; establecer ; } público doble B { obtener ; establecer ; } public double GetResult () { return A * B ; } }
En uso:
IBinaryOperation op = nulo ; resultado doble ;// Adder implementa la interfaz IBinaryOperation.op = nuevo Sumador (); op . A = 2 ; op . B = 3 ;resultado = op . GetResult (); // 5// El multiplicador también implementa la interfaz.op = nuevo multiplicador (); op . A = 5 ; op . B = 4 ;resultado = op . GetResult (); // 20
Implementación explícita
También puede implementar miembros explícitamente. Los miembros de la interfaz implementados explícitamente por una clase son accesibles solo cuando el objeto se maneja como el tipo de interfaz.
Sumador de clase pública : IBinaryOperation { double IBinaryOperation . A { obtener ; establecer ; } doble IBinaryOperation . B { obtener ; establecer ; } doble IBinaryOperation . GetResult () { return (( IBinaryOperation ) this ). A + (( IBinaryOperation ) esto ). B ; } }
En uso:
Sumador agregar = nuevo Sumador ();// Estos miembros no son accesibles: // add.A = 2; // agregar.B = 3; // resultado doble = add.GetResult ();// Transmitir al tipo de interfaz para acceder a ellos: IBinaryOperation add2 = add ; add2 . A = 2 ; add2 . B = 3 ; resultado doble = add2 . GetResult ();
Nota: Las propiedades de la clase que se extiende IBinaryOperation
son 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
Las interfaces y clases pueden extender múltiples interfaces.
clase MyClass : IInterfaceA , IInterfaceB { ... }
Aquí hay una interfaz que extiende dos interfaces.
interfaz IIinterfaceC : 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 clase o clase abstracta.
- Una clase o clase abstracta puede implementar una o más interfaces.
- Una interfaz solo puede ampliar otras interfaces.
- Una clase abstracta puede tener propiedades y métodos 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 puede.
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 instancia la clase o el 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 los lanzamientos en tiempo de ejecución o las operaciones de boxeo, como se muestra aquí: [6]
// Declara la clase genérica.public class GenericList < T > { void Add ( T input ) { } }clase TestGenericList { privada clase ExampleClass { } estática vacío Principal () { // Declarar una lista de tipo int. GenericList < int > list1 = new GenericList < int > (); // Declara una lista de tipo cadena. GenericList < cadena > list2 = new GenericList < cadena > (); // Declara una lista de tipo ExampleClass. ListaGenerica < ExampleClass > list3 = nuevo ListaGenerica < ExampleClass > (); } }
En comparación con las plantillas de C ++ , los genéricos de C # pueden proporcionar mayor seguridad, pero también tienen capacidades algo limitadas. [7] Por ejemplo, no es posible llamar a operadores aritméticos en un tipo genérico de C #. [8] A diferencia de las plantillas de C ++, los tipos parametrizados .NET se instancian en tiempo de ejecución en lugar de hacerlo el compilador; por lo tanto, pueden tener varios idiomas, mientras que las plantillas de C ++ no. Admiten algunas funciones que no son compatibles directamente con las plantillas C ++, como las 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 .NET utilizan la reificación para hacer 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 del tipo. [9]
Usar genéricos
Clases genéricas
Las clases y estructuras pueden ser genéricas.
public class List < T > { ... public void Agregar ( elemento T ) { ... } } Lista < int > lista = nueva Lista < int > (); lista . Agregar ( 6 ); lista . Agregar ( 2 );
Interfaces genéricas
interfaz IEnumerable < T > { ... }
Delegados genéricos
delegado 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 . CopyTo ( newArray , 0 ); b . CopyTo ( newArray , un . Longitud ); return newArray ; }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 }; double [] db = new double [] { 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. Pueden asociarse 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 se pueden restringir 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: [10]< >
where
Restricción | Explicación |
---|---|
where T : struct | el parámetro de tipo debe ser un tipo de valor |
where T : class | el parámetro de tipo debe ser un tipo de referencia |
where T : new() | el parámetro de tipo debe tener un constructor sin parámetros (debe aparecer al final) |
where T : <base_class> | el parámetro de tipo debe heredar de <base_class> |
where T : <interface> | El parámetro de tipo debe ser, o debe implementar, esta interfaz. |
where T : U | restricción de parámetro de tipo desnudo |
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 out
y in
, respectivamente. Estas declaraciones luego se respetan para las conversiones de tipos, tanto implícitas como explícitas, y tanto en tiempo de compilación como en tiempo de ejecución. Por ejemplo, la interfaz existente se ha redefinido de la siguiente manera:IEnumerable<T>
interfaz IEnumerable < out 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>
Derived
IEnumerable<Base>
Base
Derived
void PrintAll ( IEnumerable < objeto > objetos ) { foreach ( objeto o en objetos ) { System . Consola . WriteLine ( o ); } }IEnumerable < cadena > cadenas = nueva Lista < cadena > (); PrintAll ( cadenas ); // IEnumerable se convierte implícitamente en IEnumerable
Por contravarianza, la interfaz existente se ha redefinido de la siguiente manera:IComparer<T>
interfaz pública IComparer < en T > { int Compare ( T x , T y ); }
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 desde . Hace posible escribir código como:IComparer<Base>
Base
IComparer<Derived>
Derived
Base
IComparer < objeto > objectComparer = GetComparer (); IComparer < cadena > stringComparer = objectComparer ;
Enumeradores
Un enumerador es un iterador. Los enumeradores se obtienen normalmente llamando al método de un objeto que implementa la interfaz. Las clases de contenedor generalmente implementan esta interfaz. Sin embargo, la instrucción foreach en C # puede operar en cualquier objeto que proporcione dicho método, incluso si no se implementa . Esta interfaz se amplió a una versión genérica en .NET 2.0 .GetEnumerator()
IEnumerable
IEnumerable
A continuación, se muestra un uso simple de iteradores en C # 2.0:
// versión explícita IEnumerator < MyType > iter = list . GetEnumerator (); while ( iter . MoveNext ()) Consola . WriteLine ( iter . Current );// versión implícita foreach ( valor MyType en la lista ) Console . 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 la de Python . [11] Con a , la función mantiene automáticamente su estado durante la iteración.yield return
yield
yield 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 return i ; } }// usando el método para generar solo números pares del arreglo static void Main () { int [] numeros = { 1 , 2 , 3 , 4 , 5 , 6 }; foreach ( int i en GetEven ( números )) Consola . WriteLine ( i ); // 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 presenta carpetas, lo que facilita el acceso a las bases de datos y sus datos.
Sintaxis de la consulta
La sintaxis de consulta LINQ se introdujo en C # 3.0 y le permite escribir consultas similares a SQL en C #.
var list = new List < int > { 2 , 7 , 1 , 3 , 9 };var resultado = de i en la lista donde i > 1 seleccione i ;
Las declaraciones se compilan en llamadas a métodos, por lo que casi solo se especifican los nombres de los métodos. Los métodos que se utilizan en última instancia están determinados por la resolución de sobrecarga normal. Por lo tanto, el resultado final de la traducción se ve afectado por los símbolos incluidos en el alcance.
Lo que difiere de SQL es que la instrucción from viene primero y no al final como en SQL. Esto se debe a que parece más natural escribir como este en C # [ cita requerida ] y es compatible con "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 denominados "expresiones lambda", es una característica que le permite escribir funciones de cierre en línea en su código.
Hay varias formas de crear métodos anónimos. Antes de C # 3.0, existía un soporte limitado mediante el uso de delegados.
Ver también
- Función anónima
- Cierre (informática)
Delegados anónimos
- Esta es una característica de C # 2.0 .
Los delegados anónimos son indicadores de funciones que contienen métodos anónimos. El propósito es simplificar el uso de delegados simplificando el proceso de asignación de la función. 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 = delegate ( 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 otros no se especifican explícitamente.
// [argumentos] => [cuerpo-método]// Con parámetros n => n == 2 ( a , b ) => a + b ( a , b ) => { a ++; devuelve a + b ; }// Con parámetros escritos explícitamente ( int a , int b ) => a + b// Sin parámetros () => return 0// Asignar 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 ++; devuelve 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 list = stringList . 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 solo 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áctica 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
:
public static class StringExtensions { public static string Left ( esta cadena s , int n ) { return s . Subcadena ( 0 , n ); } }cadena s = "foo" ; s . Izquierda ( 3 ); // igual que StringExtensions.Left (s, 3);
Ver también
- Patrón de decorador
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 getter y setter de propiedad. Estas funciones tienen acceso a todas las variables del á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. [12] 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. [13]
En el ejemplo de código siguiente, el método Sum es una función local dentro del método Main. Por lo tanto, solo se puede usar dentro de su método principal Main:
static void Main ( string [] args ) { int Sum ( int x , int y ) { return x + y ; } Consola . WriteLine ( Suma ( 10 , 20 )); Consola . ReadKey (); }
Diverso
Bloques de cierre
C # implementa bloques de cierre mediante la usinginstrucción . La using
declaración acepta una expresión que da como resultado la implementación de un objeto IDisposable
, y el compilador genera código que garantiza la eliminación del objeto cuando se using
sale del alcance de la declaración. La using
declaración es azúcar sintáctica . Hace que el código sea más legible que el bloque equivalente .try ... finally
public void Foo () { using ( var bar = File . Open ( "Foo.txt" )) { // hacer algo de trabajo throw new Exception (); // la barra aún se eliminará correctamente. } }
Sincronización de subprocesos
C # proporciona la lockdeclaración , que es otro ejemplo de azúcar sintáctico beneficioso. Funciona marcando un bloque de código como una sección crítica por exclusión mutua del acceso a un objeto proporcionado. Al igual que la using
declaración, funciona mediante el compilador generando un bloque en su lugar.try ... finally
_writer StreamWriter estático privado ; public void ConcurrentMethod () { lock ( _writer ) { _writer . WriteLine ( "Línea 1" ); _escritor . WriteLine ( "Seguido por 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] public class $ AnonymousType $ 120 { [CompilerGenerated] public string Name { get ; establecer ; } }
.NET Framework viene con atributos predefinidos que se pueden utilizar. Algunos de ellos cumplen una función importante en tiempo de ejecución, mientras que otros son solo para decoración sintáctica en código como CompilerGenerated
. Solo 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
público de clase EdibleAttribute : Atributo { público EdibleAttribute () : la base () { } public EdibleAttribute ( bool isNotPoisonous ) { this . IsPoisonous = ! isNotPoisonous ; } public bool IsPoisonous { get ; establecer ; } }
Mostrando el atributo en uso usando los parámetros opcionales del constructor.
[Edible (true)] public class Peach : Fruit { // Miembros si los hay }
Preprocesador
C # presenta "directivas de preprocesador" [14] (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
, #endif
y #else
.
Directivas como #region
dar pistas a los editores para el plegado de código . El #region
bloque debe terminar con una #endregion
directiva.
public class Foo { #region Constructors public Foo () {} public Foo ( int firstParam ) {} #endregion #region Procedimientos public void IntBar ( int firstParam ) {} public void StrBar ( string firstParam ) {} public void BoolBar ( bool firstParam ) {} #endregion }
Comentarios de código
C # utiliza una barra doble ( //
) 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 con una barra inicial / asterisco ( /*
) y un asterisco final / barra inclinada ( */
).
public class Foo { / * Un comentario de varias líneas * / public static void Bar ( int firstParam ) {} }
Los comentarios no se anidan. Estos son dos comentarios únicos:
// Poder poner /* */ */ */ /* /*
/ * Puede poner / * / * / * pero termina con * /
Los comentarios de una sola línea que comienzan con tres barras se utilizan para la documentación XML. Sin embargo, esto es una convención utilizada por Visual Studio y no forma parte de la definición del lenguaje:
/// /// Esta clase es muy elegante. ///
Sistema de documentación XML
El sistema de documentación de C # es similar al Javadoc de Java , pero se basa 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 { // / Un resumen del método. // / Una descripción del parámetro. // / Comentarios sobre el método. public static void Bar ( int firstParam ) {} }
Los comentarios de documentación de varias líneas, aunque se definieron en la especificación del idioma de la versión 1.0, no se admitieron hasta la versión .NET 1.1. [15] Estos comentarios se designan con una barra inclinada inicial / asterisco / asterisco ( /**
) y un asterisco / barra inclinada final ( */
). [dieciséis]
public class Foo { / ** Un resumen del método. * Una descripción del parámetro. * Comentarios sobre el método. * / public static void Bar ( 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 inclinada / asterisco / asterisco ( ).
Este bloque de código:
/ ** * * Un resumen del método. * /
produce un comentario XML diferente al de este bloque de código: [16]
/ ** * Un resumen del método. * /
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 en un documento XML simple con reglas precisas para la asignación de identificadores de Common Language Infrastructure (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 bien definida.
Sintaxis de async-await
- 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 en paralelo y multiproceso a través de tareas.
C # 5.0 tiene soporte de lenguaje nativo para la 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 () { HttpClient httpClient = new HttpClient (); devuelve httpClient . GetStringAsync ( "www.contoso.com" ). ContinueWith (( tarea ) => { string responseBodyAsText = task . Result ; return XDocument . Parse ( responseBodyAsText ); }); } } var t = SomeAsyncCode . GetContentAsync (). ContinueWith (( tarea ) => { var xmlDocument = tarea . Resultado ; });t . Inicio ();
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 () { HttpClient httpClient = new HttpClient (); string responseBodyAsText = espera httpClient . GetStringAsync ( "www.contoso.com" ); devolver XDocument . Analizar ( responseBodyAsText ); } } var xmlDocument = aguardar SomeAsyncCode . GetContentAsync ();// La tarea se iniciará en 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. Extiende C # con características de 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 ( string ! [] args ) requiere args . Longitud > 0 { foreach ( cadena arg en argumentos ) { } }
!
se utiliza para hacer que un tipo de referencia no acepte nulos, por ejemplo, no se puede establecer el valor ennull
. Esto a diferencia de los tipos que aceptan valores NULL que permiten que los tipos de valor se establezcan comonull
.requires
indica una condición que debe seguirse en el código. En este caso, no se permite que la longitud de los argumentos sea cero o menor.
Tipos que no aceptan valores NULL
Spec # extiende C # con tipos que no aceptan valores NULL que simplemente verifica para que las variables de los tipos que aceptan valores NULL que se establecieron como no aceptables no lo sean null
. Si es, null
se lanzará una excepción.
cuerda ! aporte
En uso:
Prueba pública ( cadena ! entrada ) { ... }
Condiciones previas
Las condiciones previas se comprueban antes de ejecutar un método.
La prueba pública ( int i ) requiere i > 0 ; { esto . i = yo ; }
Postcondiciones
Las poscondiciones son condiciones que se garantiza que son correctas cuando se ejecuta un método.
public void Increment () asegura i > 0 ; { i ++; }
Excepciones marcadas
Spec # agrega excepciones marcadas como las de Java .
public void DoSomething () lanza SomeException ; // SomeException: 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 . [17]
Ver también
- .NET Framework
- C # (lenguaje de programación)
- Mono (software)
- Microsoft Visual C #
Referencias
- ^ Schildt, Herbert, C # 3.0: la referencia completa
- ^ Deitel, Harvey M .; Deitel, Paul J., C # para programadores
- ^ Restricciones en los parámetros de tipo (Guía de programación de C #)
- ^ Tipos de puntero (Guía de programación de C #)
- ^ a b Archer , Parte 2, Capítulo 4: El sistema de tipos
- ^ "Genéricos (Guía de programación de C #)" . Microsoft . Consultado el 7 de agosto de 2011 .
- ^ "Introducción a los genéricos de C #" . Microsoft.
- ^ "Diferencias entre plantillas C ++ y genéricos C #" . Microsoft.
- ^ "Introducción a los genéricos de C #" . Microsoft . Enero de 2005 . Consultado el 18 de junio de 2009 .
- ^ En Microsoft MSDN : restricciones en los parámetros de tipo (Guía de programación de C #)
- ^ "rendimiento" . Referencia del lenguaje C # . Microsoft . Consultado el 26 de abril de 2009 .
- ^ ".NET Framework - Novedades de C # 7.0" . msdn.microsoft.com . Consultado el 8 de abril de 2017 .
- ^ "Reflexiones sobre las funciones locales de C # 7" . Anton Sizikov . 2016-04-15 . Consultado el 8 de abril de 2017 .
- ^ "Directivas del preprocesador de C #" . Referencia del lenguaje C # . Microsoft . Consultado el 18 de junio de 2009 .
- ^ 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 .
- ^ a b "Delimitadores para etiquetas de documentación" . Referencia del programador de C # . Microsoft . 1 de enero de 1970 . Consultado el 18 de junio de 2009 .
- ^ Martin, Robert C. (11 de agosto de 2008), "7 Manejo de errores, uso de excepciones no comprobadas", Código limpio: un manual de artesanía de software ágil , Prentice Hall International, ISBN 978-0132350884
- Archer, Tom (2001). Dentro de C # . Microsoft Press. ISBN 0-7356-1288-9.
- Bart de Smet sobre Especificación #
enlaces externos
- NET Framework microsoft
- Proyecto mono