100% encontró este documento útil (2 votos)
204 vistas459 páginas

C Language Specification 1.2

Cargado por

Edward Sandoval
Derechos de autor
© Attribution Non-Commercial (BY-NC)
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOC, PDF, TXT o lee en línea desde Scribd
100% encontró este documento útil (2 votos)
204 vistas459 páginas

C Language Specification 1.2

Cargado por

Edward Sandoval
Derechos de autor
© Attribution Non-Commercial (BY-NC)
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOC, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 459

1

2
3
4
5
6

7 C #
8 Especificación del lenguaje
9 Versión 1.2

1 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


2 Envíe sus correcciones u otros comentarios a [email protected]
1 Aviso
2 © 1999-2003 Microsoft Corporation. Reservados todos los derechos.
3 Microsoft, Windows, Visual Basic, Visual C# y Visual C++ son marcas registradas o marcas comerciales de Microsoft
4 Corporation en los EE.UU. y/o en otros países o regiones.
5 Los demás productos y nombres de compañías mencionados en esta publicación pueden ser marcas comerciales de sus
6 respectivos propietarios.

3 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


4 Tabla de contenido

1Tabla de contenido

21. Introducción.............................................................................................................................. ......................1


3 1.1 Hola a todos............................................................................................................................... .................2
4 1.2 Estructura del programa................................................................................................................ ..............2
5 1.3 Tipos y variables........................................................................................................................ .................4
6 1.4 Expresiones.......................................................................................................................................... .......8
7 1.5 Instrucciones............................................................................................................................. ................10
8 1.6 Clases y objetos............................................................................................................. ...........................13
9 1.6.1 Miembros.............................................................................................................................. .............14
10 1.6.2 Accesibilidad................................................................................................................................ ......15
11 1.6.3 Clases base.............................................................................................................................. ...........15
12 1.6.4 Campos......................................................................................................................... .....................15
13 1.6.5 Métodos..................................................................................................................... ........................16
14 1.6.5.1 Parámetros................................................................................................................................. ...16
15 1.6.5.2 Cuerpo del método y variables locales...................................................................... ...................18
16 1.6.5.3 Métodos estáticos y de instancia........................................................................ ..........................18
17 1.6.5.4 Métodos virtuales, de reemplazo y abstractos...................................................... ........................19
18 1.6.5.5 Sobrecarga de métodos........................................................................................................... ......22
19 1.6.6 Otros miembros de función.................................................................................................. ..............22
20 1.6.6.1 Constructores.................................................................................................... ...........................24
21 1.6.6.2 Propiedades.................................................................................................................. ................25
22 1.6.6.3 Indizadores...................................................................................................................... .............25
23 1.6.6.4 Eventos..................................................................................................................................... ....26
24 1.6.6.5 Operadores................................................................................................................ ...................27
25 1.6.6.6 Destructores....................................................................................................... ..........................28
26 1.7 Estructuras......................................................................................................................... .......................28
27 1.8 Matrices............................................................................................................................... .....................29
28 1.9 Interfaces................................................................................................................................................... 30
29 1.10 Enumeraciones............................................................................................................................... .........33
30 1.11 Delegados............................................................................................................................................. ...34
31 1.12 Atributos....................................................................................................................... ..........................36
322. Estructura léxica........................................................................................................................ ...................38
33 2.1 Programas............................................................................................................................. ....................38
34 2.2 Gramáticas............................................................................................................................... .................38
35 2.2.1 Notación gramatical......................................................................................................... ..................38
36 2.2.2 Gramática léxica....................................................................................................... .........................39
37 2.2.3 Gramática sintáctica............................................................................................................... ............39
38 2.3 Análisis léxico.................................................................................................................................... .......40
39 2.3.1 Terminadores de línea................................................................................................. .......................40
40 2.3.2 Comentarios........................................................................................................................ ...............41
41 2.3.3 Espacio en blanco.............................................................................................................................. .42
42 2.4 Símbolos................................................................................................................................ ...................42
43 2.4.1 Secuencias de escape de caracteres Unicode............................................................................ ..........43
44 2.4.2 Identifiers.............................................................................................................................. .............43
45 2.4.3 Palabras clave.................................................................................................................................... .45
46 2.4.4 Literales..................................................................................................................... ........................46
47 2.4.4.1 Literales booleanos................................................................................................................... ....46
48 2.4.4.2 Literales enteros....................................................................................................... ....................46
49 2.4.4.3 Literales reales.................................................................................................... .........................47

5Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. iii


6Especificación del lenguaje C#

1 2.4.4.4 Literales de carácter........................................................................................... ..........................48


2 2.4.4.5 Literales de cadena.......................................................................................................... .............49
3 2.4.4.6 El literal null............................................................................................................................... ..51
4 2.4.5 Operadores y signos de puntuación......................................................................................... ...........51
5 2.5 Directivas de preprocesamiento........................................................................................... .....................51
6 2.5.1 Símbolos de compilación condicional...................................................................................... ..........53
7 2.5.2 Expresiones de preprocesamiento............................................................................... .......................53
8 2.5.3 Directivas de declaración................................................................................................ ...................54
9 2.5.4 Directivas de compilación condicional............................................................................................. ..56
10 2.5.5 Directivas de diagnóstico................................................................................................ ...................58
11 2.5.6 Directivas de región...................................................................................................... .....................59
12 2.5.7 Directivas de línea.......................................................................................................................... ....59
133. Conceptos básicos...................................................................................................................................... ....61
14 3.1 Inicio de la aplicación........................................................................................................... ....................61
15 3.2 Finalización de la aplicación........................................................................................................ .............62
16 3.3 Declaraciones..................................................................................................................................... .......62
17 3.4 Miembros........................................................................................................................................ ..........65
18 3.4.1 Miembros de espacio de nombres............................................................................... .......................65
19 3.4.2 Miembros de estructura.............................................................................................................. ........65
20 3.4.3 Miembros de enumeraciones........................................................................................................ ......66
21 3.4.4 Miembros de clase................................................................................................... ..........................66
22 3.4.5 Miembros de interfaz................................................................................................... ......................66
23 3.4.6 Miembros de matriz...................................................................................................... .....................66
24 3.4.7 Miembros de delegados.................................................................................................................... ..66
25 3.5 Acceso a miembros.............................................................................................................. .....................66
26 3.5.1 Accesibilidad declarada................................................................................................................. .....67
27 3.5.2 Dominios de accesibilidad....................................................................................... ..........................67
28 3.5.3 Acceso protegido para miembros de instancia................................................................................ ....71
29 3.5.4 Restricciones de accesibilidad..................................................................................................... .......72
30 3.6 Firmas y sobrecargas............................................................................................................................ .....73
31 3.7 Ámbitos......................................................................................................................... ...........................74
32 3.7.1 Ocultar nombres........................................................................................................................ .........76
33 3.7.1.1 Ocultar mediante anidación................................................................................................ ..........77
34 3.7.1.2 Ocultar mediante herencia.................................................................................................. ..........78
35 3.8 Espacios de nombres y nombres de tipos.................................................................................. ................80
36 3.8.1 Nombres completos.......................................................................................................................... ..81
37 3.9 Administración automática de la memoria............................................................................... .................82
38 3.10 Orden de ejecución..................................................................................................... ............................85
394. Tipos..................................................................................................................................................... ..........86
40 4.1 Tipos de valor........................................................................................................................................... .86
41 4.1.1 Tipo System.ValueType................................................................................................................... ...87
42 4.1.2 Constructores predeterminados......................................................................................... .................87
43 4.1.3 Tipos de estructura......................................................................................................... ....................88
44 4.1.4 Tipos simples............................................................................................................ .........................88
45 4.1.5 Tipos integrales................................................................................................................. .................89
46 4.1.6 Tipos de punto flotante................................................................................................................ .......90
47 4.1.7 Tipo decimal.......................................................................................................... ............................91
48 4.1.8 Tipo bool................................................................................................................................ ............92
49 4.1.9 Tipos de enumeración.............................................................................................. ..........................92

7iv Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


8 Tabla de contenido

1 4.2 Tipos de referencia......................................................................................................................... ...........92


2 4.2.1 Tipos de clase........................................................................................................................... ..........93
3 4.2.2 Tipo object.................................................................................................................. .......................93
4 4.2.3 Tipo string............................................................................................................................. .............94
5 4.2.4 Tipos de interfaz........................................................................................................................... ......94
6 4.2.5 Tipos de matriz.............................................................................................................................. .....94
7 4.2.6 Tipos de delegados............................................................................................................... ..............94
8 4.3 Boxing y Unboxing........................................................................................................................... ........94
9 4.3.1 Conversiones boxing................................................................................................................. .........94
10 4.3.2 Conversiones Unboxing...................................................................................................... ...............96
115. Variables................................................................................................................................ ........................97
12 5.1 Categorías de variables..................................................................................................... ........................97
13 5.1.1 Variables estáticas........................................................................................................ ......................97
14 5.1.2 Variables de instancia............................................................................................................. ............97
15 5.1.2.1 Variables de instancia en clases..................................................................................... ...............98
16 5.1.2.2 Variables de instancia en estructuras................................................................... .........................98
17 5.1.3 Elementos matriciales................................................................................................. .......................98
18 5.1.4 Parámetros de valor....................................................................................................................... .....98
19 5.1.5 Parámetros de referencia.................................................................................................... ................98
20 5.1.6 Parámetros de salida........................................................................................................................ ...99
21 5.1.7 Variables locales............................................................................................................................ .....99
22 5.2 Valores predeterminados...................................................................................................... ...................100
23 5.3 Estado de asignación definitiva...................................................................................................... .........100
24 5.3.1 Variables asignadas inicialmente.......................................................................................... ............101
25 5.3.2 Variables no asignadas inicialmente.................................................................................. ...............101
26 5.3.3 Reglas precisas para determinar asignaciones definitivas...................................... ..........................102
27 5.3.3.1 Reglas generales para instrucciones................................................................... ........................102
28 5.3.3.2 Instrucciones de bloques e instrucciones checked y unchecked...................... ...........................103
29 5.3.3.3 Instrucciones de expresiones.................................................................................... ..................103
30 5.3.3.4 Instrucciones de declaración.................................................................................................... ...103
31 5.3.3.5 Instrucciones If................................................................................................................... ........103
32 5.3.3.6 Instrucciones Switch....................................................................................... ...........................103
33 5.3.3.7 Instrucciones While.......................................................................................................... ..........105
34 5.3.3.8 Instrucciones Do...................................................................................................................... ...105
35 5.3.3.9 Instrucciones For............................................................................................................ ............105
36 5.3.3.10 Instrucciones Break, Continue y Goto........................................................................... ...........105
37 5.3.3.11 Instrucciones Throw.............................................................................................. ...................106
38 5.3.3.12 Instrucciones Return.......................................................................................................... .......106
39 5.3.3.13 Instrucciones Try-catch.................................................................................... ........................106
40 5.3.3.14 Instrucciones Try-finally................................................................................. .........................106
41 5.3.3.15 Instrucciones Try-finally-catch......................................................................................... ........107
42 5.3.3.16 Instrucciones Foreach....................................................................................................... ........108
43 5.3.3.17 Instrucciones Using................................................................................................... ...............108
44 5.3.3.18 Instrucciones Lock........................................................................................... ........................108
45 5.3.3.19 Reglas generales para expresiones simples............................................................................. ..108
46 5.3.3.20 Reglas generales para expresiones con expresiones incrustadas......................................... ......109
47 5.3.3.21 Expresiones de invocación y expresiones de creación de objetos...................................... .......109
48 5.3.3.22 Expresiones de asignación simples...................................................................................... .....109
49 5.3.3.23 Expresiones &&................................................................................................... ....................110
50 5.3.3.24 Expresiones ||................................................................................................................. ...........111

9Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. v


10Especificación del lenguaje C#

1 5.3.3.25 ! Expresiones !................................................................................................................ ..........111


2 5.3.3.26 Expresiones ?:.............................................................................................. ............................112
3 5.4 Referencias de variables.................................................................................................................. ........112
4 5.5 Atomicidad de las referencias de variable......................................................................... ......................113
56. Conversiones...................................................................................................................................... ..........114
6 6.1 Conversiones implícitas................................................................................................... .......................114
7 6.1.1 Conversiones de identidad............................................................................................................. ...114
8 6.1.2 Conversiones numéricas implícitas............................................................................ ......................114
9 6.1.3 Conversiones de enumeración implícitas.............................................................. ...........................115
10 6.1.4 Conversiones de referencia implícitas............................................................................... ...............115
11 6.1.5 Conversiones boxing.......................................................................................................... ..............116
12 6.1.6 Conversiones implícitas de expresión constante........................................................................... ....116
13 6.1.7 Conversiones definidas por el usuario implícitas................................................... ..........................116
14 6.2 Conversiones explícitas.................................................................................................................... .......116
15 6.2.1 Conversiones explícitas numéricas............................................................................................ .......117
16 6.2.2 Conversiones de enumeración explícitas............................................................................... ...........118
17 6.2.3 Conversiones explícitas de referencia................................................................... ...........................119
18 6.2.4 Conversiones Unboxing............................................................................................... ....................119
19 6.2.5 Conversiones explícitas definidas por el usuario................................................................... ...........119
20 6.3 Conversiones estándar........................................................................................................................... ..120
21 6.3.1 Conversiones implícitas estándar................................................................................. ....................120
22 6.3.2 Conversiones explícitas estándar.................................................................................................. ....120
23 6.4 Conversiones definidas por el usuario................................................................................................ .....120
24 6.4.1 Conversiones permitidas definidas por el usuario........................................................ ....................120
25 6.4.2 Evaluación de conversiones definidas por el usuario................................................ .......................120
26 6.4.3 Conversiones explícitas definidas por el usuario....................................................................... .......121
27 6.4.4 Conversiones explícitas definidas por el usuario....................................................................... .......122
287. Expresiones....................................................................................................................................... ...........124
29 7.1 Clasificaciones de expresión................................................................................................... ................124
30 7.1.1 Valores de expresiones................................................................................................. ....................125
31 7.2 Operadores.................................................................................................................................. ............125
32 7.2.1 Prioridad y asociatividad de los operadores............................................................ .........................126
33 7.2.2 Sobrecarga de operadores............................................................................................................. ....127
34 7.2.3 Resolución de sobrecarga de operador unario............................................................... ...................128
35 7.2.4 Resolución de sobrecarga de operador binario................................................................ .................128
36 7.2.5 Operadores candidatos definidos por el usuario............................................................... ................129
37 7.2.6 Promociones numéricas.................................................................................................................... 129
38 7.2.6.1 Promociones numéricas unarias ........................................................................ ........................129
39 7.2.6.2 Promociones numéricas binarias........................................................................... .....................130
40 7.3 Búsqueda de miembros........................................................................................................ ...................130
41 7.3.1 Tipos base.............................................................................................................. ..........................131
42 7.4 Miembros de función.......................................................................................................... ....................132
43 7.4.1 Listas de argumentos............................................................................................................... .........135
44 7.4.2 Resolución de sobrecargas........................................................................................ .......................137
45 7.4.2.1 Miembro de función aplicable.......................................................................................... ..........138
46 7.4.2.2 Mejor miembro de función.............................................................................................. ...........138
47 7.4.2.3 Mejor conversión........................................................................................... ............................139
48 7.4.3 Invocación de miembros de función............................................................................................. ....139
49 7.4.3.1 Invocaciones en instancias de conversión boxing.................................................................... ...141

11vi Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


12 Tabla de contenido

1 7.5 Expresiones primarias............................................................................................................... ..............141


2 7.5.1 Literales.................................................................................................................. .........................142
3 7.5.2 Nombres sencillos.............................................................................................................. ..............142
4 7.5.2.1 Significado invariable en bloques............................................................................................ ...143
5 7.5.3 Expresiones entre paréntesis.................................................................................. ..........................144
6 7.5.4 Acceso a miembros................................................................................................. .........................144
7 7.5.4.1 Nombres simples y nombres de tipos idénticos............................................................ ..............146
8 7.5.5 Expresiones de llamada........................................................................................................... .........147
9 7.5.5.1 Invocaciones de método........................................................................................... ..................147
10 7.5.5.2 Invocaciones de delegados..................................................................................... ....................148
11 7.5.6 Acceso a elementos:......................................................................................................... ................148
12 7.5.6.1 Acceso a matrices.......................................................................................................... .............149
13 7.5.6.2 Acceso al indizador............................................................................................ ........................149
14 7.5.7 Acceso a this.......................................................................................................... ..........................150
15 7.5.8 Acceso a bases................................................................................................................................ ..151
16 7.5.9 Operadores postfijos de incremento y decremento................................................................ ...........151
17 7.5.10 El operador new......................................................................................................... ....................152
18 7.5.10.1 Expresiones de creación de objetos........................................................................ ..................153
19 7.5.10.2 Expresiones de creación de matrices.................................................................... ....................155
20 7.5.10.3 Expresiones de creación de delegados.............................................................................. ........156
21 7.5.11 Operador typeof.................................................................................................. ...........................158
22 7.5.12 Los operadores checked y unchecked................................................................ ............................159
23 7.6 Operadores unarios........................................................................................................ .........................161
24 7.6.1 Operador unario de signo más.................................................................................................. ........162
25 7.6.2 Operador unario de signo menos............................................................................................ ..........162
26 7.6.3 Operador de negación lógica................................................................................................. ...........164
27 7.6.4 Operador de complemento de bit a bit............................................................................................ ..164
28 7.6.5 Operadores prefijos de incremento y decremento.................................................. ..........................164
29 7.6.6 Expresiones de conversión................................................................................................ ...............165
30 7.7 Operadores aritméticos............................................................................................... ............................166
31 7.7.1 Operador de multiplicación.................................................................................................. ............166
32 7.7.2 Operador de división........................................................................................................... .............167
33 7.7.3 Operador de resto............................................................................................................... ..............168
34 7.7.4 Operador de suma.................................................................................................... ........................169
35 7.7.5 Operador de resta......................................................................................................... ....................171
36 7.8 Operadores de desplazamiento....................................................................................................... .........173
37 7.9 Operadores de comprobación de tipos y relacionales...................................................................... ........174
38 7.9.1 Operadores de comparación de enteros............................................................................ ................175
39 7.9.2 Operadores de comparación de punto flotante...................................................... ...........................176
40 7.9.3 Operadores de comparación decimales................................................................... .........................176
41 7.9.4 Operadores de igualdad booleanos....................................................................................... ............177
42 7.9.5 Operadores de comparación de tipo de enumeración................................................ .......................177
43 7.9.6 Operadores de igualdad de tipos de referencia............................................................... ..................177
44 7.9.7 Operadores de igualdad de cadenas............................................................................................ ......179
45 7.9.8 Operadores de igualdad de delegados................................................................... ...........................180
46 7.9.9 Operador Is.......................................................................................................... ............................180
47 7.9.10 Operador As............................................................................................................... ....................181
48 7.10 Operadores lógicos............................................................................................................................ ....181
49 7.10.1 Operadores lógicos enteros.................................................................................. ..........................182
50 7.10.2 Operadores lógicos de enumeración....................................................................................... ........182
51 7.10.3 Operadores lógicos booleanos............................................................................................... .........182

13Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. vii


14Especificación del lenguaje C#

1 7.11 Operadores lógicos condicionales...................................................................................... ...................182


2 7.11.1 Operadores lógicos condicionales booleanos........................................................ .........................183
3 7.11.2 Operadores lógicos condicionales definidos por el usuario........................................... .................183
4 7.12 Operador condicional................................................................................................................ ............184
5 7.13 Operadores de asignación......................................................................................... ............................185
6 7.13.1 Asignación simple........................................................................................................... ...............185
7 7.13.2 Asignación compuesta.............................................................................................................. ......187
8 7.13.3 Asignación de eventos........................................................................................................... .........188
9 7.14 Expresión............................................................................................................................ ..................188
10 7.15 Expresiones constantes......................................................................................................................... .189
11 7.16 Expresiones booleanas....................................................................................................... ...................190
128. Instrucciones............................................................................................................................................ ....191
13 8.1 Puntos finales y alcance....................................................................................................... ...................191
14 8.2 Bloques................................................................................................................................ ...................193
15 8.2.1 Listas de instrucciones............................................................................................. ........................193
16 8.3 Instrucción vacía.................................................................................................................... .................194
17 8.4 Instrucciones con etiqueta......................................................................................................... ..............194
18 8.5 Instrucciones de declaración............................................................................................ .......................195
19 8.5.1 Declaraciones de variables locales................................................................................. ..................195
20 8.5.2 Declaraciones de constantes locales.................................................................................... .............196
21 8.6 Instrucciones de expresiones......................................................................................................... ..........197
22 8.7 Instrucciones de selección............................................................................................................... ........197
23 8.7.1 Instrucción If.......................................................................................................................... ..........197
24 8.7.2 Instrucción Switch.......................................................................................................................... ..198
25 8.8 Instrucciones de iteración................................................................................................................... .....203
26 8.8.1 Instrucción While................................................................................................................ .............203
27 8.8.2 Instrucción Do............................................................................................................................. .....203
28 8.8.3 Instrucción For.................................................................................................................. ...............204
29 8.8.4 Instrucción Foreach................................................................................................................. .........205
30 8.9 Instrucciones Jump............................................................................................................................... ...207
31 8.9.1 Instrucción Break............................................................................................................ .................208
32 8.9.2 Instrucción Statement............................................................................................................... ........209
33 8.9.3 Instrucción Goto........................................................................................................................... ....209
34 8.9.4 Instrucción Return.................................................................................................................... ........210
35 8.9.5 Instrucción Throw........................................................................................................ ....................211
36 8.10 Instrucción Try......................................................................................................................... .............212
37 8.11 Instrucciones Checked y Unchecked......................................................................................... ............216
38 8.12 Instrucción Lock........................................................................................................... ........................216
39 8.13 Instrucción Using................................................................................................................... ...............217
409. Espacios de nombres................................................................................................................... ................220
41 9.1 Unidades de compilación....................................................................................................... .................220
42 9.2 Declaraciones de espacio de nombres................................................................................... ..................220
43 9.3 Directivas Using..................................................................................................................................... .222
44 9.3.1 Directivas Using alias........................................................................................... ...........................222
45 9.3.2 Directivas Using espacio de nombres.......................................................................................... .....225
46 9.4 Miembros de espacio de nombres...................................................................................... .....................227
47 9.5 Declaraciones de tipo................................................................................................................ ..............227
4810. Clases.............................................................................................................................. ...........................229
49 10.1 Declaraciones de clases................................................................................................................. ........229

15viii Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


16 Tabla de contenido

1 10.1.1 Modificadores de clase............................................................................................................ .......229


2 10.1.1.1 Clases abstractas................................................................................................................... ....230
3 10.1.1.2 Clases Sealed........................................................................................................................ ....230
4 10.1.2 Especificación de clase base......................................................................................................... ..230
5 10.1.2.1 Clases base............................................................................................................. ..................231
6 10.1.2.2 Implementaciones de interfaces............................................................................................. ...233
7 10.1.3 Cuerpo de clase............................................................................................................. .................233
8 10.2 Miembros de clase....................................................................................................... .........................233
9 10.2.1 Herencia........................................................................................................................... ..............235
10 10.2.2 Modificador New......................................................................................................... ..................235
11 10.2.3 Modificadores de acceso............................................................................................... .................235
12 10.2.4 Tipos constituyentes.......................................................................................................... .............235
13 10.2.5 Miembros estáticos y de instancia...................................................................................... ............236
14 10.2.6 Tipos anidados................................................................................................... ............................237
15 10.2.6.1 Nombre completo.......................................................................................................... ...........237
16 10.2.6.2 Accesibilidad declarada................................................................................................ ............237
17 10.2.6.3 Ocultar.................................................................................................................................... ..238
18 10.2.6.4 Acceso this....................................................................................................... ........................239
19 10.2.6.5 Acceso a miembros privados y protegidos del tipo contenedor........................... .....................239
20 10.2.7 Nombres de miembro reservados................................................................................. ..................240
21 10.2.7.1 Nombres de miembros reservados para propiedades................................................ ................242
22 10.2.7.2 Nombres de miembros reservados para eventos....................................................... ................242
23 10.2.7.3 Nombres de miembros reservados para indizadores.................................................... .............242
24 10.2.7.4 Nombres de miembros reservados para destructores................................................ ................243
25 10.3 Constantes.................................................................................................................................... .........243
26 10.4 Campos............................................................................................................................. ....................244
27 10.4.1 Campos estáticos y de instancia................................................................................. ....................246
28 10.4.2 Campos de sólo lectura........................................................................................ ..........................246
29 10.4.2.1 Utilizar campos de sólo lectura estáticos para constantes............................................... ..........247
30 10.4.2.2 Versiones de constantes y campos de sólo lectura estáticos.............................................. ........247
31 10.4.3 Campos volatile............................................................................................................................. .248
32 10.4.4 Inicialización de campos.............................................................................................. ..................249
33 10.4.5 Inicializadores de variables........................................................................................ ....................249
34 10.4.5.1 Inicialización de campos estáticos.................................................................................... ........250
35 10.4.5.2 Inicialización de campos de instancia.................................................................................. .....252
36 10.5 Métodos......................................................................................................................... .......................252
37 10.5.1 Parámetros de métodos.................................................................................................................. .254
38 10.5.1.1 Parámetros de valor...................................................................................................... ............255
39 10.5.1.2 Parámetros de referencia................................................................................... .......................256
40 10.5.1.3 Parámetros de salida....................................................................................................... ..........257
41 10.5.1.4 Matrices de parámetros................................................................................ ............................258
42 10.5.2 Métodos estáticos y de instancia............................................................................. .......................261
43 10.5.3 Métodos virtuales............................................................................................................... ............261
44 10.5.4 Métodos de reemplazo.......................................................................................... .........................264
45 10.5.5 Métodos sellados.................................................................................................................... ........265
46 10.5.6 Métodos abstractos.................................................................................................................. .......266
47 10.5.7 Métodos externos......................................................................................................... ..................267
48 10.5.8 Cuerpo del método......................................................................................................... ................268
49 10.5.9 Sobrecarga de métodos................................................................................................................ ...269
50 10.6 Propiedades................................................................................................................................. ..........269
51 10.6.1 Propiedades estáticas y de instancia.............................................................................. .................270

17Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. ix


18Especificación del lenguaje C#

1 10.6.2 Descriptores de acceso................................................................................................ ...................270


2 10.6.3 Descriptores de acceso virtual, sellado, de reemplazo y abstracto............................................ ......275
3 10.7 Eventos....................................................................................................................... ..........................277
4 10.7.1 Eventos como campos........................................................................................................... .........279
5 10.7.2 Descriptores de acceso de evento .................................................................................. ................281
6 10.7.3 Eventos estáticos y de instancia........................................................................... ..........................282
7 10.7.4 Descriptores de acceso virtual, sellado, de reemplazo y abstracto............................................ ......282
8 10.8 Indizadores..................................................................................................................................... .......283
9 10.8.1 Sobrecarga de indizadores...................................................................................................... ........287
10 10.9 Operadores............................................................................................................................... .............287
11 10.9.1 Operadores unarios........................................................................................................................ .288
12 10.9.2 Operadores binarios............................................................................................. ..........................289
13 10.9.3 Operadores de conversión........................................................................................... ...................290
14 10.10 Constructores de instancia............................................................................................................... ....291
15 10.10.1 Inicializadores de constructor................................................................................................ .......292
16 10.10.2 Inicializadores de variables de instancia.................................................................................... ...293
17 10.10.3 Ejecución de constructores................................................................................................ ...........293
18 10.10.4 Constructores predeterminados................................................................................ ....................295
19 10.10.5 Constructores Private............................................................................................. ......................296
20 10.10.6 Parámetros de constructor de instancia opcionales............................................................... ........296
21 10.11 Constructores static........................................................................................................... ..................297
22 10.12 Destructores................................................................................................................... .....................299
2311. Estructuras................................................................................................................................................ .302
24 11.1 Declaraciones de estructuras........................................................................................... ......................302
25 11.1.1 Modificadores de estructuras.................................................................................................... ......302
26 11.1.2 Interfaces Struct.................................................................................................. ...........................303
27 11.1.3 Cuerpo de estructura.................................................................................................................... ...303
28 11.2 Miembros de estructura.............................................................................................................. ...........303
29 11.3 Diferencias entre clase y estructura......................................................................................... ..............303
30 11.3.1 Semánticas de valor................................................................................................................... .....304
31 11.3.2 Herencia....................................................................................................................... ..................305
32 11.3.3 Asignación.................................................................................................................................. ....305
33 11.3.4 Valores predeterminados..................................................................................... ...........................305
34 11.3.5 Boxing y Unboxing....................................................................................................... .................306
35 11.3.6 Significado de This..................................................................................................................... ....306
36 11.3.7 Inicializadores de campo................................................................................................ ................306
37 11.3.8 Constructores.................................................................................................................................. 308
38 11.3.9 Destructores........................................................................................................ ...........................309
39 11.3.10 Constructores Static................................................................................................................... ...309
40 11.4 Ejemplos de estructuras.................................................................................................................... .....309
41 11.4.1 Tipo entero de base de datos..................................................................................................... ......309
42 11.4.2 Tipo booleano de base de datos................................................................................. .....................311
4312. Matrices.............................................................................................................................. .......................314
44 12.1 Tipos de matriz.................................................................................................................................. ....314
45 12.1.1 Tipo System.Array.................................................................................................... .....................315
46 12.2 Creación de matrices................................................................................................................. ............315
47 12.3 Acceso a los elementos de matriz................................................................................................. .........315
48 12.4 Miembros de matriz.......................................................................................................... ....................315
49 12.5 Covarianza de matrices..................................................................................................... ....................315

19x Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


20 Tabla de contenido

1 12.6 Inicializadores de matrices....................................................................................................... .............316


213. Interfaces........................................................................................................................... ........................318
3 13.1 Declaraciones de interfaz.................................................................................................... ..................318
4 13.1.1 Modificadores de interfaz............................................................................................................ ...318
5 13.1.2 Interfaces base.......................................................................................................................... ......319
6 13.1.3 Cuerpo de interfaz.............................................................................................................. ............319
7 13.2 Miembros de interfaz....................................................................................................... .....................319
8 13.2.1 Métodos de interfaz................................................................................................................. .......321
9 13.2.2 Propiedades de interfaz........................................................................................... .......................321
10 13.2.3 Eventos de interfaz............................................................................................................... ..........321
11 13.2.4 Indizadores de interfaz................................................................................................ ...................321
12 13.2.5 Acceso a miembros de interfaz................................................................................................ .......322
13 13.3 Nombres completos de miembros de interfaz........................................................... ............................324
14 13.4 Implementaciones de interfaces..................................................................................... .......................324
15 13.4.1 Implementaciones de miembro de interfaz explícitas............................................................ .........325
16 13.4.2 Asignación de interfaces............................................................................................................... ..328
17 13.4.3 Herencia de implementación de interfaces..................................................................... ................331
18 13.4.4 Reimplementación de interfaces................................................................................................... ..333
19 13.4.5 Interfaces y clases abstractas............................................................................................... ...........334
2014. Enumeraciones...................................................................................................................... ....................336
21 14.1 Declaraciones de enumeración.................................................................................................... ..........336
22 14.2 Modificadores de enumeración......................................................................................... ....................337
23 14.3 Miembros de enumeración....................................................................................................... .............337
24 14.4 Tipo System.Enum................................................................................................................... .............339
25 14.5 Valores y operaciones de enumeración............................................................................................ ......339
2615. Delegados................................................................................................................................. ..................341
27 15.1 Declaraciones de delegados........................................................................................ ..........................341
28 15.2 Creación de instancias de delegados............................................................................. ........................343
29 15.3 Invocación de delegados.................................................................................................. .....................345
3016. Excepciones............................................................................................................................................ ....347
31 16.1 Causas de excepciones.......................................................................................................... ................347
32 16.2 Clase System.Exception............................................................................................................. ...........347
33 16.3 Cómo controlar excepciones............................................................................................... ..................348
34 16.4 Clases de excepción comunes......................................................................................... ......................348
3517. Atributos................................................................................................................................ ....................350
36 17.1 Clases de atributo...................................................................................................................... ............350
37 17.1.1 Uso de los atributos.............................................................................................................. ..........350
38 17.1.2 Parámetros posicionales y con nombre........................................................................................... 351
39 17.1.3 Tipos de parámetros de atributos......................................................................................... ...........353
40 17.2 Especificación de atributos................................................................................................................... .353
41 17.3 Instancias de atributo................................................................................................. ...........................360
42 17.3.1 Compilación de un atributo............................................................................................ ................360
43 17.3.2 Recuperación en tiempo de ejecución de una instancia de atributo................................ ................360
44 17.4 Atributos reservados......................................................................................................................... .....361
45 17.4.1 Atributo AttributeUsage.......................................................................................... .......................362
46 17.4.2 Atributo Conditional...................................................................................................................... .362
47 17.4.2.1 Métodos condicionales.............................................................................................. ...............363

21Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. xi


22Especificación del lenguaje C#

1 17.4.2.2 Clases de atributo condicional....................................................................................... ...........365


2 17.4.3 Atributo Obsolete......................................................................................................... ..................367
3 17.5 Atributos para interoperabilidad................................................................................................. ...........368
4 17.5.1 Interoperabilidad con componentes COM y Win32............................................. ..........................368
5 17.5.2 Interoperabilidad con otros lenguajes .NET............................................................... ....................368
6 17.5.2.1 Atributo IndexerName................................................................................................... ...........368
718. Código no seguro......................................................................................................................... ..............369
8 18.1 Contextos no seguros....................................................................................................... .....................369
9 18.2 Tipos de punteros................................................................................................................ ..................372
10 18.3 Variables fijas y móviles.............................................................................................. .........................374
11 18.4 Conversiones de puntero......................................................................................................... ..............375
12 18.5 Punteros en expresiones....................................................................................................... .................377
13 18.5.1 Direccionamiento indirecto de punteros.............................................................................. ...........377
14 18.5.2 Acceso a miembros de puntero................................................................................................ .......378
15 18.5.3 Acceso a elementos de puntero...................................................................................................... .379
16 18.5.4 Operador de dirección.................................................................................................... ................380
17 18.5.5 Incremento y decremento de punteros................................................................................... .........380
18 18.5.6 Aritmética con punteros....................................................................................... ..........................381
19 18.5.7 Comparación de punteros...................................................................................................... .........383
20 18.5.8 Operador Sizeof...................................................................................................... .......................383
21 18.6 Instrucción fixed........................................................................................................... ........................384
22 18.7 Asignación de pila........................................................................................................................ .........388
23 18.8 Asignación dinámica de memoria................................................................................ .........................389
24A. Comentarios de documentación............................................................................................ ....................392
25 A.1 Introducción.......................................................................................................................... .................392
26 A.2 Etiquetas recomendadas........................................................................................................ .................393
27 A.2.1 <c>................................................................................................................................. .................394
28 A.2.2 <code>.................................................................................................................. ..........................394
29 A.2.3 <example>...................................................................................................................................... .395
30 A.2.4 <exception>................................................................................................................................... ..395
31 A.2.5 <include>.................................................................................................................. ......................396
32 A.2.6 <list>................................................................................................................................ ...............397
33 A.2.7 <para>............................................................................................................................. ................398
34 A.2.8 <param>........................................................................................................................... ...............399
35 A.2.9 <paramref>................................................................................................................................. .....399
36 A.2.10 <permission>............................................................................................................................. ....400
37 A.2.11 <summary>.......................................................................................................... .........................400
38 A.2.12 <returns>.......................................................................................................................... .............400
39 A.2.13 <see>................................................................................................................................... ..........402
40 A.2.14 <seealso>...................................................................................................................................... .402
41 A.2.15 <summary>.............................................................................................................. .....................403
42 A.2.16 <value>................................................................................................................ .........................403
43 A.3 Procesar el archivo de documentación.......................................................................... .........................403
44 A.3.1 Formato de cadena de Id...................................................................................... ...........................404
45 A.3.2 Ejemplos de cadena de Id.................................................................................................... ............404
46 A.4 Un ejemplo....................................................................................................................................... ......410
47 A.4.1 Código fuente C#.................................................................................................... ........................410
48 A.4.2 XML resultante..................................................................................................... ..........................413
49B. Gramática........................................................................................................................... ........................416

23xii Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


24 Tabla de contenido

1 B.1 Gramática léxica................................................................................................................................ .....416


2 B.1.1 Terminadores de línea................................................................................................................ ......416
3 B.1.2 Espacio en blanco................................................................................................................ ............416
4 B.1.3 Comentarios.......................................................................................................... ..........................416
5 B.1.4 Símbolos...................................................................................................................................... ....417
6 B.1.5 Secuencias de escape de caracteres Unicode............................................................... ....................417
7 B.1.6 Identifiers................................................................................................................ ........................418
8 B.1.7 Palabras clave...................................................................................................................... ............419
9 B.1.8 Literales.................................................................................................................................... .......419
10 B.1.9 Operadores y signos de puntuación........................................................................... ......................421
11 B.1.10 Directivas de preprocesamiento............................................................................................. ........421
12 B.2 Gramática sintáctica........................................................................................................... ....................423
13 B.2.1 Conceptos básicos................................................................................................... ........................423
14 B.2.2 Tipos......................................................................................................................................... .......423
15 B.2.3 Variables.............................................................................................................................. ............425
16 B.2.4 Expresiones............................................................................................................... ......................425
17 B.2.5 Instrucciones................................................................................................................................... .428
18 B.2.6 Espacios de nombres................................................................................................ .......................431
19 B.2.7 Clases......................................................................................................................... .....................432
20 B.2.8 Estructuras.............................................................................................................................. .........438
21 B.2.9 Matrices.................................................................................................................................... .......438
22 B.2.10 Interfaces........................................................................................................................ ...............439
23 B.2.11 Enumeraciones................................................................................................... ...........................440
24 B.2.12 Delegados......................................................................................................................... .............440
25 B.2.13 Atributos............................................................................................................................. ...........441
26 B.3 Extensiones de la gramática para el código no seguro................................................................... .........442
27C. Referencias............................................................................................................................................. .....445

25Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. xiii


26 Capítulo 18 Código no seguro

1 1.Introducción

2C# (“C sharp”) es un lenguaje de programación sencillo y moderno, orientado a objetos y con seguridad de
3tipos. C# tiene su raíz en la familia de lenguajes C y resultará inmediatamente familiar a programadores de C,
4C++ y Java. ECMA International estandariza C# como el estándar ECMA-334, e ISO/CEI como el estándar
5ISO/CEI 23270. El compilador de C# de Microsoft para .NET Framework es una implementación compatible
6de ambos estándares.
7C# es un lenguaje de programación orientado a objetos, pero también es compatible con la programación
8orientada a componentes. El diseño de software contemporáneo se basa cada vez más en componentes de
9software en forma de paquetes de funcionalidad autodescriptivos y autosuficientes. La clave de dichos
10componentes reside en que presentan un modelo de programación con propiedades, métodos y eventos, en que
11tienen atributos que facilitan información declarativa sobre el componente y en que incorporan su propia
12documentación. C# proporciona construcciones de lenguaje para que se admitan directamente estos conceptos,
13haciendo de C# un lenguaje muy natural en el que crear y utilizar los componentes de software.
14Algunas de las características de C# ayudan en la construcción de aplicaciones sólidas y duraderas: la
15recolección de elementos no utilizados reclama automáticamente la memoria ocupada por objetos no utilizados;
16el control de excepciones proporciona un enfoque extensible y estructurado de la detección y recuperación de
17errores; y el diseño con seguridad de tipos del lenguaje hace que sea imposible tener variables no inicializadas,
18indizar las matrices más allá de sus límites o realizar conversiones de tipo no comprobado.
19C# tiene un sistema de tipos unificado. Todos los tipos de C#, incluidos tipos primitivos como i n ty doub le,
20heredan de un tipo raíz object único. Por lo tanto, todos los tipos comparten un conjunto de operaciones
21comunes, y se pueden almacenar, transportar y tratar valores de cualquier tipo de una manera coherente.
22Además, C# admite tipos de referencia definidos por el usuario y tipos de valor, y permite la asignación
23dinámica de objetos así como el almacenamiento en línea de estructuras ligeras.
24Para asegurarse de que los programas y bibliotecas de C# evolucionan con el tiempo de forma compatible, se ha
25dado mucha importancia al control de versiones en el diseño de C#. Muchos lenguajes de programación apenas
26prestan atención a este problema y, en consecuencia, los programas escritos en estos lenguajes se interrumpen
27más de lo necesario cuando se incluyen versiones más recientes de bibliotecas dependientes. Los aspectos del
28diseño de C# con influencia directa de las consideraciones sobre el control de versiones incluyen los
29modificadores virtual y override, las reglas de la resolución de sobrecarga de métodos y la compatibilidad con
30declaraciones explícitas de miembros de interfaz.
31En el resto de este capítulo se explican las características esenciales del lenguaje C#. Mientras que en capítulos
32posteriores se describen las reglas y las excepciones de una forma muy detallada e incluso a veces matemática,
33este capítulo se ha redactado dando prioridad a la claridad y la brevedad, a veces incluso a expensas de la
34integridad. El propósito es proporcionar al lector una introducción al lenguaje que pueda facilitarle la
35programación de sus primeros programas y la lectura de posteriores capítulos.

27Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 1


28Especificación del lenguaje C#

11.1 Hola a todos


2El programa Hola a todos (“Hello, World”) se utiliza tradicionalmente para presentar un lenguaje de
3programación. Aquí está en C#:
4 using System;
5 class Hello
6 {
7 static void Main() {
8 Console.WriteLine("Hello, World");
9 }
10 }
11Normalmente, la extensión de los archivos de código fuente de C# es . c s. Suponiendo que “Hello, World” se
12almacena en el archivo he l l o . c, sel programa se puede compilar con el compilador de Microsoft C#, utilizando
13la línea de comandos
14 csc he l l o . c s
15que genera un ensamblado ejecutable denominado he l l o . exe
. El resultado de la ejecución de esta aplicación es
16 He l l o , Wor ld
17El programa “Hello, World” se inicia con una directiva us ingque hace referencia al espacio de nombres
18Sys tem. Los espacios de nombres proporcionan una jerarquía que permite organizar programas y bibliotecas de
19C#. Los espacios de nombres contienen tipos y otros espacios de nombres, por ejemplo, el espacio de nombres
20System contiene varios tipos, como la clase Console, a la que se hace referencia en el programa, y otros
21espacios de nombres, como IO y Collections. Una directiva using que hace referencia a un espacio de nombres
22determinado permite el uso no calificado de los tipos que son miembros del espacio de nombres. Debido a la
23directiva using, el programa puede utilizar Console.WriteLine como forma abreviada para
24System.Console.WriteLine.
25La clase Hello declarada por el programa “Hello, World” tiene un único miembro, el método denominado Main.
26El método Main se declara con el modificador static. A diferencia de los métodos de instancia, que hacen
27referencia a una instancia de objeto determinada utilizando la palabra clave this, los métodos estáticos
28funcionan sin referencia a un objeto determinado. Por convención, un método estático denominado Main sirve
29como punto de entrada de un programa.
30El método WriteLine genera el resultado del programa en la clase Console del espacio de nombres System.
31Esta clase es proporcionada por las bibliotecas de clases de .NET Framework, a las que de forma
32predeterminada hace referencia automáticamente el compilador de Microsoft C#. Observe que el propio C# no
33tiene una biblioteca en tiempo de ejecución independiente. En su lugar, .NET Framework es la biblioteca en
34tiempo de ejecución de C#.

351.2 Estructura del programa


36Los conceptos clave de organización en C# son programas, espacios de nombres, tipos, miembros y
37ensamblados. Los programas de C# constan de uno o varios archivos de código fuente. Los programas declaran
38tipos, que contienen los miembros y pueden organizarse en los espacios de nombres. Las clases y las interfaces
39son ejemplos de tipos. Los campos, métodos, propiedades y eventos son ejemplos de miembros. Cuando se
40compilan programas de C#, se empaquetan físicamente en ensamblados. Normalmente la extensión de los
41ensamblados es .exe o .dll, dependiendo de si implementan aplicaciones o bibliotecas.

292 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


30 Capítulo 18 Código no seguro

1En el ejemplo

2 using System;
3 namespace Acme.Collections

4 {

5 public class Stack

68 public void Push(object data) {


79 top = new Entry(top, data);

10
11 public object Pop() {

12 if (top == null) throw new InvalidOperationException();

13 object result = top.data;

14 top = top.next;
17
15 class Entry

18
16 {

19 public Entry next;


21 public Entry(Entry next, object data) {
20
22 this.next = next;

23 this.data = data;

24 }

25declara una clase}denominada Stack en un espacio de nombres llamado Acme.Co l l ec t i ons


28 . El nombre
29completo de esta clase es Acme.Collections.Stack. La clase contiene varios miembros: un campo denominado
26
30top, dos métodos denominados Push y Pop, y una clase anidada denominada Entry. La clase Entry contiene
31tres miembros más: un campo denominado next, un campo denominado data y un constructor. Suponiendo que
27
32el código fuente del ejemplo se almacena en el archivo acme.cs, la línea de comandos

33 csc /t:library acme.cs


34compila el ejemplo como una biblioteca (código sin un punto de entrada Main) y genera un ensamblado
35denominado acme.dll.
36Los ensamblados contienen código ejecutable en forma de instrucciones de Lenguaje intermedio (IL) e
37información simbólica en forma de metadatos. Antes de ejecutarse, el código IL de un ensamblado se convierte
38automáticamente en código específico del procesador mediante el compilador Just-In-Time (JIT) de .NET
39Common Language Runtime.
40Dado que un ensamblado es una unidad autodescriptiva de funcionalidad que contiene código y metadatos, las
41directivas #include y los archivos de encabezado no son necesarios en C#. Los tipos y miembros públicos
42contenidos en un ensamblado determinado estarán disponibles en un programa de C# haciendo referencia a
43dicho ensamblado al compilar el programa. Por ejemplo, este programa utiliza la clase
44Acme.Collections.Stack desde el ensamblado acme.dll:

45 using System;

46 using Acme.Collections;

31Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 3


32Especificación del lenguaje C#

1 class Test

2 {

3 static void Main() {

4 Stack s = new Stack();

5 s.Push(1);

6 s.Push(10);

7 s.Push(100);
138Si el programa se almacena en el archivo tes t . c,scuando se compila tes t . c sse puede hacer referencia al
14ensamblado acme.dll utilizando la opción del compilador /r:
9
15
10 csc /r:acme.dll test.cs
16De este modo se crea un ensamblado ejecutable denominado test.exe que, cuando se ejecuta, genera el
17
11resultado:
12
18 100

19 10
21C# permite el almacenamiento del texto de origen de un programa en varios archivos de código fuente. Cuando
20
22se compila un programa de C# de varios archivos, se procesan todos los archivos de código fuente juntos, y
23éstos pueden hacer referencia libremente unos a otros. Conceptualmente, es como si todos los archivos de
24código fuente se concatenaran en un gran archivo antes de procesarse. En C# nunca son necesarias las
25declaraciones adelantadas porque, salvo alguna excepción, el orden de la declaración no es significativo. C# no
26limita un archivo de código fuente a declarar un único tipo público ni exige que el nombre del archivo de código
27fuente coincida con un tipo declarado en el archivo de código fuente.

281.3 Tipos y variables


29Hay dos clases de tipos en C#: tipos de valor y tipos de referencia. Las variables de tipos de valor contienen
30directamente sus datos mientras que las variables de tipos de referencia almacenan las referencias a sus datos,
31que se conocen como objetos. En el caso de los tipos de referencia, es posible que dos variables hagan referencia
32al mismo objeto y, por tanto, que las operaciones en una variable afecten al objeto al que hace referencia la otra
33variable. En el caso de los tipos de valor, cada variable tiene su propia copia de los datos, de manera que no es
34posible que las operaciones de una afecten a la otra (exceptuando las variables de los parámetros ref y out).
35Los tipos de valor de C# se dividen a su vez en tipos simples, tipos de enumeración y tipos de estructura, y los
36tipos de referencia de C# en tipos de clase, tipos de interfaz, tipos de matriz y tipos de delegado.
37La tabla siguiente proporciona información general sobre el sistema de tipos de C#.
38

334 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


34 Capítulo 18 Código no seguro

Categoría Descripción
Tipos de Tipos simples Integrales con signo: sby te, shor t, int, long
valor
Integrales sin signo: byte, ushort, uint, ulong
Caracteres Unicode: char
Punto flotante IEEE: float, double
Decimal de gran precisión: decimal
Booleano: bool
Tipos de User-defined types of the form enum E { . . . }
enumeración
Tipos de User-defined types of the form s t ruc t S { . . . }
estructura
Tipos de Tipos de clase Clase base última de los demás tipos: object
referencia
Cadenas de Unicode: string
Tipos definidos por el usuario en forma classC{...}
Tipos de interfaz User-defined types of the form i n te r face I { . . . }
Tipos de matriz Unidimensional y multidimensional, por ejemplo, int[] e int[,]
Tipos de User-defined types of the form de legate T D( . . . )
delegados
39
40Los ocho tipos integrales admiten los valores de 8 bits, 16 bits, 32 bits y 64 bits, con y sin signo.
41Los dos tipos de punto flotante float y double se representan utilizando los formatos IEEE 754 de 32 bits de
42precisión simple y de 64 bits de doble precisión.
43El tipo decimal es un tipo de datos de 128 bits apto para cálculos financieros y monetarios.
44El tipo boo l de C# permite representar valores booleanos, que son t rueo false.
45El procesamiento de caracteres y cadenas en C# utiliza la codificación Unicode. El tipo char representa una
46unidad de código Unicode de 16 bits y el tipo string representa una secuencia de unidades de código Unicode
47de 16 bits.
48La tabla siguiente resume los tipos numéricos de C#.
49

35Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 5


36Especificación del lenguaje C#

Categoría Bits Tipo Intervalo/precisión


Integral con 8 sbyte –128...127
signo
16 short –32,768...32,767
32 int –2,147,483,648...2,147,483,647
64 long –9,223,372,036,854,775,808...9,223,372,036,854,775,807
Integral sin 8 byte 0...255
signo
16 ushort 0...65,535
32 uint 0...4,294,967,295
64 ulong 0...18,446,744,073,709,551,615
Punto 32 float 1,5 × 10.45 a 3,4 × 1038, con precisión de 7 dígitos
flotante
64 double 5,0 × 10−324 a 1,7 × 10308, con precisión de 15 dígitos
Decimal 128 decimal 1,0 × 10−28 a 7,9 × 1028, con precisión de 28 dígitos
50
51Los programas de C# utilizan declaraciones de tipo para crear nuevos tipos. Una declaración de tipo especifica
52el nombre y los miembros del nuevo tipo. Cinco de las categorías de tipos de C# son definibles por el usuario:
53tipos de clase, tipos de estructura, tipos de interfaz, tipos de enumeración y tipos de delegado.
54Un tipo de clase define una estructura de datos que contiene miembros de datos (campos) y miembros de
55función (métodos, propiedades y otros). Los tipos de clase admiten la herencia y el polimorfismo, mecanismos
56mediante los que las clases derivadas pueden ampliar y especializar clases base.
57Un tipo de estructura es similar a un tipo de clase en cuanto a que ambas representan una estructura con
58miembros de datos y miembros de función. No obstante, a diferencia de las clases, las estructuras son tipos de
59valores y no requieren asignación del montón. Los tipos de estructura no admiten la herencia especificada por el
60usuario y heredan implícitamente del tipo ob jec t.
61Un tipo de interfaz define un contrato como un conjunto con nombre de miembros de función. Una clase o
62estructura que implementa una interfaz debe proporcionar implementaciones de los miembros de función de la
63interfaz. Una interfaz puede derivarse de varias interfaces base, y una clase o estructura puede implementar
64varias interfaces.
65Un tipo de enumeración es un tipo distinto con constantes con nombre. Todos los tipos de enumeración tienen
66un tipo subyacente, que debe ser uno de los ocho tipos integrales. El conjunto de valores de un tipo de
67enumeración es el mismo que el del conjunto de valores del tipo subyacente.
68Un tipo de valor delegado representa las referencias a los métodos con una lista de parámetros y un valor
69devuelto determinados. Los delegados permiten tratar métodos como entidades, que se pueden asignar a las
70variables y pasarse como parámetros. Los delegados son similares al concepto de punteros a función de otros
71lenguajes, pero al contrario de los punteros a función, los delegados están orientados a objetos y proporcionan
72seguridad de tipos.
73C# admite matrices unidimensionales y multidimensionales de cualquier tipo. Al contrario que otros tipos, los
74tipos de matriz no tienen que ser declarados antes de poder utilizarse. En su lugar, los tipos de matriz se crean
75mediante un nombre de tipo seguido de corchetes. Por ejemplo, i n t [ es] una matriz unidimensional de i n t, int[,]
76es una matriz bidimensional de int e int[][] es una matriz unidimensional de matrices unidimensionales de int.
77El sistema de tipos de C# está unificado, de manera que un valor de cualquier tipo pueda tratarse como un
78objeto. Todos los tipos de C# se derivan directa o indirectamente del tipo de clase object, que es la clase base

376 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


38 Capítulo 18 Código no seguro

1definitiva de todos los tipos. Los valores de los tipos de referencia se tratan como objetos considerándolos
2sencillamente como del tipo ob jec t. Los valores de los tipos de valor se tratan como objetos mediante la
3realización de operaciones de conversión boxing y unboxing. En el ejemplo siguiente, se convierte un valor i n t
4en object y de nuevo en int.

5 using System;
6 class Test

7 {

8 static void Main() {

9 int i = 123;

10 object o = i; // Boxing
14Cuando un valor de un tipo de valor se convierte en el tipo object, se asigna una instancia de objeto, también
11denominada “box”, para que contenga el valor, y éste se copia en dicha instancia. A la inversa, cuando se
15
16convierte una referencia object en un tipo de valor, se comprueba que el objeto al que se hace referencia es un
12
17cuadro del tipo de valor correcto, y si la comprobación se lleva a cabo con éxito, el valor del cuadro se copia
13
18externamente.
19El sistema de tipos unificado de C# significa en la práctica que los tipos de valor pueden convertirse en objetos
20“a petición”. Debido a la unificación, las bibliotecas de uso general que utilizan el tipo object, como las clases
21de colección en .NET Framework, pueden utilizarse tanto con los tipos de referencia como con los tipos de
22valor.
23Hay varios tipos de variables en C#, entre los que se incluyen campos, elementos de matriz, variables locales y
24parámetros. Las variables representan ubicaciones de almacenamiento, y cada variable tiene un tipo que
25determina qué valores se pueden almacenar en ella, como se muestra en la tabla siguiente.
26

Tipo de variable Posible contenido


Tipo de valor Un valor de ese tipo exacto
object Una referencia nula, una referencia a un objeto de cualquier tipo de
referencia o una referencia a un valor de tipo box de cualquier tipo de
valor
Tipo de clase Una referencia nula, una referencia a una instancia de ese tipo de clase o
una referencia a una instancia de una clase derivada de dicho tipo de
clase
Tipo de interfaz Una referencia nula, una referencia a una instancia de un tipo de clase
que implemente dicho tipo de interfaz o una referencia a un valor de tipo
box que implemente dicho tipo de interfaz
Tipo de matriz Una referencia nula, una referencia a una instancia de dicho tipo de
matriz o una referencia a una instancia de un tipo de matriz compatible
Tipo delegado Una referencia nula o una referencia a una instancia de dicho tipo
delegado
27

39Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 7


40Especificación del lenguaje C#

11.4 Expresiones
2Las expresiones se construyen a partir de operandos y operadores. Los operadores de una expresión indican
3qué operaciones se aplican a los operandos. Entre los ejemplos de operadores se incluyen +, -, *, / y new. Son
4ejemplos de operandos los literales, campos, variables locales y expresiones.
5Cuando una expresión contiene varios operadores, la prioridad de los operadores controla el orden de
6evaluación de los operadores individuales. Por ejemplo, la expresión x + y * z se evalúa como x + (y * z)
7porque el operador * tiene prioridad sobre +.
8La mayoría de los operadores se pueden sobrecargar. La sobrecarga de operadores permite utilizar
9implementaciones de operadores definidas por el usuario en operaciones en las que al menos uno de los
10operandos es de un tipo estructura o clase definido por el usuario.
11En la tabla siguiente se resumen los operadores de C#, enumerando las categorías en orden de prioridad de
12mayor a menor: Los operadores de la misma categoría tienen la misma prioridad.
13

418 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


42 Capítulo 18 Código no seguro

Categoría Expresión Descripción


Principal x.m Acceso a miembros
x(...) Invocación de métodos y delegados
x[...] Matriz y acceso del indizador
x++ Incremento posterior
x-- Decremento posterior
new T(...) Creación de objetos y de delegados
new T[...] Creación de matrices
typeof(T) Obtiene el objeto Sys tem. Typepara T
checked(x) Evalúa la expresión en contexto comprobado
unchecked(x) Evalúa la expresión en contexto no comprobado
Unario +x Identidad
-x Negación
!x Negación lógica
~x Negación bit a bit
++x Incremento previo
--x Decremento previo
(T)x Convertir explícitamente x en el tipo T
Multiplicativo x*y Multiplicación
x/y División
x%y Resto
Sumatorio x+y Suma, concatenación de cadenas, combinación de
delegados
x–y Resta, eliminación de delegados
Desplazamiento x << y Desplazamiento a la izquierda
x >> y Desplazamiento a la derecha
Comprobación de x<y Menor que
tipos y x>y Mayor que
relacionales
x <= y Menor o igual que
x >= y Mayor o igual que
x is T Devuelve t ruesi x es T, de lo contrario, false
x as T Devuelve x escrito como T o null si x no es un T
Igualdad x == y Igual
x != y No igual

43Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 9


44Especificación del lenguaje C#

AND lógico x&y AND bit a bit entero, AND lógico booleano
XOR lógico x^y XOR bit a bit entero, XOR lógico booleano
OR lógico x|y OR bit a bit entero, OR lógico booleano
AND condicional x && y Evaluates y only if x is true
OR condicional x || y Evaluates y only if x is false
Condicional x?y:z Evalúa y si x es true, z si x es false
Asignación x=y Asignación
x op= y Asignación compuesta; los operadores compatibles
son
*= /= %= += -= <<= >>= &= ^= |=
14
151.5 Instrucciones
16Las acciones de un programa se expresan utilizando instrucciones. C# admite varios tipos de instrucciones,
17algunos de los cuales se definen en términos de instrucciones incrustadas.
18Un bloque permite escribir varias instrucciones en contextos donde se admite una única instrucción. Un bloque
19se compone de una lista de instrucciones escrita entre delimitadores { y }.
20Las instrucciones de declaración se utilizan para declarar constantes y variables locales.
21Las instrucciones de expresión se utilizan para evaluar las expresiones. Las expresiones que pueden utilizarse
22como instrucciones incluyen invocaciones de método, asignaciones de objetos utilizando el operador new,
23asignaciones utilizando =, operadores de asignación compuesta y operadores de incremento y decremento
24utilizando los operadores ++ y --.
25Las instrucciones de selección seleccionan una de las instrucciones que se van a ejecutar en función del valor de
26alguna expresión. En este grupo están las instrucciones if y switch.
27Las instrucciones de iteración se utilizan para ejecutar repetidamente una instrucción incrustada. En este grupo
28están las instrucciones while, do, for y foreach.
29Las instrucciones de salto se utilizan para transferir el control. En este grupo están las instrucciones break,
30continue, goto, throw y return.
31La instrucción try...catch se utiliza para detectar las excepciones que se producen durante la ejecución de un
32bloque y la instrucción try...finally se usa para especificar el código de finalización que siempre se ejecuta,
33aparezca o no una excepción.
34Las instrucciones checked y unchecked se utilizan con el fin de controlar el contexto de comprobación de
35desbordamiento para operaciones aritméticas y conversiones.
36La instrucción lock se utiliza para bloquear un objeto determinado mediante exclusión mutua, ejecutar una
37instrucción y, a continuación, liberar el bloqueo.
38La instrucción using se utiliza para obtener un recurso, ejecutar una instrucción y, a continuación, eliminar
39dicho recurso.
40La tabla siguiente muestra las instrucciones de C# y proporciona un ejemplo para cada una.
41

4510 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


46 Capítulo 18 Código no seguro

Instrucción Ejemplo
Declaraciones de static void Main() {
variables locales int a;
int b = 2, c = 3;
a = 1;
Console.WriteLine(a + b + c);
}
Declaraciones de static void Main() {
constantes locales const float pi = 3.1415927f;
const int r = 25;
Console.WriteLine(pi * r * r);
}
Instrucciones de static void Main() {
expresiones int i;
i = 123; // Expression statement
Console.WriteLine(i); // Expression statement
i++; // Expression statement
Console.WriteLine(i); // Expression statement
}
Instrucción i f static void Main(string[] args) {
if (args.Length == 0) {
Console.WriteLine("No arguments");
}
else {
Console.WriteLine("One or more arguments");
}
}
Instrucción sw i t ch static void Main(string[] args) {
int n = args.Length;
switch (n) {
case 0:
Console.WriteLine("No arguments");
break;
case 1:
Console.WriteLine("One argument");
break;
default:
Console.WriteLine("{0} arguments", n);
break;
}
}
}
Instrucción whi le static void Main(string[] args) {
int i = 0;
while (i < args.Length) {
Console.WriteLine(args[i]);
i++;
}
}

47Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 11


48Especificación del lenguaje C#

Instrucción do static void Main() {


string s;
do {
s = Console.ReadLine();
if (s != null) Console.WriteLine(s);
} while (s != null);
}
Instrucción f o r static void Main(string[] args) {
for (int i = 0; i < args.Length; i++) {
Console.WriteLine(args[i]);
}
}
Instrucción f o reach static void Main(string[] args) {
foreach (string s in args) {
Console.WriteLine(s);
}
}
Instrucción break static void Main() {
while (true) {
string s = Console.ReadLine();
if (s == null) break;
Console.WriteLine(s);
}
}
Instrucción cont inue static void Main(string[] args) {
for (int i = 0; i < args.Length; i++) {
if (args[i].StartsWith("/")) continue;
Console.WriteLine(args[i]);
}
}
Instrucción goto static void Main(string[] args) {
int i = 0;
goto check;
loop:
Console.WriteLine(args[i++]);
check:
if (i < args.Length) goto loop;
}
Instrucción re tu rn static int Add(int a, int b) {
return a + b;
}
static void Main() {
Console.WriteLine(Add(1, 2));
return;
}

4912 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


50 Capítulo 18 Código no seguro

Instrucciones th row static double Divide(double x, double y) {


y t ry if (y == 0) throw new DivideByZeroException();
return x / y;
}
static void Main(string[] args) {
try {
if (args.Length != 2) {
throw new Exception("Two numbers required");
}
double x = double.Parse(args[0]);
double y = double.Parse(args[1]);
Console.WriteLine(Divide(x, y));
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
Instrucciones static void Main() {
checked y int i = int.MaxValue;
unchecked checked {
Console.WriteLine(i + 1); // Exception
}
unchecked {
Console.WriteLine(i + 1); // Overflow
}
}
l ockstatement class Account
{
decimal balance;
public void Withdraw(decimal amount) {
lock (this) {
if (amount > balance) {
throw new Exception("Insufficient funds");
}
balance -= amount;
}
}
}
us ingstatement static void Main() {
using (TextWriter w = File.CreateText("test.txt")) {
w.WriteLine("Line one");
w.WriteLine("Line two");
w.WriteLine("Line three");
}
}
42
431.6 Clases y objetos
44Las clases son el tipo más importante de C#. Una clase es una estructura de datos que combina estados (campos)
45y acciones (métodos y otros miembros de función) en una unidad única. Una clase proporciona una definición
46para las instancias de la clase creadas dinámicamente, también conocidas como objetos. Las clases admiten la

51Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 13


52Especificación del lenguaje C#

1herencia y el polimorfismo, mecanismos mediante los que una clase derivada puede ampliar y especializar una
2clase base.
2Las clases nuevas se crean utilizando declaraciones de clase. Las declaraciones de clase se inician con un
3encabezado que especifica los atributos y modificadores de la clase, el nombre, la clase base (si existe) y las
4interfaces implementadas por ella. El encabezado va seguido por el cuerpo de la clase, que consiste en una lista
5de declaraciones de miembros escritas entre los delimitadores { y }.
3La siguiente es una declaración de una clase simple denominada Po in t:

4 public class Point

5 {

65 public Point(int x, int y) {

6 this.x = x;

7 this.y = y;
68Las instancias de clases se crean utilizando el operador new , que asigna memoria para una nueva instancia,
7invoca un constructor para inicializar la instancia y devuelve una referencia a la misma. Las instrucciones
98siguientes crean dos objetos Po in ty almacenan las referencias a dichos objetos en dos variables:

7 Point p1 = new Point(0, 0);

88La memoria
Point p2 =por
ocupada newun Point(10, 20);automáticamente cuando el objeto deja de estar en uso. No es
objeto se reclama
9posible ni necesario desasignar explícitamente objetos en C#.

91.6.1 Miembros
10Los miembros de una clase son o miembros estáticos o miembros de instancia. Los miembros estáticos
11pertenecen a las clases y los miembros de instancia pertenecen a los objetos (instancias de clases).
11La tabla siguiente proporciona información general sobre los tipos de miembros que puede contener una clase.
12

Miembro Descripción
Constantes Los valores constantes asociados a la clase
Campos Las variables de la clase
Métodos Los cálculos y acciones que puede realizar la clase
Propiedades Las acciones asociadas a la lectura y escritura de propiedades con nombre de la
clase
Indizadores Las acciones asociadas a instancias de indización de la clase como una matriz
Eventos Las notificaciones que pueden ser generadas por la clase
Operadores Las conversiones y operadores de expresión admitidos por la clase
Constructores Las acciones requeridas para inicializar instancias de la clase o la propia clase
Destructores Acciones que hay que llevar a cabo antes de que las instancias de la clase sean
descartadas de forma permanente
Tipos Tipos anidados declarados por la clase
13

5314 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


54 Capítulo 18 Código no seguro

11.6.2 Accesibilidad
2Cada miembro de una clase tiene asociada una accesibilidad, que controla las regiones del texto del programa a
3las que tiene acceso el miembro. Existen cinco formas posibles de accesibilidad. Éstas se resumen en la
4siguiente tabla.
5

Accesibilidad Significado
public Acceso no limitado
protected Acceso limitado a esta clase y a las clases derivadas de la misma
internal Acceso limitado a este programa
protected internal Acceso limitado a este programa y a las clases derivadas de la misma
private Acceso limitado a esta clase
6
71.6.3 Clases base
8Una declaración de clase puede especificar una clase base si a continuación del nombre de clase se colocan dos
9puntos y el nombre de la clase base. Omitir una especificación de clase base es igual que derivar del tipo ob jec t.
10En el ejemplo siguiente, la clase base de Po in t3Des Point, y la clase base de Point es object:
11 pub l i c c lass Po in t
12 {
13 pub l i c i n t x , y ;
14 pub l i c Po in t ( i n t x , i n t y ) {
15 th i s . x = x ;
16 th i s . y = y ;
17 }
18 }
19 pub l i c c lass Po in t3D: Po in t
20 {
21 pub l i c i n t z ;
22 pub l i c Po in t3D( in t x , i n t y , i n t z ) : Po in t (x , y ) {
23 th i s . z = z ;
24 }
25 }
26Las clases heredan los miembros de sus clases base. La herencia significa que una clase contiene implícitamente
27todos los miembros de su clase base, salvo los constructores de la clase base. Una clase derivada puede agregar
28nuevos miembros a los heredados, pero no puede quitar la definición de un miembro heredado. En el ejemplo
29anterior, Point3D hereda los campos x e y de Point, y cada instancia de Point3D contiene tres campos x, y, y z.
30Existe una conversión implícita desde un tipo de clase a cualquiera de sus tipos de clase base. Por consiguiente,
31una variable de un tipo de clase puede hacer referencia a una instancia de dicha clase o a una instancia de
32cualquier clase derivada. Por ejemplo, teniendo en cuenta las declaraciones de clase anteriores, una variable de
33tipo Point puede hacer referencia a un Point o a un Point3D:
34 Po in t a = new Po in t (10 , 20) ;
35 Po in t b = new Po in t3D(10 , 20 , 30) ;

361.6.4 Campos
37Un campo es una variable que está asociada a una clase o a una instancia de una clase.

55Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 15


56Especificación del lenguaje C#

1Un campo declarado con el modificador s ta t i cdefine un campo estático. Un campo estático identifica
2exactamente una ubicación de almacenamiento. Independientemente del número de instancias que se creen de
3una clase, sólo habrá una copia de un campo estático.
4Un campo declarado sin el modificador s ta t i cdefine un campo de instancia. Todas las instancias de una clase
5contienen una copia independiente de todos los campos de instancia de esa clase.
6En el ejemplo siguiente, cada instancia de la clase Co lo rtiene una copia independiente de los campos de
7instancia r, g y b, pero sólo hay una copia de los campos estáticos Black, White, Red, Green y Blue:

8 public class Color

9 {

10 public static readonly Color Black = new Color(0, 0, 0);

11 public static readonly Color White = new Color(255, 255, 255);

12 public static readonly Color Red = new Color(255, 0, 0);


15 private byte r, g, b;
13
16 public Color(byte r, byte g, byte b) {
14
17 this.r = r;

18 this.g = g;

19 this.b = b;
22Como se puede ver en el ejemplo anterior, los campos de sólo lectura se pueden declarar con un modificador
20
23readonly. Solamente se pueden realizar asignaciones a un campo readonly como parte de la declaración del
24
21campo o en un constructor de instancias o un constructor estático en la misma clase.
251.6.5 Métodos
26Un método es un miembro que implementa un cálculo o una acción que puede realizar un objeto o una clase. El
27acceso a los métodos estáticos se obtiene a través de la clase. Se tiene acceso a los métodos de instancia a través
28de las instancias de la clase.
29Los métodos tienen una lista (posiblemente vacía) de parámetros, que representa valores o referencias variables
30que se pasan al método y un tipo de valor devuelto que especifica el tipo del valor calculado y devuelto por el
31método. El tipo de valor devuelto de un método es void si no devuelve ningún valor.
32La firma de un método debe ser única en la clase en la que se declara el método. La firma de un método se
33compone del nombre del método y del número, los modificadores y los tipos de sus parámetros. La firma de un
34método no incluye el tipo de valor devuelto.

351.6.5.1 Parámetros
36Los parámetros se utilizan para pasar valores o referencias variables a los métodos. Los parámetros de un
37método reciben sus valores reales de los argumentos que se especifican cuando se invoca el método. Existen
38cuatro clases de parámetros: parámetros de valores, parámetros de referencias, parámetros de salida y matrices
39de parámetros.
40Para pasar el parámetro de entrada se utiliza un parámetro de valor. Un parámetro de valor corresponde a una
41variable local que obtiene su valor inicial del argumento proporcionado para el parámetro. Las modificaciones
42de un parámetro de valor no afectan al argumento proporcionado para el parámetro.
43Un parámetro de referencia se utiliza tanto para proporcionar parámetros de entrada como de salida. El
44argumento pasado para un parámetro de referencia debe ser una variable, y durante la ejecución del método, el

5716 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


58 Capítulo 18 Código no seguro

1parámetro de referencia representa la misma ubicación de almacenamiento que la variable del argumento. Los
2parámetros de referencia se declaran con el modificador re f. El ejemplo siguiente ilustra el uso de los
3parámetros re f .

2 using System;
3 class Test

4 {

5 static void Swap(ref int x, ref int y) {

6 int temp = x;

7 x = y;
4 static void Main() {
8
5 int i = 1, j = 2;
9
6 Swap(ref i, ref j);

7 Console.WriteLine("{0} {1}", i, j); // Outputs "2 1"


5Para pasar parámetros de salida se utiliza un parámetro de salida. Un parámetro de salida es similar a un
68parámetro de referencia, excepto en que el valor inicial del argumento suministrado por el llamador no es
79importante. Los parámetros de salida se declaran con un modificador out. El ejemplo siguiente ilustra el uso de
8los parámetros out .

6 using System;
7 class Test

8 {

9 static void Divide(int x, int y, out int result, out int remainder) {

10 result = x / y;

118 static void Main() {

129 int res, rem;

10 Divide(10, 3, out res, out rem);

11 Console.WriteLine("{0} {1}", res, rem); // Outputs "3 1"


9Una matriz de parámetros permite pasar un número variable de argumentos a un método. Un parámetro de
12
10matriz se declara con un modificador params . Sólo el último parámetro de un método puede ser una matriz de
11parámetros y el tipo de una matriz de parámetros debe ser un tipo de matriz unidimensional. Los métodos Wr i te
13
12y WriteLine de la clase System.Console son buenos ejemplos del uso de una matriz de parámetros. Se
13declaran del siguiente modo:

10 public class Console

11 {

12
11 public static void WriteLine(string fmt, params object[] args) {...}
12 ...

13 } un método que utiliza una matriz de parámetros, la matriz de parámetros se comporta exactamente
13Dentro de
14como un parámetro normal de un tipo de matriz. Sin embargo, en una invocación de un método con una matriz
15de parámetros, es posible pasar un argumento único del tipo de matriz de parámetros o cualquier número de

59Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 17


60Especificación del lenguaje C#

1argumentos del tipo elemento de la matriz de parámetros. En este último caso, se crea automáticamente una
2instancia de matriz y se inicializa con los argumentos especificados. Este ejemplo

2 Console.WriteLine("x={0} y={1} z={2}", x, y, z);


3equivale a escribir lo siguiente.

4 object[] args = new object[3];

5 args[0] = x;

6 args[1] = y;

751.6.5.2 Cuerpo del método y variables locales


86El cuerpo de un método especifica las instrucciones que se deben ejecutar cuando se invoque el método.
7Un cuerpo del método puede declarar variables específicas para la invocación del método. Tales variables se
8denominan variables locales. Una declaración de variable local especifica un nombre de tipo, un nombre de
9variable y posiblemente un valor inicial. El ejemplo siguiente declara una variable local i con un valor inicial de
10cero y una variable local j sin valor inicial.

8 using System;
9 class Squares

10 {

11 static void Main() {

12 int i = 0;

13 int j;

14 while (i < 10) {

15 j = i * i;
10
16C# exige que las variables locales estén definitivamente asignadas para que se puedan obtener sus valores. Por
11ejemplo, si la declaración de i anterior no incluyera un valor inicial, el compilador notificaría un error para la
17utilización subsiguiente de i ,porque i no estaría definitivamente asignada en dichos puntos del programa.
12
18
11Un método puede utilizar instrucciones return para devolver el control a su llamador. En un método que
12
19devuelve void , las instrucciones return no pueden especificar una expresión. En un método que no devuelve
13void, las instrucciones return deben incluir una expresión que calcule el valor devuelto.
20
121.6.5.3 Métodos estáticos y de instancia
13Un método declarado con un modificador static es un método estático. Un método estático no funciona en una
14instancia específica y sólo tiene acceso a miembros estáticos.
14Un método declarado sin un modificador static es un método de instancia. Los métodos de instancia funcionan
15en instancias específicas y pueden tener acceso tanto a miembros estáticos como a los de instancia. Se puede
16tener acceso explícitamente a la instancia en la que se invocó un método de instancia como this. Es incorrecto
17hacer referencia a this en un método estático.
15La clase Entity siguiente tiene tanto miembros estáticos como de instancia.

16 class Entity

17 {

18
6118 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
62 Capítulo 18 Código no seguro

1 int serialNo;
2 public Entity() {

3 serialNo = nextSerialNo++;

45 public int GetSerialNo() {

6 return serialNo;

78 public static int GetNextSerialNo() {

9 return nextSerialNo;

10
11 public static void SetNextSerialNo(int value) {

12 nextSerialNo = value;

13 }
15Cada instancia Ent i t ycontiene un número de serie (y probablemente alguna otra información que no se muestra
16
14aquí). El constructor Ent i t y(que es como un método de instancia) inicializa la nueva instancia con el siguiente
17número de serie disponible. Dado que el constructor es un miembro de instancia, se puede tener acceso tanto al
18campo de instancia serialNo como al campo estático nextSerialNo.
19Los métodos estáticos GetNextSerialNo y SetNextSerialNo pueden tener acceso al campo estático
20nextSerialNo, pero sería incorrecto que tuvieran acceso al campo de instancia serialNo.
21En el ejemplo siguiente se muestra el uso de la clase Entity.

22 using System;
23 class Test

24 {

25 static void Main() {


27 Entity e1 = new Entity();
26
28 Entity e2 = new Entity();
29 Console.WriteLine(e1.GetSerialNo()); // Outputs "1000"

30 Console.WriteLine(e2.GetSerialNo()); // Outputs "1001"

31 Console.WriteLine(Entity.GetNextSerialNo()); // Outputs "1002"


34Tenga en cuenta que los métodos estáticos SetNextSerialNo y GetNextSerialNo se invocan en la clase,
32
35mientras que el método de instancia GetSerialNo se invoca en las instancias de la clase.
33
361.6.5.4 Métodos virtuales, de reemplazo y abstractos
37Cuando una declaración de método de instancia incluye un modificador virtual, se dice que el método es un
38método virtual. Si no existe un modificador virtual, se dice que el método es un método no virtual.
39En la invocación de un método virtual, el tipo en tiempo de ejecución de la instancia para la que tiene lugar la
40invocación determina la implementación del método real que se va a invocar. Cuando se invoca a un método no
41virtual, el factor determinante es el tipo en tiempo de compilación de la instancia.
42En las clases derivadas, los métodos virtuales se pueden reemplazar. Cuando una declaración del método de
43instancia incluye un modificador override, el método reemplaza un método virtual heredado con la misma
44firma. Mientras que una declaración de método virtual introduce un método nuevo, una declaración de método

63Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 19


64Especificación del lenguaje C#

1de reemplazo especializa un método virtual heredado existente proporcionando una nueva implementación de
2ese método.
3Un método abstract es un método virtual sin implementación. Los métodos abstractos se declaran con el
4modificador abs t rac ty sólo se permiten en una clase que también se declare como abs t rac.tLos métodos
5abstractos deben reemplazarse en cada clase derivada no abstracta.
4El ejemplo siguiente declara una clase abstracta, Express ion, que representa un nodo de árbol de expresión y
5tres clases derivadas Cons tan t, VariableReference y Operation, que implementan los nodos de árbol de
6expresión para las constantes, referencias variables y operaciones aritméticas.

5 using System;

6 using System.Collections;
6 public abstract class Expression

7 {

8 public abstract double Evaluate(Hashtable vars);


7 public class Constant: Expression
9
8 {

98 public Constant(double value) {

9 this.value = value;

109 public override double Evaluate(Hashtable vars) {

10 return value;

11 }
10 public class VariableReference: Expression
12
11 {

12
11 public VariableReference(string name) {

12 this.name = name;

13
12 public override double Evaluate(Hashtable vars) {

13 object value = vars[name];

14 if (value == null) {

15 throw new Exception("Unknown variable: " + name);

16 }
13
17 public class Operation: Expression

14
18 {

15
19 Expression left;

16
14 public Operation(Expression left, char op, Expression right) {
17
15 this.left = left;

16 this.op = op;

17
18
6520 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
66 Capítulo 18 Código no seguro

1 public override double Evaluate(Hashtable vars) {

2 double x = left.Evaluate(vars);

3 double y = right.Evaluate(vars);

4 switch (op) {

5 case '+': return x + y;

6 case '-': return x - y;

7 case '*': return x * y;


138Las cuatro clases anteriores se pueden utilizar para modelar expresiones aritméticas. Por ejemplo, utilizando
14instancias de estas clases, la expresión x+3 se puede representar como sigue.
9
15
10 Expression e = new Operation(

16
11 new VariableReference("x"),

17
12 '+',
19El método Eva lua tede una instancia Express ionse invoca para evaluar la expresión determinada y generar un
20
18valor double. El método toma como argumento un Hashtable que contiene nombres de variable (como claves
21de las entradas) y valores (como valores de las entradas). El método Evaluate es un método abstracto virtual, lo
22que significa que deben reemplazarlo las clases derivadas no abstractas para proporcionar una implementación
23real.
24Una implementación de Constant de Evaluate simplemente devuelve la constante almacenada. Una
25implementación de VariableReference busca el nombre de la variable en la tabla hash y devuelve el valor
26resultante. Una implementación de Operation evalúa primero los operandos izquierdo y derecho (invocando de
27forma recursiva sus métodos Evaluate) y, a continuación, lleva a cabo la operación aritmética correspondiente.
28El programa siguiente utiliza las clases Expression para evaluar la expresión x*(y+2) para distintos valores de
29x e y.

30 using System;

31 using System.Collections;
32 class Test

33 {

34
35 Expression e = new Operation(

36 new VariableReference("x"),

37 '*',

38 new Operation(

39 new VariableReference("y"),

40 '+',
44 Hashtable vars = new Hashtable();
41
45 vars["x"] = 3;
42
46 vars["y"] = 5;
43
47

67Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 21


68Especificación del lenguaje C#

1 vars["x"] = 1.5;

2 vars["y"] = 9;

3 Console.WriteLine(e.Evaluate(vars)); // Outputs "16.5"

461.6.5.5 Sobrecarga de métodos


57La sobrecarga del método permite que varios métodos de la misma clase tengan el mismo nombre siempre que
8sus firmas sean únicas. Al compilar una invocación de un método sobrecargado, el compilador utiliza la
9resolución de sobrecarga para determinar el método concreto que se va a invocar. La resolución de sobrecarga
10busca el método que mejor se ajusta a los argumentos o informa de un error si no se puede encontrar ningún
11ajuste adecuado. El ejemplo siguiente muestra la resolución de sobrecarga en vigor. El comentario para cada
12invocación en el método Main muestra qué método se ha invocado.

13 class Test

14 {

15 static void F() {...}

16
18 static void F(object x) {
17
19 Console.WriteLine("F(object)");

20
21 static void F(int x) {

22 Console.WriteLine("F(int)");

23
24 static void F(double x) {

25 Console.WriteLine("F(double)");

26
27 static void F(double x, double y) {

28 Console.WriteLine("F(double, double)");

29
30 static void Main() {

31 F(); // Invokes F()

32 F(1); // Invokes F(int)

33 F(1.0); // Invokes F(double)

34 F("abc"); // Invokes F(object)

35 F((double)1); // Invokes F(double)


40
36Como muestra el ejemplo, un método determinado siempre puede ser seleccionado convirtiendo explícitamente
41los argumentos en los tipos de parámetros exactos.
37
42
381.6.6 Otros miembros de función
43Los miembros que contienen código ejecutable se conocen colectivamente como miembros de función de una
39clase. La sección anterior describe métodos, que son el tipo primario de miembros de función. En esta sección se
44
45describen los otros tipos de miembros de función admitidos por C#: constructores, propiedades, indizadores,
46eventos, operadores y destructores.

6922 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


70 Capítulo 18 Código no seguro

1La tabla siguiente muestra una clase llamada L i s tque implementa una lista creciente de objetos. La clase
2contiene varios ejemplos de los tipos más comunes de miembros de función.
3

public class List


{
const int defaultCapacity = 4; Constante
object[] items; Campos
int count;
public List(): List(defaultCapacity) {} Constructores
public List(int capacity) {
items = new object[capacity];
}
public int Count { Propiedades
get { return count; }
}
public string Capacity {
get {
return items.Length;
}
set {
if (value < count) value = count;
if (value != items.Length) {
object[] newItems = new object[value];
Array.Copy(items, 0, newItems, 0, count);
items = newItems;
}
}
}
public object this[int index] { Indizador
get {
return items[index];
}
set {
items[index] = value;
OnListChange();
}
}

71Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 23


72Especificación del lenguaje C#

public void Add(object item) { Métodos


if (count == Capacity) Capacity = count * 2;
items[count] = item;
count++;
OnChanged();
}
protected virtual void OnChanged() {
if (Changed != null) Changed(this, EventArgs.Empty);
}
public override bool Equals(object other) {
return Equals(this, other as List);
}
static bool Equals(List a, List b) {
if (a == null) return b == null;
if (b == null || a.count != b.count) return false;
for (int i = 0; i < a.count; i++) {
if (!object.Equals(a.items[i], b.items[i])) {
return false;
}
}
return true;
}
public event EventHandler Changed; Evento
public static bool operator ==(List a, List b) { Operadores
return Equals(a, b);
}
public static bool operator !=(List a, List b) {
return !Equals(a, b);
}
}
4
51.6.6.1 Constructores
6C# admite tanto constructores de instancia como estáticos. Un constructor de instancia es un miembro que
7implementa las acciones que se requieren para inicializar una instancia de una clase. Un constructor estático es
8un miembro que implementa las acciones exigidas para inicializar una clase cuando se carga por primera vez.
9Un constructor se declara como método sin devolución de tipo y con el mismo nombre que la clase contenedora.
10Si una declaración de un constructor incluye un modificador s ta t i,cdeclara un constructor estático. De lo
11contrario, declara un constructor de instancia.
12Los constructores de instancia se pueden sobrecargar. Por ejemplo, la clase L i s tdeclara dos constructores de
13instancia, uno sin parámetros y otro que toma un parámetro i n t. Los constructores de instancia se invocan
14utilizando el operador new. Las siguientes instrucciones asignan dos instancias List utilizando cada uno de los
15constructores de la clase List.
16 L i s t l i s t1 = new L i s t ( ) ;
17 L i s t l i s t2 = new L i s t (10 ) ;
18Al contrario que otros miembros, los constructores de instancia no se heredan, y una clase sólo tiene los
19constructores de instancia realmente declarados en la clase. Si no se suministra ningún constructores de instancia
20para una clase, se proporcionará automáticamente uno vacío sin parámetros.

7324 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


74 Capítulo 18 Código no seguro

11.6.6.2 Propiedades
2Las propiedades son una extensión natural de los campos. Los dos son miembros denominados con tipos
3asociados, y la sintaxis que se utiliza para el acceso a campos y propiedades es la misma. No obstante, a
4diferencia de los campos, las propiedades no denotan ubicaciones de almacenamiento. Las propiedades tienen
5descriptores de acceso que especifican las instrucciones que deben ejecutarse para leer o escribir sus valores.
6Una propiedad se declara como un campo, sólo que la declaración finaliza con un descriptor de acceso get y/o
7un descriptor de acceso se t escrito entre los delimitadores { y } en lugar de finalizar con un punto y coma. Una
8propiedad que tiene un descriptor de acceso get y un set es una propiedad de lectura y escritura, una propiedad
9que tiene sólo un descriptor de acceso get es una propiedad de sólo lectura, y una propiedad que tiene sólo un
10descriptor de acceso set es una propiedad de sólo escritura.
11Un descriptor de acceso get corresponde a un método sin parámetros que devuelve un valor del tipo de la
12propiedad. Exceptuando cuando se trata del destino de una asignación, al hacer referencia a una propiedad en
13una expresión, se invoca al descriptor de acceso get de la propiedad para calcular su valor.
14Un descriptor de acceso set corresponde a un método con un parámetro único denominado value y ningún tipo
15de valor devuelto. Cuando se hace referencia a una propiedad como el destino de una asignación o como el
16operando de ++ o --, se invoca al descriptor de acceso set con un argumento que proporciona el nuevo valor.
17La clase List declara dos propiedades, Count y Capacity; la primera de ellas es de sólo lectura y la segunda de
18lectura y escritura. El siguiente es un ejemplo del uso de estas propiedades.

19 List names = new List();

20 names.Capacity = 100; // Invokes set accessor

21 int i = names.Count; // Invokes get accessor


23Al igual que los campos y métodos, C# admite tanto las propiedades de instancia como las propiedades
24
22estáticas. Las propiedades estáticas se declaran con el modificador static, y las propiedades de instancias, sin él.
25Los descriptores de acceso de una propiedad pueden ser virtuales. Cuando una declaración de propiedad incluye
26un modificador virtual, abstract u override, se aplica a los descriptores de acceso de la propiedad.

271.6.6.3 Indizadores
28Un indizador es un miembro que permite indizar objetos de la misma manera que una matriz. Un indizador se
29declara como una propiedad, salvo en que el nombre del miembro es this seguido de una lista de parámetros
30escritos entre delimitadores [ y ]. Los parámetros están disponibles en los descriptores de acceso del indizador.
31Al igual que las propiedades, los indizadores pueden ser de lectura y escritura, de sólo lectura y de sólo
32escritura, y los descriptores de acceso de un indizador pueden ser virtuales.
33La clase List declara un indizador de lectura y escritura único que toma un parámetro int. El indizador permite
34indizar instancias List con valores int. Por ejemplo:

35 List numbers = new List();

36 names.Add("Liz");

37 names.Add("Martha");

38 names.Add("Beth");

39 for (int i = 0; i < names.Count; i++) {


43Los indizadores se pueden sobrecargar, lo que significa que pueden declararse varios indizadores siempre y
40cuando sus parámetros difieran en número o tipos.
44
41
42
75Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 25
76Especificación del lenguaje C#

11.6.6.4 Eventos
2Un evento es un miembro que permite a una clase u objeto proporcionar notificaciones. Un evento se declara
3como un campo, salvo que la declaración incluye una palabra clave event y el tipo debe ser un tipo delegado.
4En una clase que declara un miembro de evento, el evento se comporta como un campo de un tipo delegado
5(siempre que el evento no sea abstracto y no declare descriptores de acceso). El campo almacena una referencia
6a un delegado que representa los controladores de eventos que se han agregado al evento. Si no hay ningún
7controlador de eventos presente, el campo es nu l .l
8La clase L i s tdeclara un miembro de evento único llamado Changed , que indica que se ha agregado un nuevo
9elemento a la lista. El método virtual OnChanged inicia el evento Changed, comprobando previamente que el
10evento es null (lo que significa que no hay ningún controlador presente). La noción de iniciar un evento
11equivale exactamente a invocar el delegado representado por el evento; por lo tanto, no hay construcciones
12especiales del lenguaje para producir eventos.
13Los clientes reaccionan a los eventos a través de los controladores de eventos. Los controladores de eventos se
14asocian utilizando el operador += y se eliminan utilizando el operador -=. El ejemplo siguiente asocia un
15controlador de eventos al evento Changed de List.

7726 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


78 Capítulo 18 Código no seguro

1 using System;
2 class Test

3 {

45 static void ListChanged(object sender, EventArgs e) {

6 changeCount++;

78 static void Main() {

9 List names = new List();

10 names.Changed += new EventHandler(ListChanged);

11 names.Add("Liz");

12 names.Add("Martha");

13 names.Add("Beth");
17Para escenarios avanzados donde se busca el control del almacenamiento subyacente de un evento, una
18
14declaración de evento puede proporcionar explícitamente descriptores de acceso add y remove , que en cierto
19modo son similares al descriptor de acceso set de una propiedad.
15
20
161.6.6.5 Operadores
21Un operador es un miembro que define el significado de aplicar un operador de expresión determinado a las
22instancias de una clase. Pueden definirse tres categorías de operadores: operadores unarios, operadores binarios
23y operadores de conversión. Todos los operadores deberán declararse como public y static.
24La clase List declara dos operadores operator== y operator!=, proporcionando de este modo un nuevo
25significado a expresiones que aplican dichos operadores a instancias List. Específicamente, los operadores
26definen la igualdad de dos instancias List comparando todos los objetos contenidos que utilizan sus métodos
27Equals. El ejemplo siguiente utiliza el operador == para comparar dos instancias List.

28 using System;
29 class Test

30 {

31 static void Main() {

32 List a = new List();

33 a.Add(1);

34 a.Add(2);

35 List b = new List();

36 b.Add(1);

37El resultado del primer


43 b.Add(2);
Console.WriteLine es True porque las dos listas contienen el mismo número de objetos
44
38con los mismos valores. Si List no definiera operator==, el primer Console.WriteLine habría generado False
45porque a y b hacen referencia a dos instancias List diferentes.
39
40
41
42
79Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 27
80Especificación del lenguaje C#

11.6.6.6 Destructores
2Un destructor es un miembro que implementa las acciones necesarias para destruir una instancia de una clase.
3Los destructores no pueden tener parámetros, no pueden tener modificadores de accesibilidad y no se pueden
4invocar de forma explícita. El destructor de una instancia se invoca automáticamente durante la recolección de
5elementos no utilizados.
6El recolector de elementos no utilizados tiene una amplia libertad para decidir cuándo debe recolectar objetos y
7ejecutar destructores. En concreto, el control de tiempo de las invocaciones del destructor no es determinista y
8los destructores se pueden ejecutar en cualquier subproceso. Por estas y otras razones, las clases sólo deberían
9implementar los destructores cuando no es factible ninguna otra solución.

101.7 Estructuras
11Como las clases, las estructuras son estructuras de datos que pueden contener miembros de datos y miembros
12de función, pero al contrario que las clases, las estructuras son tipos de valor y no requieren asignación del
13montón. Una variable de un tipo de estructura almacena directamente los datos de la estructura, mientras que
14una variable de un tipo de clase almacena una referencia a un objeto dinámicamente asignado. Los tipos de
15estructura no admiten la herencia especificada por el usuario y heredan implícitamente del tipo ob jec t.
16Las estructuras son particularmente útiles para estructuras de datos pequeñas que tienen semánticas de valor. Los
17números complejos, los puntos de un sistema de coordenadas o los pares de valores clave de un diccionario son
18buenos ejemplos de estructuras. El uso de estructuras en lugar de clases para estructuras de datos pequeñas
19puede suponer una diferencia sustancial en el número de asignaciones de memoria que realiza una aplicación.
20Por ejemplo, el programa siguiente crea e inicializa una matriz de 100 puntos. Con Po in timplementada como
21una clase, se inicializan 101 objetos separados, uno para la matriz y uno para cada uno de los 100 elementos.
22 c lass Po in t
23 {
24 pub l i c i n t x , y ;
25 pub l i c Po in t ( i n t x , i n t y ) {
26 th i s . x = x ;
27 th i s . y = y ;
28 }
29
30 c lass Tes t
31 {
32 s ta t i c vo id Main ( ) {
33 Po in t [ ] po in ts = new Po in t [100 ] ;
34 f o r ( i n t i = 0 ; i < 100 ; i++) po in ts [ i ] = new Po in t ( i , i ) ;
35 }
37Una alternativa es convertir Po in ten una estructura.
36
38 s t ruc t Po in t
39 {
40 pub l i c i n t x , y ;
41 pub l i c Po in t ( i n t x , i n t y ) {
42 th i s . x = x ;
43 th i s . y = y ;
44 }
45
46Ahora, sólo se crea una instancia de un objeto, el del array, y las instancias de Po in tse almacenan en línea en el
47array.

8128 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


82 Capítulo 18 Código no seguro

1Los constructores de estructuras se invocan con el operador new, pero esto no implica que se esté asignando la
2memoria. En lugar de asignar dinámicamente un objeto y devolver una referencia al mismo, un constructor de
3estructuras simplemente devuelve el propio valor de la estructura (normalmente en una ubicación temporal en la
4pila) y a continuación se copia este valor como sea necesario.
5En el caso de las clases, es posible que dos variables hagan referencia al mismo objeto y, por tanto, que las
6operaciones en una variable afecten al objeto al que hace referencia la otra variable. En el caso de las
7estructuras, cada variable tiene su propia copia de los datos, de manera que no es posible que las operaciones de
8una afecten a la otra. Por ejemplo, el resultado generado por el fragmento de código siguiente depende de si
9Po in tes una clase o una estructura.
10 Po in t a = new Po in t (10 , 10) ;
11 Po in t b = a ;
12 a .x = 20 ;
13
14Si Po in Conso
tes unale .Wr ielteL
clase, ine (b .x
resultado es )20
; porque a y b hacen referencia al mismo objeto. Si Point es una
15estructura, el resultado es 10 porque la asignación de a a b crea una copia del valor y esta copia no resulta
16afectada por la subsiguiente asignación a a.x.
17El ejemplo anterior resalta dos de las limitaciones de estructuras. En primer lugar, copiar una estructura
18completa normalmente es menos eficaz que copiar una referencia a un objeto, por lo que la asignación y el modo
19de pasar parámetros de valor puede ser más costoso con estructuras que con tipos de referencia. En segundo
20lugar, salvo en el caso de los parámetros ref y out, no es posible crear referencias a estructuras, que descarta su
21uso en varias situaciones.

221.8 Matrices
23Una matriz es una estructura de datos que contiene una serie de variables a las que se obtiene acceso a través de
24índices calculados. Las variables contenidas en una matriz, también conocidas como elementos de la matriz, son
25todas del mismo tipo, que se denomina tipo de elemento de la matriz.
26Los tipos de matriz son tipos de referencia, por lo que la declaración de una variable matricial solamente reserva
27espacio para una instancia de matriz. Las instancias de matriz reales se crean dinámicamente en tiempo de
28ejecución utilizando el operador new. La operación new especifica la longitud de la nueva instancia de matriz,
29que se establece para el período de duración de la instancia. Los índices de los elementos de una matriz pueden
30ir desde 0 a Length-1. El operador new inicializa automáticamente los elementos de una matriz con su valor
31predeterminado que, por ejemplo, es cero para todos los tipos numéricos y null para todos los tipos de
32referencia.
33El ejemplo siguiente crea una matriz de elementos int, inicializa la matriz e imprime su contenido.
34 us ing Sys tem;
35 c lass Tes t
36 {
37 s ta t i c vo id Main ( ) {
38 i n t [ ] a = new i n t [10 ] ;
39 f o r ( i n t i = 0 ; i < a .Length ; i++) a[ i ] = i * i ;
40 f o r ( i n t i = 0 ; i < a .Length ; i++) {
41 Conso le .Wr i teL ine ( "a [{0}] = {1}" , i , a[ i ] ) ;
42 }
45
43 Este ejemplo crea y funciona en una matriz unidimensional. C# también admite las matrices
46multidimensionales. El número de dimensiones de un tipo de matriz, también conocido como el rango del tipo
44

83Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 29


84Especificación del lenguaje C#

1de matriz, es uno más el número de comas escrito entre los corchetes del tipo de matriz. El ejemplo siguiente
2asigna una matriz unidimensional, una bidimensional y una tridimensional.

3 int[] a1 = new int[10];

4 int[,] a2 = new int[10, 5];


65La matriz a1 contiene 10 elementos, la matriz a2 contiene 50 (10 × 5) elementos y la matriz a3 contiene 100
7(10 × 5 × 2) elementos.
8Los elementos de una matriz pueden ser de cualquier tipo, incluido un tipo de matriz. Una matriz con elementos
9de tipo de matriz a veces se denomina matriz escalonada porque las longitudes de las matrices de elementos no
10tienen que ser siempre las mismas. El ejemplo siguiente asigna una matriz de matrices de int:

11 int[][] a = new int[3][];

12 a[0] = new int[10];

13 a[1] = new int[5];


15La primera línea crea una matriz con tres elementos, cada uno de tipo int[] y cada uno con un valor inicial null.
16
14A continuación, las líneas subsiguientes inicializan los tres elementos con referencias a instancias de matriz
17individuales de longitudes diversas.
18El operador new permite especificar los valores iniciales de los elementos de matriz utilizando un inicializador
19de matrices, que es una lista de expresiones escrita entre los delimitadores { y }. El ejemplo siguiente asigna e
20inicializa un int[] con tres elementos.

21 int[] a = new int[] {1, 2, 3};


22Observe que la longitud de la matriz se deduce del número de expresiones existentes entre { y }. La variable
23local y las declaraciones de campo se pueden acortar más, de tal modo que no es necesario volver a especificar
24el tipo de matriz.

25 int[] a = {1, 2, 3};


26Los dos ejemplos anteriores son equivalentes a lo siguiente:

27 int[] a = new int[3];

28 a[0] = 1;

29 a[1] = 2;
311.9 Interfaces
30
32Una interfaz define un contrato que puede ser implementado por clases y estructuras. Una interfaz puede
33contener métodos, propiedades, eventos e indizadores. Una interfaz no proporciona implementaciones de los
34miembros que define, simplemente especifica los miembros que deben proporcionar las clases o estructuras que
35implementan la interfaz.
36Las interfaces pueden utilizar una herencia múltiple. En el ejemplo siguiente, la interfaz IComboBox hereda de
37ITextBox y de IListBox.

38 interface IControl

39 {

40 void Paint();

41

8530 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


86 Capítulo 18 Código no seguro

1 interface ITextBox: IControl

2 {

3 void SetText(string text);


5 interface IListBox: IControl
4
6 {

7 void SetItems(string[] items);


9 interface IComboBox: ITextBox, IListBox {}
8

87Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 31


88Especificación del lenguaje C#

1Las clases y las estructuras pueden implementar varias interfaces. En el ejemplo siguiente, la clase Ed i tBox
2implementa I Con t ro el IDataBound.

3 interface IDataBound

4 {

5 void Bind(Binder b);


7 public class EditBox: IControl, IDataBound
6
8 {

109 public void Bind(Binder b) {...}

11Cuando }una clase o estructura implementa una interfaz determinada, las instancias de dicha clase o estructura se
12
13pueden convertir implícitamente en dicho tipo de interfaz. Por ejemplo:

14 EditBox editBox = new EditBox();

15 IControl control = editBox;


17
16 En los casos en los que no se sabe estáticamente que una interfaz pueda implementar una interfaz determinada,
18se pueden utilizar conversiones de tipo dinámicas. Por ejemplo, las instrucciones siguientes utilizan
19conversiones de tipo dinámicas para obtener implementaciones de interfaces IControl y IDataBound de un
20objeto. Dado que el tipo real del objeto es EditBox, las conversiones se realizaron con éxito.

21 object obj = new EditBox();

22 IControl control = (IControl)obj;


24
23En la clase EditBox anterior, el método Paint de la interfaz IControl y el método Bind de la interfaz
25IDataBound se implementan utilizando miembros public. C# también admite implementaciones explícitas de
26miembro de la interfaz y, al utilizarlas la clase o la estructura, se puede evitar que los miembros sean public.
27Una implementación explícita de miembro de interfaz se escribe utilizando el nombre completo de miembro de
28interfaz. Por ejemplo, la clase EditBox podría implementar los métodos IControl.Paint e IDataBound.Bind
29utilizando implementaciones explícitas de miembro de interfaz del siguiente modo.

30 public class EditBox: IControl, IDataBound

31 {

32
33 void IDataBound.Bind(Binder b) {...}

34Sólo se puede
35 } tener acceso a los miembros de interfaz explícitos mediante el tipo de interfaz. Por ejemplo, la
36implementación de IControl.Paint proporcionada por la clase EditBox anterior sólo se puede invocar
37convirtiendo primero la referencia EditBox en el tipo de interfaz IControl.

38 EditBox editBox = new EditBox();

39 editBox.Paint(); // Error, no such method

40 IControl control = editBox;

41

8932 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


90 Capítulo 18 Código no seguro

11.10 Enumeraciones
2Un tipo enum (enumeración) es un tipo de valor distinto con un conjunto de constantes con nombre. El ejemplo
3siguiente declara y utiliza un tipo de enumeración denominado Co lo rcon tres valores constantes: Red , Green y
4Blue.

5 using System;
6 enum Color

7 {

8 Red,

9 Green,
12
10 class Test

13
11 {

14 static void PrintColor(Color color) {

15 switch (color) {

16 case Color.Red:

17 Console.WriteLine("Red");

18 break;

19 case Color.Green:

20 Console.WriteLine("Green");

21 break;

22 case Color.Blue:
30 static void Main() {
23
31
24 Color c = Color.Red;

32 PrintColor(c);
25
33
26 PrintColor(Color.Blue);
36Cada tipo enum tiene un tipo integral correspondiente denominado tipo subyacente del tipo de enumeración. Un
34
37tipo de enumeración que no declara explícitamente un tipo subyacente tiene int como tipo subyacente. El
27
38
35 formato de almacenamiento y el rango de posibles valores de un tipo de enumeración vienen determinados por
28
39su tipo subyacente. El conjunto de valores que puede tomar un tipo enum no está limitado por sus miembros de
40enumeración. En concreto, cualquier valor del tipo subyacente de una enumeración puede convertirse en el tipo
29
41enum, y es un valor aceptado y distintivo de dicho tipo enum.
42El ejemplo siguiente declara un tipo de enumeración denominado Alignment con un tipo subyacente de sbyte.

43 enum Alignment: sbyte

44 {

45 Left = -1,

46 Center = 0,

47
48
91Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 33
92Especificación del lenguaje C#

1Como se muestra en el ejemplo anterior, una declaración de miembro de enumeración puede incluir una
2expresión constante que especifica el valor del miembro. El valor constante de cada miembro de enumeración
3debe estar comprendido en el intervalo del tipo subyacente de la enumeración. Cuando una declaración de
4miembro de enumeración no especifica explícitamente un valor, se da al miembro el valor cero (si es el primer
5miembro del tipo enum) o el valor del miembro de la enumeración precedente más uno.
6Los valores de enumeración se pueden convertir en valores de tipo integral y viceversa, utilizando las
7conversiones de tipo. Por ejemplo:

8 int i = (int)Color.Blue; // int i = 2;

109El valor Color c = (Color)2;


predeterminado // Colores c
de cualquier tipo de enumeración = Color.Blue;
el valor cero de tipo integral convertido en el tipo
11enum. En los casos en los que las variables se inicializan automáticamente con un valor predeterminado, éste es
12el valor proporcionado para las variables de tipos enum. Para que el valor predeterminado de un tipo enum esté
13fácilmente disponible, el literal 0 se convierte implícitamente en cualquier tipo enum. De este modo, se permite
14lo siguiente.

15 Color c = 0;

161.11 Delegados
17Un tipo delegado representa las referencias a los métodos con una lista de parámetros determinada y un tipo de
18valor devuelto. Los delegados permiten tratar métodos como entidades, que se pueden asignar a las variables y
19pasarse como parámetros. Los delegados son similares al concepto de punteros a función de otros lenguajes,
20pero al contrario de los punteros a función, los delegados están orientados a objetos y proporcionan seguridad de
21tipos.
22El ejemplo siguiente declara y utiliza un tipo delegado denominado Func t i on.

23 using System;
24 delegate double Function(double x);
25 class Multiplier

26 {

27
28 public Multiplier(double factor) {

29 this.factor = factor;

30
31 public double Multiply(double x) {

32 return x * factor;

33 }
35 class Test
34
36 {

37 static double Square(double x) {

38
40 static double[] Apply(double[] a, Function f) {
39
41 double[] result = new double[a.Length];

42 for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);

43
44
9334 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
94 Capítulo 18 Código no seguro

1 static void Main() {

2 double[] a = {0.0, 0.5, 1.0};


3 double[] squares = Apply(a, new Function(Square));
4 double[] sines = Apply(a, new Function(Math.Sin));
5 Multiplier m = new Multiplier(2.0);

6 double[] doubles = Apply(a, new Function(m.Multiply));

7 }

95Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 35


96Especificación del lenguaje C#

1Una instancia del tipo delegado Func t i onpuede hacer referencia a cualquier método que tome un argumento
2doub le y que devuelva un valor double. El método Apply aplica un tipo Function determinado a los
3elementos de un tipo double[] y devuelve un tipo double[] con los resultados. En el método Main, se utiliza
4Apply para aplicar tres funciones diferentes a un double[].
5Un delegado puede hacer referencia a un método estático (como Square o Math.Sin en el ejemplo anterior) o a
6un método de instancia (como m.Multiply en el ejemplo anterior). Un delegado que hace referencia a un
7método de instancia también hace referencia a un objeto determinado y, cuando se invoca el método de instancia
8a través del delegado, dicho objeto pasa a ser this en la invocación.
9Una propiedad interesante y útil de un delegado es que no conoce ni necesita conocer la clase del método a la
10que hace referencia; lo único que importa es que el método al que se hace referencia tenga los mismos
11parámetros y el mismo tipo de valor devuelto que el delegado.

121.12 Atributos
13Los tipos, miembros y otras entidades de un programa de C# admiten modificadores que controlan ciertos
14aspectos de su comportamiento. Por ejemplo, la accesibilidad de un método se controla utilizando los
15modificadores public, protected, internal y private. C# generaliza esta función de tal forma que los tipos
16definidos por el usuario de información declarativa puedan agregarse a las entidades del programa y ser
17recuperados en tiempo de ejecución. Los programas especifican esta información declarativa adicional
18definiendo y utilizando atributos.
19El ejemplo siguiente declara un atributo HelpAttribute que se puede colocar en entidades de programa para
20proporcionar vínculos a la documentación asociada.

21 using System;
22 public class HelpAttribute: Attribute

23 {

24 string url;
26 public HelpAttribute(string url) {
25
27 this.url = url;

28
29 public string Url {

30 get { return url; }

31
32 public string Topic {

33 get { return topic; }

34 set { topic = value; }


37Todas las clases de atributo derivan de la clase base System.Attribute proporcionada por .NET Framework. Si
35
38el nombre de un atributo termina en Attribute, se puede omitir dicha parte del nombre al hacer referencia al
36atributo. Por ejemplo, el atributo HelpAttribute se puede utilizar del siguiente modo.
39

40 [Help("http://msdn.microsoft.com/.../MyClass.htm")]

41 public class Widget

42 {

43 [Help("http://msdn.microsoft.com/.../MyClass.htm", Topic = "Display")]

44
45
9736 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
98 Capítulo 18 Código no seguro

1Este ejemplo asocia un He lpAt t r i bu te


a la clase Widget y otro HelpAttribute al método Display de la clase.
2Los constructores públicos de una clase de atributo controlan la información que debe proporcionarse al asociar
3el atributo a una entidad de programa. Puede proporcionarse información adicional haciendo referencia a las
4propiedades públicas de lectura y escritura de la clase de atributo (como la referencia previa a la propiedad
5Topic).
6El ejemplo siguiente muestra cómo utilizar la reflexión para recuperar en tiempo de ejecución la información de
7atributos para una entidad determinada del programa.

8 using System;

9 using System.Reflection;
10 class Test

11 {

12 static void ShowHelp(MemberInfo member) {

13 HelpAttribute a = Attribute.GetCustomAttribute(member,

14 typeof(HelpAttribute)) as HelpAttribute;

15 if (a == null) {

16 Console.WriteLine("No help for {0}", member);

17 }
23
18 static void Main() {

24
19 ShowHelp(typeof(Widget));

25
20 ShowHelp(typeof(Widget).GetMethod("Display"));
28
26
21Cuando se solicita un atributo determinado a través de la reflexión, se invoca al constructor para la clase de
29atributo con la información proporcionada en el código fuente del programa y se devuelve la instancia de
27atributo resultante. Si la información adicional se proporciona a través de propiedades, dichas propiedades se
30
22
31establecen en los valores dados antes de que se devuelva la instancia de atributo.

99Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 37


100Especificación del lenguaje C#

1 2.Estructura léxica

22.1 Programas
3Un programa de C# consta de uno o varios archivos de código fuente, formalmente conocidos como unidades
4de compilación (§9.1). Un archivo de código fuente es una secuencia ordenada de caracteres Unicode. Los
5archivos de código fuente normalmente tienen una correspondencia de uno a uno con los archivos de un sistema
6de archivos, pero esta correspondencia no es necesaria. Para maximizar la portabilidad, se recomienda utilizar la
7codificación UTF-8 para los archivos de un sistema de archivos.
8En términos conceptuales, un programa se compila en tres fases:
91. Transformación, que convierte un archivo, a partir de un repertorio de caracteres y un esquema de
10 codificación concretos, en una secuencia de caracteres Unicode.
112. Análisis léxico, que convierte una secuencia de caracteres de entrada Unicode en una secuencia de símbolos
12 (token).
133. Análisis sintáctico, que convierte la secuencia de símbolos en código ejecutable.

142.2 Gramáticas
15Esta especificación presenta la sintaxis del lenguaje de programación C# mediante el uso de dos gramáticas. La
16gramática léxica (§2.2.2) define cómo se combinan los caracteres Unicode para formar terminadores de línea,
17espacios en blanco, comentarios, símbolos (tokens) y directivas de preprocesamiento. La gramática sintáctica
18(§2.2.3) define cómo se combinan los símbolos resultantes de la gramática léxica para formar programas de C#.

192.2.1 Notación gramatical


20Las gramáticas léxica y sintáctica se presentan mediante producciones gramaticales. Cada producción
21gramatical define un símbolo no terminal y las posibles expansiones de dicho símbolo en secuencias de
22símbolos no terminales o terminales. En las producciones gramaticales, los símbolos no terminales se muestran
23en cursiva, y los símbolos te rm ina lse muestran en una fuente de ancho fijo.
24La primera línea de una producción gramatical es el nombre del símbolo no terminal que se define, seguido de
25un carácter de dos puntos. Cada línea con sangría sucesiva contiene una expansión posible del símbolo no
26terminal dado como una secuencia de símbolos no terminales o terminales. Por ejemplo, la producción:
27 while-statement:
28 whi l e ( boolean-expression ) embedded-statement
29define una instrucción while (while-statement) formada por el símbolo (token) whi l e, seguida del símbolo “(”,
30de una expresión booleana (boolean-expression), del símbolo “)” y de una instrucción incrustada (embedded-
31statement).
32Cuando hay más de una expansión posible de un símbolo no terminal, las alternativas se muestran en líneas
33individuales. Por ejemplo, la producción:
34 statement-list:
35 statement
36 statement-list statement

10138 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


102 Capítulo 18 Código no seguro

1define una lista de instrucciones (statement-list) que consta de una instrucción (statement) o de una lista de
2instrucciones (statement-list) seguida de una instrucción (statement). Es decir, la definición es recursiva y
3especifica que una lista de instrucciones consta de una o varias instrucciones.
4Se utiliza un sufijo en subíndice “opt” para indicar un símbolo opcional. La producción:
5 block:
6 { statement-listopt }
7es una forma abreviada de:
8 block:
9 { }
10 { statement-list }
11y define un bloque (block) compuesto por una lista de instrucciones (statement-list) opcional entre los símbolos
12“{” y “}”.
13Las alternativas normalmente se enumeran en líneas distintas, aunque en los casos en los que hay varias
14alternativas, el texto “una de las siguientes” puede preceder a una lista de expansiones proporcionadas en la
15misma línea. Esto es sencillamente una forma abreviada de mostrar todas las alternativas en líneas distintas. Por
16ejemplo, la producción:
17 real-type-suffix: uno de los siguientes
18 F f D d M m
19es una forma abreviada de:
20 real-type-suffix:
21 F
22 f
23 D
24 d
25 M
26 m

272.2.2 Gramática léxica


28La gramática léxica de C# se presenta en §2.3, §2.3.3 y §2.5. Los símbolos terminales de la gramática léxica son
29los caracteres del juego de caracteres Unicode, y la gramática léxica especifica cómo se combinan los caracteres
30para formar tokens (§2.4), espacios en blanco (§2.3.3), comentarios (§2.3.2) y directivas de preprocesamiento
31(§2.5).
32Todos los archivos de código fuente de un programa de C# deben ajustarse a la producción entrada (input) de la
33gramática léxica (§2.3).

342.2.3 Gramática sintáctica


35La gramática sintáctica de C# se presenta en los capítulos y apéndices que siguen a este capítulo. Los símbolos
36terminales de la gramática sintáctica son los tokens definidos por la gramática léxica, mientras la gramática
37sintáctica es la que especifica cómo se combinan los símbolos para formar programas de C#.
38Todos los archivos de código fuente de un programa de C# deben cumplir la producción unidad de compilación
39(compilation-unit) de la gramática sintáctica (§9.1).

103Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 39


104Especificación del lenguaje C#

12.3 Análisis léxico


2La producción entrada (input) define la estructura léxica de un archivo de código fuente de C#. Un archivo de
3código fuente de un programa de C# debe ajustarse a esta producción de la gramática léxica.
4 input:
5 input-sectionopt
6 input-section:
7 input-section-part
8 input-section input-section-part
9 input-section-part:
10 input-elementsopt new-line
11 pp-directive
12 input-elements:
13 input-element
14 input-elements input-element
15 input-element:
16 whitespace
17 comment
18 token
19Cinco elementos básicos constituyen la estructura léxica de un archivo de código fuente de C#: los terminadores
20de línea (§2.3.1), el espacio en blanco (§2.3.3), los comentarios (§2.3.2), los símbolos (token) (§2.4) y las
21directivas de preprocesamiento (§2.5). De estos elementos básicos, solamente los tokens son significativos en la
22gramática sintáctica de un programa de C# (§2.2.3).
23El procesamiento léxico de un archivo de código fuente de C# consiste en reducir el archivo a una secuencia de
24símbolos para formar la entrada del análisis sintáctico. Los terminadores de línea, el espacio en blanco y los
25comentarios pueden corresponder a símbolos diferentes, y las directivas de preprocesamiento pueden hacer que
26se omitan secciones del archivo de código fuente, pero por lo demás estos elementos léxicos no tienen
27repercusiones en la estructura sintáctica de un programa de C#.
28Cuando varias producciones de la gramática léxica coinciden con una secuencia de caracteres de un archivo de
29código fuente, el procesamiento léxico siempre forma el elemento léxico más largo posible. Por ejemplo, la
30secuencia de caracteres / /se procesa como el principio de un comentario en una sola línea, porque este elemento
31léxico es más largo que un símbolo de una sola /.

322.3.1 Terminadores de línea


33Los terminadores de línea dividen los caracteres de un archivo de código fuente de C# en líneas.
34 new-line:
35 Carácter de retorno de carro (U+000D )
36 Carácter de salto de línea (U+000A )
37 Carácter de retorno de carro (U+000D) seguido de un carácter de salto de línea (U+000A)
38 Carácter de línea siguiente (U+0085)
39 Carácter separador de línea (U+2028)
40 Carácter separador de párrafo (U+2029)
41Por compatibilidad con las herramientas de edición de código fuente que agregan marcadores de fin de archivo,
42y para permitir que un archivo de código fuente pueda verse como una secuencia de líneas terminadas
43correctamente, se aplican las siguientes transformaciones, por orden, a todos los archivos de código fuente de un
44programa de C#:

10540 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


106 Capítulo 18 Código no seguro

1• Si el último carácter del archivo de código fuente es un carácter Control-Z (U+001A ), el carácter se
2 elimina.
3• Al final del archivo de código de fuente se agrega un carácter de retorno de carro (U+000D ) si este archivo
4 no está vacío y si su último carácter no es un retorno de carro (U+000D ), un salto de línea (U+000A), un
5 separador de línea (U+2028) o un separador de párrafo (U+2029D).

62.3.2 Comentarios
7Se aceptan dos formas de comentarios: comentarios en una línea y comentarios delimitados. Los comentarios
8de una sola línea empiezan con los caracteres // y se extienden hasta el final de la línea de código fuente. Los
9comentarios delimitados empiezan con los caracteres /* y terminan con los caracteres */. Estos comentarios
10pueden estar en varias líneas.
11 comment:
12 single-line-comment
13 delimited-comment
14 single-line-comment:
15 / / input-charactersopt
16 input-characters:
17 input-character
18 input-characters input-character
19 input-character:
20 Cualquier carácter Unicode salvo un carácter de nueva línea (new-line-character)
21 new-line-character:
22 Carácter de retorno de carro (U+000D )
23 Carácter de salto de línea (U+000A )
24 Carácter de línea siguiente (U+0085)
25 Carácter separador de línea (U+2028)
26 Carácter separador de párrafo (U+2029)
27 delimited-comment::
28 /* delimited-comment-textopt asterisks /
29 delimited-comment-text::
30 delimited-comment-section
31 delimited-comment-text delimited-comment-section
32 delimited-comment-section::
33 not-asterisk
34 asterisks not-slash
35 asterisks::
36 *
37 asterisks *
38 not-asterisk:
39 Cualquier carácter Unicode excepto *
40 not-slash:
41 Cualquier carácter Unicode excepto /
42Los comentarios no pueden anidarse. Las secuencias de caracteres /* y */ no tienen ningún significado especial
43dentro de un comentario //, y las secuencias de caracteres// y /* no tienen ningún significado especial dentro de
44un comentario delimitado.

107Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 41


108Especificación del lenguaje C#

1Los comentarios no se procesan dentro de literales de carácter y de cadena.


2En el ejemplo

3 /* Hello, world program

4 This program writes “hello, world” to the console

5 */

6 class Hello

7 {

128incluye un comentario
static void Main() {
delimitado.
139En el ejemplo
10
14 // Hello, world program
11
15 // This program writes “hello, world” to the console

16 //

17 class Hello // any name will do for this class

18 {

19muestra varios
23 static void en
comentarios Main() { // this method must be named "Main"
una línea.
20
242.3.3 Espacio en blanco
21
25Espacio en blanco se define como cualquier carácter con la clase Unicode Zs (que incluye el carácter de espacio)
26
22así como el carácter de tabulación horizontal, el carácter de tabulación vertical y el carácter de avance de página.
27 whitespace:
28 Cualquier carácter con clase Unicode Zs
29 Carácter de tabulación horizontal (U+0009 )
30 Carácter de tabulación vertical (U+000B )
31 Carácter de avance de página (U+000C)

322.4 Símbolos
33Existen varias clases de símbolos (token): identificadores, palabras clave, literales, operadores y signos de
34puntuación. Los espacios en blanco y los comentarios no son símbolos (token), si bien actúan como separadores
35entre ellos.
36 token:
37 identifier
38 keyword
39 integer-literal
40 real-literal
41 character-literal
42 string-literal
43 operator-or-punctuator

10942 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


110 Capítulo 18 Código no seguro

12.4.1 Secuencias de escape de caracteres Unicode


2Una secuencia de escape de caracteres Unicode representa un carácter Unicode. Las secuencias de escape de
3carácter Unicode se procesan en identificadores (§2.4.2), literales de carácter (§2.4.4.4) y literales de cadena
4normales (§2.4.4.5). Un carácter de escape Unicode no se procesa en ninguna otra ubicación (por ejemplo, para
5formar un operador, un signo de puntuación o una palabra clave).
6 unicode-escape-sequence:
7 \ u hex-digit hex-digit hex-digit hex-digit
8 \U hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit
9Una secuencia de escape Unicode representa el carácter Unicode único formado por el número hexadecimal que
10sigue a los caracteres “\ u” o “\U”. Dado que C# usa una codificación de 16 bits de los puntos de código Unicode
11en caracteres y valores de cadena, un carácter Unicode en el intervalo de U+10000 a U+10FFFF no está
12permitido en un literal de caracteres y se representa mediante un par Unicode suplente en un literal de cadena.
13Los caracteres Unicode con puntos de código por encima de 0x10FFFF no se aceptan.
14No se llevan a cabo conversiones múltiples. Por ejemplo, el literal de la cadena “\ u005Cu005C ” equivale a
15“\ u005C” y no a “\”. El valor Unicode \u005C es el carácter “\”.
16En el ejemplo

17 class Class1

18 {

19 static void Test(bool \u0066) {

20 char c = '\u0066';

21 if (\u0066)
25muestra varios usos de \u0066, que es la secuencia de escape de la letra “f”. El programa equivale a
22
26
23 class Class1

27
24 {

28 static void Test(bool f) {

29 char c = 'f';

30 if (f)

312.4.2 Identifiers
34
35
32Las reglas de los identificadores explicadas en esta sección se corresponden exactamente con las recomendadas
36por la norma Unicode Anexo 15, a excepción del carácter de subrayado, que está permitido como carácter inicial
33
37(como era tradicional en el lenguaje de programación C). Las secuencias de escape Unicode están permitidas en
38los identificadores y el carácter “@” está permitido como prefijo para habilitar el uso de palabras clave como
39identificadores.
40 identifier:
41 available-identifier
42 @ identifier-or-keyword
43 available-identifier:
44 Un elemento identifier-or-keyword que no sea keyword

111Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 43


112Especificación del lenguaje C#

1 identifier-or-keyword:
2 identifier-start-character identifier-part-charactersopt
3 identifier-start-character:
4 letter-character
5 _ (carácter de subrayado U+005F )
6 identifier-part-characters:
7 identifier-part-character
8 identifier-part-characters identifier-part-character
9 identifier-part-character:
10 letter-character
11 decimal-digit-character
12 connecting-character
13 combining-character
14 formatting-character
15 letter-character:
16 Un carácter Unicode de clases Lu, Ll, Lt, Lm, Lo, o Nl
17 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de clases
18 Lu, Ll, Lt, Lm, Lo o Nl
19 combining-character:
20 Un carácter Unicode de clases Mn o Mc
21 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de clases
22 Mn o Mc
23 decimal-digit-character:
24 Un carácter Unicode de la clase Nd
25 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de la clase
26 Nd
27 connecting-character:
28 Un carácter Unicode de la clase Pc
29 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de la clase
30 Pc
31 formatting-character:
32 Un carácter Unicode de la clase Cf
33 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de la clase
34 Cf
35Para obtener más información sobre las anteriores clases de caracteres, consulte la sección 4.5 de la versión 3.0
36de la norma Unicode (Unicode Standard, Version 3.0).
37Entre los ejemplos de identificadores válidos se incluyen “i den t i f i e”,r1“_ iden t i f i e”,r2y “@if”.
38Los identificadores incluidos en programas según la norma deberán seguir el formato canónico definido en el
39documento Unicode Normalization Form C (anexo 15 de la norma Unicode). El comportamiento ante
40identificadores que no se ajusten a dicho formato dependerá de la implementación; no obstante, no serán
41necesarios diagnósticos.
42El prefijo “@” habilita el uso de palabras clave como identificadores, lo cual resulta útil cuando se interactúa
43con otros lenguajes de programación. El carácter @ en realidad no forma parte del identificador, por lo que el
44identificador podría considerarse en otros lenguajes como un identificador normal, sin el prefijo. Los
45identificadores con prefijo @ se conocen como identificadores textuales. El uso del prefijo @ para los
46identificadores que no son palabras clave está permitido, pero no se recomienda por cuestión de estilo.

11344 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


114 Capítulo 18 Código no seguro

1En el ejemplo:

2 class @class

3 {

4 public static void @static(bool @bool) {

5 if (@bool)

6 System.Console.WriteLine("true");

7 else
11 class Class1
8
12 {
9
13 static void M() {
10
14 cl\u0061ss.st\u0061tic(true);
17define una clase denominada “c lass” con un método estático llamado “s ta t i”c que toma un parámetro
15
18denominado “bool”. Tenga en cuenta que, dado que los escapes de Unicode no están permitidos en las palabras
19
16clave, el símbolo “cl\u0061ss” es un identificador idéntico a “@class”.
20Dos identificadores se consideran el mismo si son idénticos después de aplicarse las siguientes
21transformaciones, por orden:
22• El prefijo “@”, si aparece, será eliminado.
23• Cada secuencia de escape Unicode (unicode-escape-sequence) se transformará en su carácter Unicode
24 correspondiente.
25• Todos los caracteres de formato (formatting-characters) serán eliminados.
26Los identificadores que contienen dos caracteres de subrayado (U+005F) consecutivos se reservan para uso de
27la implementación. Por ejemplo, una implementación podría proporcionar palabras clave extendidas que
28empiecen por dos caracteres de subrayado.

292.4.3 Palabras clave


30Una palabra clave es una secuencia de caracteres similar a un identificador que está reservada y no puede
31utilizarse como identificador excepto cuando tiene como prefijo el carácter @.

115Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 45


116Especificación del lenguaje C#

1 keyword: una de las siguientes


2 abs t rac t as base boo l break
3 byte case ca tch char checked
4 c lass cons t cont inue dec ima l de fau l t
5 de legate do doub le e l se enum
6 event exp l i c i t exte rn f a l se f i na l l y
7 f i xed f l oa t fo r f o reach goto
8 if imp l i c i t in int i n te r face
9 i n te rna l is l ock l ong namespace
10 new nu l l ob jec t opera to r out
11 ove r r i de params pr i va te pro tec tedpub l i c
12 readon ly re f re tu rn sby te sea led
13 shor t s i zeo f s tacka l l oc
s ta t i c s t r i ng
14 s t ruc t sw i t ch th i s th row t rue
15 t ry t ypeo f u in t u long unchecked
16 unsa fe ushor t us ing v i r tua l vo id
17 vo la t i l e whi l e
18En algunos lugares de la gramática, hay identificadores específicos con significados especiales, pero no son
19palabras clave. Por ejemplo, en una declaración de propiedad, los identificadores “get” y “se t” tienen un
20significado especial (§10.6.2). En estas ubicaciones no se permite usar identificadores distintos de get o set,
21por lo que este uso no presenta conflictos si se utilizan estas palabras como identificadores.

222.4.4 Literales
23Un literal es una representación en código fuente de un valor.
24 literal:
25 boolean-literal
26 integer-literal
27 real-literal
28 character-literal
29 string-literal
30 null-literal

312.4.4.1 Literales booleanos


32Existen dos valores literales booleanos: true y false.
33 boolean-literal:
34 t rue
35 f a l se
36El tipo de un literal booleano (boolean-literal) es boo l.

372.4.4.2 Literales enteros


38Los literales enteros permiten escribir valores de los tipos int, uint, long, y ulong. Los literales enteros tienen
39dos formatos posibles: decimal y hexadecimal.
40 integer-literal:
41 decimal-integer-literal
42 hexadecimal-integer-literal
43 decimal-integer-literal:
44 decimal-digits integer-type-suffixopt

11746 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


118 Capítulo 18 Código no seguro

1 decimal-digits:
2 decimal-digit
3 decimal-digits decimal-digit
4 decimal-digit: uno de los siguientes
5 0 1 2 3 4 5 6 7 8 9
6 integer-type-suffix: uno de los siguientes
7 U u L l UL Ul uL ul LU Lu lU lu
8 hexadecimal-integer-literal:
9 0x hex-digits integer-type-suffixopt
10 0X hex-digits integer-type-suffixopt
11 hex-digits:
12 hex-digit
13 hex-digits hex-digit
14 hex-digit: uno de los siguientes
15 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f
16El tipo de un literal entero se determina como sigue:
17• Si el literal no tiene sufijo, su tipo es el primero de los tipos en los cuales se puede representar su valor: i n t,
18 u in t, long, ulong.
19• Si el literal tiene el sufijo U o u, su tipo es el primero de los tipos en los cuales se puede representar su
20 valor: uint, ulong.
21• Si el literal tiene el sufijo L o l, su tipo es el primero de los tipos en los cuales se puede representar su valor:
22 long, ulong.
23• Si el literal tiene el sufijo UL, Ul, uL, ul, LU, Lu, lU, o lu, es de tipo ulong.
24Si el valor representado por un literal entero está fuera del intervalo de valores del tipo ulong, se produce un
25error en tiempo de compilación.
26Por cuestiones de estilo, a la hora de escribir literales de tipo long se recomienda el uso de “L” en lugar de “l”
27para evitar la fácil confusión de la letra “l” con el dígito “1”.
28Para que el número de valores int y long escritos como literales enteros decimales sea el mínimo posible,
29existen las dos reglas siguientes:
30• Cuando aparece un literal entero decimal (decimal-integer-literal) con el valor 2147483648 (231) y sin sufijo
31 de tipo entero (integer-type-suffix) como el símbolo (token) inmediatamente posterior a un símbolo de
32 operador unario menos (§7.6.2), el resultado es una constante de tipo int con el valor −2147483648 (−231).
33 En todas las demás situaciones, un literal entero decimal (decimal-integer-literal) de estas características es
34 de tipo uint.
35• Cuando aparece un literal entero decimal (decimal-integer-literal) con el valor 9223372036854775808 (263)
36 y sin sufijo de tipo entero (integer-type-suffix) o con el sufijo de tipo entero L o l como símbolo
37 inmediatamente posterior de un símbolo de operador unario menos (§7.6.2), el resultado es una constante de
38 tipo long con el valor −9223372036854775808 (−263). En todas las demás situaciones, un literal entero
39 decimal (decimal-integer-literal) de estas características es de tipo ulong.

402.4.4.3 Literales reales


41Los literales reales permiten escribir valores de los tipos float, double y decimal.

119Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 47


120Especificación del lenguaje C#

1 real-literal:
2 decimal-digits . decimal-digits exponent-partopt real-type-suffixopt
3 . decimal-digits exponent-partopt real-type-suffixopt
4 decimal-digits exponent-part real-type-suffixopt
5 decimal-digits real-type-suffix
6 exponent-part:
7 e signopt decimal-digits
8 E signopt decimal-digits
9 sign: uno de los siguientes
10 + -
11 real-type-suffix: uno de los siguientes
12 F f D d M m
13Si no se especifica el sufijo de tipo real (real-type-suffix ), el tipo del literal real es doub le. De lo contrario, el
14sufijo de tipo real determina el tipo del literal real, como sigue:
15• Un literal real con el sufijo F o f es de tipo float. Por ejemplo, los literales 1f, 1.5f, 1e10f y 123.456F son
16 de tipo float.
17• Un literal real con el sufijo D o d es de tipo double. Por ejemplo, los literales 1d, 1.5d, 1e10d y
18 123.456D son de tipo double.
19• Un literal real con el sufijo M o m es de tipo decimal. Por ejemplo, los literales 1m, 1.5m, 1e10m y
20 123.456M son de tipo decimal. Este literal se convierte en un valor decimal tomando el valor exacto y, si
21 es necesario, redondeando al valor más cercano que se pueda representar mediante el redondeo de banca
22 (§4.1.7). Se conserva cualquier escala que se detecte en el literal, salvo que el valor esté redondeado o sea
23 cero (en este caso, el signo y la escala son 0). Por lo tanto, el análisis del literal 2.900m se interpreta para
24 formar un decimal con signo 0, coeficiente 2900 y escala 3.
25Si el literal especificado no puede representarse en el tipo indicado, se produce un error en tiempo de
26compilación.
27El valor de un literal real de tipo f l oa to doub le se determina mediante el uso del modo “redondeo al más
28cercano” de IEEE.
29Tenga en cuenta que, en un literal real, siempre son necesarios dígitos decimales tras el punto decimal. Por
30ejemplo, 1 .3F es un literal real pero 1 .F no lo es.

312.4.4.4 Literales de carácter


32Un literal de carácter representa un carácter único y normalmente está compuesto por un carácter entre comillas,
33como por ejemplo ' a .'
34 character-literal:
35 ' character '
36 character:
37 single-character
38 simple-escape-sequence
39 hexadecimal-escape-sequence
40 unicode-escape-sequence
41 single-character:
42 Cualquier carácter excepto ' (U+0027 ), \ (U+005C), y carácter de nueva línea (new-line-
43 character)

12148 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


122 Capítulo 18 Código no seguro

1 simple-escape-sequence: una de las siguientes


2 \' \" \\ \0 \a \b \f \n \r \t \v
3 hexadecimal-escape-sequence:
4 \ x hex-digit hex-digitopt hex-digitopt hex-digitopt
5Todo carácter que siga a un carácter de barra diagonal (\) en un carácter (character) debe ser uno de los
6siguientes: ', ", \, 0, a, b, f, n, r, t, u, U, x, v. En caso contrario, se producirá un error en tiempo de compilación.
7Una secuencia de escape hexadecimal representa un solo carácter Unicode cuyo valor está formado por el
8número hexadecimal que siga a “\x”.
9Si el valor representado por un literal de carácter es mayor que U+FFFF, se produce un error en tiempo de
10compilación.
11Una secuencia de escape de caracteres Unicode (§2.4.1) en un literal de caracteres debe estar comprendido en el
12intervalo de U+0000 a U+FFFF.
13Una secuencia de escape sencilla representa una codificación de caracteres Unicode, como se describe en la
14tabla inferior.
15

Secuencia de Nombre del Codificación


escape carácter Unicode
\' Comilla simple 0x0027
\" Comilla doble 0x0022
\\ Barra invertida 0x005C
\0 Nulo 0x0000
\a Alerta 0x0007
\b Retroceso 0x0008
\f Avance de 0x000C
página
\n Nueva línea 0x000A
\r Retorno de carro 0x000D
\t Tabulación 0x0009
horizontal
\v Tabulación 0x000B
vertical
16
17El tipo de un literal de caracteres (character-literal) es char.

182.4.4.5 Literales de cadena


19C# admite dos formatos de literales de cadena: literales de cadena regulares y literales de cadena textuales.
20Un literal de cadena regular consta de cero o más caracteres entre comillas dobles (como en "hello") y puede
21incluir secuencias de escape sencillas (como \t para el carácter de tabulación) y secuencias de escape
22hexadecimales y Unicode.
23Un literal de cadena textual consta de un carácter @ seguido de un carácter de comillas dobles, cero o más
24caracteres y un carácter de comillas dobles de cierre. Un ejemplo sencillo puede ser @"he l l o ". En un literal de

123Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 49


124Especificación del lenguaje C#

1cadena textual, los caracteres comprendidos entre los delimitadores se interpretan de manera literal, siendo la
2única excepción una secuencia de escape comillas (quote-escape-sequence). En concreto, las secuencias de
3escape sencillas, las secuencias de escape hexadecimales y Unicode no se procesan en los literales de cadena
4textuales. Un literal de cadena textual puede estar en varias líneas.
5 string-literal:
6 regular-string-literal
7 verbatim-string-literal
8 regular-string-literal:
9 " regular-string-literal-charactersopt "
10 regular-string-literal-characters:
11 regular-string-literal-character
12 regular-string-literal-characters regular-string-literal-character
13 regular-string-literal-character:
14 single-regular-string-literal-character
15 simple-escape-sequence
16 hexadecimal-escape-sequence
17 unicode-escape-sequence
18 single-regular-string-literal-character:
19 Cualquier carácter excepto " (U+0022 ), \ (U+005C), y un carácter de nueva línea (new-line-
20 character)
21 verbatim-string-literal:
22 @" verbatim -string-literal-charactersopt "
23 verbatim-string-literal-characters:
24 verbatim-string-literal-character
25 verbatim-string-literal-characters verbatim-string-literal-character
26 verbatim-string-literal-character:
27 single-verbatim-string-literal-character
28 quote-escape-sequence
29 single-verbatim-string-literal-character:
30 Cualquier carácter excepto "
31 quote-escape-sequence:
32 ""
33Todo carácter que siga a un carácter de barra diagonal (\) en un carácter literal de cadena regular (regular-string-
34literal-character) debe ser uno de los siguientes: ', ", \, 0, a, b, f, n, r, t, u, U, x, v. En caso contrario, se
35producirá un error en tiempo de compilación.
36En el ejemplo
37 s t r i ng a = "he l l o , wor ld" ; / / he l l o , wor ld
38 s t r i ng b = @"he l l o , wor ld" ; / / he l l o , wor ld
39 s t r i ng c = "he l l o \ t wor ld" ; / / he l l o wor ld
40 s t r i ng d = @"he l l o \ t wor ld" ; / / he l l o \ t wor ld
41 s t r i ng e = " J oe sa id \ "He l l o \ " to me" ; / / J oe sa id "He l l o " to me
42 s t r i ng f = @"Joe sa id " "He l l o " " to me" ; / / J oe sa id "He l l o " to me
43 s t r i ng g = " \ \ \ \ se rve r \ \ sha re \ \ f i l e . t x t " ; / / \ \ se rve r \ sha re \ f i l e . t x t
44 s t r i ng h = @"\ \ se rve r \ sha re \ f i l e . t x t " ; / / \ \ se rve r \ sha re \ f i l e . t x t

12550 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


126 Capítulo 18 Código no seguro

1 string i = "one\r\ntwo\r\nthree";

2 string j = @"one

3 two
5muestra varios literales de cadena. El último literal de cadena, j, es un literal de cadena textual que ocupa varias
64líneas. Los caracteres encerrados entre comillas, incluidos los espacios en blanco (por ejemplo, caracteres de
7nueva línea), se conservan literalmente.
8Puesto que una secuencia de escape hexadecimal puede tener un número variable de dígitos hexadecimales, el
9literal de cadena " \ x123"contiene un solo carácter con un valor hexadecimal de 123. Para crear una cadena que
10contenga el carácter con el valor hexadecimal 12 seguido del carácter 3, se podría escribir " \ x00123"o "\x12"
11+ "3" en su lugar.
12El tipo de un literal de cadena (string-literal) es string.
13Cada literal de cadena no genera necesariamente una nueva instancia de cadena. Cuando aparecen dos o más
14literales de cadena equivalentes según el operador de igualdad de cadenas (§7.9.7) en el mismo programa, estos
15literales se refieren a la misma instancia de cadena. Por ejemplo, el resultado del programa

16 class Test

17 {

18 static void Main() {

19 object a = "hello";

20 object b = "hello";
24es True porque los dos literales hacen referencia a la misma instancia de cadena.
21
25
222.4.4.6 El literal null
26
23 null-literal:
27 nu l l
28El tipo de un literal nulo (null-literal) es el tipo null.

292.4.5 Operadores y signos de puntuación


30Existen varias clases de operadores y signos de puntuación. Los operadores se utilizan en las expresiones para
31describir operaciones que implican uno o más operandos. Por ejemplo, la expresión a + b usa el operador +
32para sumar los dos operandos, a y b. Los signos de puntuación permiten agrupar y separar.
33 operator-or-punctuator: uno de los siguientes
34 { } [ ] ( ) . , : ;
35 + - * / % & | ^ ! ~
36 = < > ? ++ -- && || << >>
37 == != <= >= += -= *= /= %= &=
38 |= ^= <<= >>= ->

392.5 Directivas de preprocesamiento


40Las directivas de preprocesamiento ofrecen la capacidad de omitir condicionalmente secciones de los archivos
41de código fuente, con el fin de notificar errores y advertencias, así como de delimitar regiones características del
42código fuente. El término “directivas de preprocesamiento” se utiliza por motivos de coherencia con los
43lenguajes de programación C y C++. En C#, no existe un paso de preprocesamiento individual; las directivas de
44preprocesamiento se procesan como parte de la fase de análisis léxico.

127Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 51


128Especificación del lenguaje C#

1 pp-directive:
2 pp-declaration
3 pp-conditional
4 pp-line
5 pp-diagnostic
6 pp-region
7A continuación se indican las directivas de preprocesamiento disponibles:
8• #def ine y #unde f , que permiten definir y anular, respectivamente, símbolos de compilación condicional
9 (§2.5.3).
10• #i f, #el i ,f #else, y #endif, para omitir de forma condicional secciones de archivos de código fuente
11 (§2.5.4).
12• #line, que permite controlar números de línea de errores y advertencias (§2.5.7).

13• #error y #warning, que permiten emitir errores y advertencias, respectivamente (§2.5.5).

14• #region y #endregion, para marcar de forma explícita secciones del código fuente (§2.5.6).
15Una directiva de preprocesamiento siempre ocupa una línea independiente del código fuente y siempre empieza
16por un carácter # y un nombre de directiva de preprocesamiento. Puede haber un espacio en blanco antes del
17carácter # y entre éste y el nombre de la directiva.
18Una línea del código fuente que contiene una directiva #define, #undef, #if, #elif, #else, #endif o #line
19puede terminar con un comentario de una sola línea. Los comentarios delimitados (el estilo de comentarios /*
20*/ ) no están permitidos en las líneas de código fuente que contienen directivas de preprocesamiento.
21Las directivas de preprocesamiento no son símbolos (tokens) y no forman parte de la gramática sintáctica de C#.
22No obstante, las directivas de preprocesamiento pueden utilizarse para incluir o excluir secuencias de símbolos
23y, de esta forma, pueden afectar al significado de un programa de C#. Por ejemplo, una vez compilado, el
24programa:

25 #define A

26 #undef B
27 class C

28 {

29 #if A

30 void F() {}

31 #else
34 #if B
32
35 void H() {}
33
36 #else

37 void I() {}
40produce como resultado exactamente la misma secuencia de símbolos que el programa:
38
41 class C
39
42 {

43 void F() {}

44
4552
129 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
130 Capítulo 18 Código no seguro

1Por lo tanto, aunque los dos programas sean muy diferentes léxicamente, sintácticamente son idénticos.

22.5.1 Símbolos de compilación condicional


3La compilación condicional que suministran funcionalmente las directivas #i f, #e l i ,f #else, y #endif se
4controla mediante expresiones de preprocesamiento (§2.5.2) y símbolos de compilación condicional.
5 conditional-symbol:
6 Cualquier elemento identifier-or-keyword excepto t rueo f a l se
7Un símbolo de compilación condicional tiene dos estados posibles: definido o no definido. Al principio del
8procesamiento léxico de un archivo de código fuente, un símbolo de compilación condicional tiene el estado no
9definido, a menos que haya sido definido de forma explícita por un mecanismo externo (como una opción del
10compilador de la línea de comandos). Cuando se procesa una directiva #define, el símbolo de compilación
11condicional nombrado en la directiva queda definido en el archivo de código fuente. El símbolo permanece
12definido hasta que se procesa una directiva #undef del mismo símbolo o hasta que se llega al final del archivo
13de código fuente. Una de las implicaciones es que las directivas #define y #undef de un archivo de código
14fuente no surten efecto en otros archivos de código fuente del mismo programa.
15Cuando se hace referencia a un símbolo de compilación condicional definido en una expresión de
16preprocesamiento, éste adquiere el valor booleano true, y un símbolo de compilación condicional no definido
17tiene el valor booleano false. No es necesario que los símbolos de compilación condicional se declaren
18explícitamente antes de que se haga referencia a ellos en expresiones de preprocesamiento. En lugar de ello, los
19símbolos no declarados simplemente no se definen y, por lo tanto, tienen el valor false.
20El espacio de nombres de los símbolos de compilación condicional es único y exclusivo de todas las demás
21entidades con nombre de un programa de C#. Sólo puede hacerse referencia a los símbolos de compilación
22condicional en directivas #define y #undef y en expresiones de preprocesamiento.

232.5.2 Expresiones de preprocesamiento


24Las expresiones de preprocesamiento pueden encontrarse en las directivas #if y #elif. En las expresiones de
25preprocesamiento se permiten los operadores !, ==, !=, && y ||, además de los paréntesis, empleados para
26agrupar.
27 pp-expression:
28 whitespaceopt pp-or-expression whitespaceopt
29 pp-or-expression:
30 pp-and-expression
31 pp-or-expression whitespaceopt | | whitespaceopt pp-and-expression
32 pp-and-expression:
33 pp-equality-expression
34 pp-and-expression whitespaceopt && whitespaceopt pp-equality-expression
35 pp-equality-expression:
36 pp-unary-expression
37 pp-equality-expression whitespaceopt == whitespaceopt pp-unary-expression
38 pp-equality-expression whitespaceopt != whitespaceopt pp-unary-expression
39 pp-unary-expression:
40 pp-primary-expression
41 ! whitespaceopt pp-unary-expression

131Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 53


132Especificación del lenguaje C#

1 pp-primary-expression:
2 t rue
3 f a l se
4 conditional-symbol
5 ( whitespaceopt pp-expression whitespaceopt )
6Cuando se hace referencia a un símbolo de compilación condicional definido en una expresión de
7preprocesamiento, éste adquiere el valor booleano t rue, y un símbolo de compilación condicional no definido
8tiene el valor booleano f a l se.
9La evaluación de una expresión de preprocesamiento siempre produce un valor booleano. Las reglas de
10evaluación de una expresión de preprocesamiento son las mismas que las de una expresión constante (§7.15),
11excepto en que las únicas entidades definidas por el usuario a las que puede hacerse referencia son símbolos de
12compilación condicional.

132.5.3 Directivas de declaración


14Las directivas de declaración permiten definir o anular la definición de símbolos de compilación condicional.
15 pp-declaration:
16 whitespaceopt # whitespaceopt define whitespace conditional-symbol pp-new-line
17 whitespaceopt # whitespaceopt undef whitespace conditional-symbol pp-new-line
18 pp-new-line:
19 whitespaceopt single-line-commentopt new-line
20El procesamiento de una directiva #def ine causa la definición del símbolo de compilación condicional dado,
21empezando en la línea del código fuente que sigue a la directiva. De igual manera, el procesamiento de una
22directiva #unde f causa la eliminación de la definición del símbolo de compilación condicional dado,
23empezando en la línea del código fuente que sigue a la directiva.
24Todas las directivas #def ine y #unde f de un archivo de código fuente deben aparecer antes del primer símbolo
25(token) (§2.4) en el archivo de código fuente; de lo contrario, se producirá un error en tiempo de compilación.
26En términos intuitivos, las directivas #define y #undef deben preceder a cualquier “código real” en el archivo
27de código fuente.
28En el ejemplo:
29 #def ine Ente rp r i se
30 #i f Pro fess i ona l | | Ente rp r i se
31 #def ine Advanced
32 #end i f
33 namespace Megacorp .Data
34 {
35 #i f Advanced
36 c lass P ivo t Tab le { . . . }
37 #end i f
39
38 es válido porque las directivas #define preceden al primer símbolo (token) (la palabra clave namespace) en
40el archivo de código fuente.
41El siguiente ejemplo daría como resultado un error en tiempo de compilación porque en él hay una directiva
42#define después del código real:

13354 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


134 Capítulo 18 Código no seguro

1 #define A

2 namespace N

3 {

4 #define B

5 #if B
9Puede utilizarse #def ine para definir un símbolo de compilación condicional que ya esté definido, sin
106necesidad de que intervenga ningún símbolo #unde f para ello. El ejemplo siguiente define un símbolo de
117compilación condicional A y después lo define otra vez.

128 #define A

13
14#undef#define A la definición” de un símbolo de compilación condicional que no está definido. En el
puede “anular
15siguiente ejemplo se define un símbolo de compilación condicional A para después eliminar dicha definición dos
16veces; el segundo #undef , pese a no surtir ningún efecto, sigue siendo válido.

17 #define A

18 #undef A

19

135Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 55


136Especificación del lenguaje C#

12.5.4 Directivas de compilación condicional


2Las directivas de compilación condicional permiten incluir o excluir de forma condicional partes de un archivo
3de código fuente.
4 pp-conditional:
5 pp-if-section pp-elif-sectionsopt pp-else-sectionopt pp-endif
6 pp-if-section:
7 whitespaceopt # whitespaceopt i f whitespace pp-expression pp-new-line conditional-
8 sectionopt
9 pp-elif-sections:
10 pp-elif-section
11 pp-elif-sections pp-elif-section
12 pp-elif-section:
13 whitespaceopt # whitespaceopt e l i f whitespace pp-expression pp-new-line conditional-
14 sectionopt
15 pp-else-section:
16 whitespaceopt # whitespaceopt e l se pp-new-line conditional-sectionopt
17 pp-endif:
18 whitespaceopt # whitespaceopt end i f pp-new-line
19 conditional-section:
20 input-section
21 skipped-section
22 skipped-section:
23 skipped-section-part
24 skipped-section skipped-section-part
25 skipped-section-part:
26 skipped-charactersopt new-line
27 pp-directive
28 skipped-characters:
29 whitespaceopt not-number-sign input-charactersopt
30 not-number-sign:
31 Cualquier carácter de entrada (input-character) excepto #
32Como indica la sintaxis, las directivas de compilación condicional deben escribirse como conjuntos formados
33por (en este orden): una directiva #i f , cero o más directivas #el i f, cero o más directivas #else y una directiva
34#endif. Entre las directivas se encuentran secciones condicionales de código fuente. Cada sección está
35controlada por la directiva inmediatamente precedente. Una sección condicional puede contener directivas de
36compilación condicional anidadas a condición de que dichas directivas formen conjuntos completos.
37Una condicional pp (pp-conditional) selecciona como mucho una de las secciones condicionales (conditional-
38section) contenidas para el procesamiento léxico normal:
39• Las expresiones pp (pp-expression) de las directivas #if y #elif se evalúan por orden hasta que una produce
40 un resultado true. Si una expresión produce true, se selecciona la sección condicional (conditional-section)
41 de la directiva correspondiente.
42• Si el resultado de todas las expresiones pp (pp-expression) es false, y si hay presente una directiva #else,
43 se selecciona la sección condicional (conditional-section) de la directiva #else.

13756 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


138 Capítulo 18 Código no seguro

1• En caso contrario, no se selecciona la sección condicional (conditional-section).


2La sección condicional seleccionada, si existe, se procesa como una sección entrada (input-section) normal: el
3código fuente de la sección debe cumplir la gramática léxica, se generan símbolos a partir de dicho código y las
4directivas de preprocesamiento de la sección tienen los efectos prescritos.
5Las secciones condicionales restantes (conditional-sections), si existen, se procesan como secciones omitidas
6(skipped-sections): excepto en lo que respeta a las directivas de preprocesamiento, el código fuente de la sección
7no tiene por qué cumplir la gramática léxica; no se generan tokens a partir del código fuente de la sección; y las
8directivas de preprocesamiento de la sección deben ser léxicamente correctas, pero en caso contrario no se
9procesan. Dentro de una sección condicional (conditional-section) que se ha procesado como sección
10omitida(skipped-section), cualquier sección condicional (conditional-section) anidada (contenida en
11construcciones #i f...#end i f y #region...#endregion anidadas) también se procesa como sección omitida
12(skipped-section).
13En el ejemplo siguiente se ilustra cómo pueden anidarse directivas de compilación condicional:

14 #define Debug // Debugging on

15 #undef Trace // Tracing off


16 class PurchaseTransaction

17 {

18 void Commit() {

19 #if Debug

20 CheckConsistency();

21 #if Trace

22 WriteToLog(this.ToString());
28
23Excepto por las directivas de preprocesamiento, el código fuente omitido no se somete al análisis léxico. Por
29ejemplo, el código siguiente es válido, a pesar del comentario sin terminación de la sección #else:
24
30
25 #define Debug // Debugging on
31 class PurchaseTransaction
26
32 {
27
33 void Commit() {

34 #if Debug

35 CheckConsistency();

36 #else
41No obstante, debe tenerse en cuenta que las directivas de preprocesamiento deben ser léxicamente correctas
37
42aunque se encuentren en secciones omitidas del código fuente.
38
43Las directivas de preprocesamiento no se procesan cuando aparecen en elementos de entrada que ocupan varias
44
39líneas. Por ejemplo, el programa:
40

139Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 57


140Especificación del lenguaje C#

1 class Hello

2 {

3 static void Main() {

4 System.Console.WriteLine(@"hello,

5 #if Debug

6 world

7 #else
138da como resultado:

149 hello,
10
15 #if Debug
11
16 world
12
17 #else
20En casos concretos, el conjunto de directivas de preprocesamiento que se procesa puede depender de la
18evaluación de la expresión pp (pp-expression). En el ejemplo:
21
19
22 #if X

23 /*

24 #else
27muestra siempre la misma secuencia de símbolos (c lassQ { }), independientemente de que X esté definido o
25
28no. Si X está definido, las únicas directivas que se procesan son #if y #endif, a causa del comentario en varias
26
29líneas. Si X no está definido, las tres directivas (#if, #else, #endif) forman parte del conjunto de directivas.

302.5.5 Directivas de diagnóstico


31Las directivas de diagnóstico permiten generar de forma explícita mensajes de error y advertencias que se
32notifican de la misma manera que otros errores y advertencias en tiempo de compilación.
33 pp-diagnostic:
34 whitespaceopt # whitespaceopt er ro r pp-message
35 whitespaceopt # whitespaceopt warning pp-message
36 pp-message:
37 new-line
38 whitespace input-charactersopt new-line
39En el ejemplo:
40 #warn ing Code rev iew needed be fo re check - i n
41 #i f Debug && Reta i l
42 #er ro r A bu i l d can ' t be both debug and re ta i l
43 #end i f
44 c lass Tes t { . . . }
45muestra siempre la advertencia (“Code review needed before check-in”). Además, si están definidos los
46símbolos condicionales Debug y Retail , muestra el error en tiempo de compilación “A build can't be both

14158 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


142 Capítulo 18 Código no seguro

1debug and retail”. Observe que en un mensaje pp (pp-message) puede especificarse cualquier texto: no es
2necesario incluir símbolos formados correctamente, como indica el apóstrofo (comilla simple) de la palabra
3can ’ t.

42.5.6 Directivas de región


5Las directivas de región marcan de forma explícita regiones del código fuente.
6 pp-region:
7 pp-start-region conditional-sectionopt pp-end-region
8 pp-start-region:
9 whitespaceopt # whitespaceopt reg ion pp-message
10 pp-end-region:
11 whitespaceopt # whitespaceopt endreg ion pp-message
12No se asocia ningún significado semántico a las regiones; los programadores y las herramientas automáticas las
13utilizan para marcar secciones del código fuente. El mensaje especificado en una directiva #reg ion o
14#endreg ion tampoco tiene un significado semántico; sencillamente se usa para identificar la región. Las
15directivas #region y #endregion correspondientes pueden tener mensajes pp (pp-message) diferentes.
16El procesamiento léxico de una región:
17 #reg ion
18 ...
19 #endreg ion
20se corresponde exactamente con el procesamiento léxico de una directiva de compilación condicional de la
21forma:
22 #i f t rue
23 ...
24 #end i f
252.5.7 Directivas de línea
26Las directivas de línea permiten cambiar los números de línea y los nombres de archivo de código fuente que
27comunica el compilador en resultados del tipo de advertencias y errores.
28Las directivas de línea suelen utilizarse en herramientas de metaprogramación que generan código fuente C# a
29partir de otras entradas de texto.
30 pp-line:
31 whitespaceopt # whitespaceopt l i ne whitespace line-indicator pp-new-line
32 line-indicator:
33 decimal-digits whitespace file-name
34 decimal-digits
35 de fau l t
36 h idden
37 file-name:
38 " file-name-characters "
39 file-name-characters:
40 file-name-character
41 file-name-characters file-name-character

143Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 59


144Especificación del lenguaje C#

1 file-name-character:
2 Cualquier carácter de entrada (input-character) excepto "
3Cuando no aparecen directivas #l ine, el compilador muestra en su resultado los números de línea y nombres de
4archivo de código fuente reales. La directiva #l inesuele utilizarse en herramientas de metaprogramación que
5generan código fuente C# a partir de otras entradas de texto. Cuando se procesa una directiva #line que incluye
6un indicador de línea (line-indicator) que no es default, el compilador trata la línea siguiente a la directiva
7como si tuviera el número de línea dado (y el nombre de archivo, si se especifica).
8Una directiva #line default invierte el efecto de todas las directivas #line anteriores. El compilador comunica
9la información de línea real para las líneas posteriores, exactamente igual que si no se hubieran procesado
10directivas #line.
11Una directiva #line hidden no produce ningún efecto sobre el archivo ni sobre los números de línea de los que
12se informa en los mensajes de error, pero sí afecta a la depuración en el nivel de código fuente. Durante la
13depuración, las líneas entre una directiva #line hidden y la siguiente directiva #line (no #line hidden)
14carecerán de información de número de línea. Cuando se recorre el código en el depurador, estas líneas se
15omiten por completo.
16Observe que los nombres de archivo (file-name) se diferencian de los literales de cadena ordinarios en que los
17caracteres de escape no se procesan; en los nombres de archivo, el carácter ‘\’ sólo designa un carácter
18convencional de barra diagonal inversa.

14560 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


146 Capítulo 18 Código no seguro

1 3.Conceptos básicos

23.1 Inicio de la aplicación


3Una aplicación es un ensamblado que contiene un punto de entrada. Cuando se ejecuta una aplicación, se crea
4un nuevo dominio de aplicación. Pueden existir varias creaciones de instancias diferentes de una aplicación
5simultáneamente en el mismo equipo, cada una con su propio dominio de aplicación.
6Un dominio de aplicación habilita el aislamiento de aplicaciones, al actuar como contenedor del estado de una
7aplicación. Un dominio de aplicación se comporta como contenedor y límite de los tipos definidos en la
8aplicación y en las bibliotecas de clases que utiliza. Los tipos cargados en un dominio de aplicación son distintos
9de los mismos tipos cargados en otro, y las instancias de los objetos no se comparten directamente entre
10dominios de aplicación. Por ejemplo, cada dominio de aplicación mantiene su propia copia de variables estáticas
11para estos tipos, y un constructor estático de un tipo se ejecuta como máximo una vez por dominio de
12aplicación. Las implementaciones tienen libertad para proporcionar mecanismos o directivas específicas de la
13implementación para la creación y destrucción de los dominios de aplicación.
14Un inicio de programa se produce cuando el entorno de ejecución llama a un método designado, que se conoce
15como punto de entrada de la aplicación. Este método de punto de entrada siempre se denomina Main y puede
16tener una de las firmas siguientes:

17 static void Main() {...}


18 static void Main(string[] args) {...}
19 static int Main() {...}
20 static int Main(string[] args) {...}
21Como se muestra, el punto de entrada de manera opcional puede devolver un valor i n t. El valor devuelto se
22utiliza en la finalización de la aplicación (§3.2).
23El punto de entrada puede tener, opcionalmente, un parámetro formal. El parámetro puede tener cualquier
24nombre, pero el tipo del parámetro debe ser s t r i ng .[ Si
] el parámetro formal está presente, el entorno de
25ejecución crea y pasa un argumento s t r i ng [que
] contiene los argumentos de línea de comandos especificados al
26iniciar la aplicación. El argumento string[] nunca es null, pero puede tener longitud cero si no se encuentra
27especificado ningún argumento de línea de comandos.
28Dado que C# acepta la sobrecarga de métodos, una clase o una estructura puede contener varias definiciones del
29mismo método, a condición de que todas tengan una firma diferente. No obstante, ninguna clase o estructura de
30un mismo programa podrá contener más de un método denominado Main cuya definición lo califique para ser
31utilizado como punto de entrada de aplicación. Sin embargo, sí se permiten otras versiones sobrecargadas de
32Main, a condición de que tengan más de un parámetro o de que su único parámetro no sea de tipo string[].
33Una aplicación puede estar compuesta por varias clases o estructuras, dos o más de las cuales pueden contener
34un método denominado Main cuya definición le califica para utilizarlo como punto de entrada de la aplicación.
35En tales casos, debe utilizarse un mecanismo externo (por ejemplo, una opción de compilador de línea de
36comandos) para seleccionar uno de estos métodos Main como punto de entrada.
37En C#, todos los métodos deben estar definidos como miembros de una clase o una estructura. Normalmente, la
38accesibilidad declarada (§3.5.1) de un método está determinada por los modificadores de acceso (§10.2.3)
39especificados en su declaración y, de forma similar, la accesibilidad declarada de un tipo está determinada por

147Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 61


148Especificación del lenguaje C#

1los modificadores de acceso especificados en su declaración. Para que sea posible llamar a un método dado de
2un tipo concreto, tanto el tipo como el miembro deben ser accesibles. No obstante, el punto de entrada de la
3aplicación es un caso especial. En concreto, el entorno de ejecución puede tener acceso al punto de entrada de la
4aplicación con independencia de su accesibilidad declarada y de la accesibilidad declarada de sus declaraciones
5de tipo envolvente.
6A todos los demás respectos, los métodos de punto de entrada se comportan como los de punto de no entrada.

73.2 Finalización de la aplicación


8La finalización de la aplicación devuelve el control al entorno de ejecución.
9Si el tipo del valor devuelto por el método de punto de entrada de la aplicación es i n t, el valor devuelto
10cumple la función de código de estado de finalización de la misma. El propósito de este código es permitir la
11comunicación del éxito o el error al entorno de ejecución.
12Si el tipo del valor devuelto del método de punto de entrada es vo id, cuando se alcanza la llave de cierre (}),
13que finaliza el método, o cuando se ejecuta una instrucción return que no contiene una expresión, se produce un
14código de estado de finalización igual a 0.
15Antes de la finalización de una aplicación, se llama a destructores para todos los objetos que aún no hayan sido
16sometidos a la recolección de elementos no utilizados (salvo que se haya suprimido este tipo de limpieza
17mediante, por ejemplo, una llamada al método de biblioteca GC.SuppressFinalize).

183.3 Declaraciones
19Las declaraciones de un programa de C# definen los elementos constituyentes del programa. Los programas de
20C# se organizan mediante el uso de espacios de nombres (§9), que pueden contener declaraciones de tipo y
21declaraciones de espacio de nombres anidadas. Las declaraciones de tipo (§9.5) se utilizan para definir clases
22(§10), estructuras (§11), interfaces (§13), enumeraciones (§14) y delegados (§15). Las categorías de los
23miembros permitidos en una declaración de tipo dependen de la forma de la declaración. Por ejemplo, las
24declaraciones de clase pueden contener declaraciones para las constantes (§10.3), campos (§10.4), métodos
25(§10.5), propiedades (§10.6), eventos (§10.7), indizadores (§10.8), operadores (§10.9), constructores de
26instancia (§10.10), constructores estáticos (§10.11), destructores (§10.12) y tipos anidados.
27Una declaración define un nombre en el espacio de declaración (declaration space ) al que pertenece la
28declaración. Con la excepción de los miembros sobrecargados (§3.6), es un error en tiempo de compilación
29contar con dos o más declaraciones que introducen miembros con el mismo nombre en un espacio de
30declaración. No está permitido que un espacio de declaración contenga categorías de miembros diferentes con el
31mismo nombre. Por ejemplo, un espacio de declaración no puede contener un campo y un método con el mismo
32nombre.
33Existen varios tipos diferentes de espacios de declaración, como se explica a continuación.
34• Dentro de todos los archivos de código fuente de un programa, las declaraciones de miembro de espacio de
35 nombres (namespace-member-declarations ) sin una declaración de espacio de nombres envolvente son
36 miembros de un solo espacio de declaración combinado, denominado espacio de declaración global (global
37 declaration space).
38• Dentro de todos los archivos de código fuente de un programa, las declaraciones de miembro de espacio de
39 nombres (namespace-member-declarations) de las declaraciones de espacio de nombres (namespace-
40 declarations) que tienen el mismo nombre completo del espacio de nombres son miembros de un solo
41 espacio de declaración combinado.
42• Cada declaración de clase, estructura o interfaz crea un nuevo espacio de declaración. Los nombres se
43 introducen en este espacio de declaración mediante declaraciones de miembros de clase (class-member-

14962 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


150 Capítulo 18 Código no seguro

1 declarations), declaraciones de miembros de estructura (struct-member-declarations) o declaraciones de


2 miembros de interfaz (interface-member-declarations). Excepto en las declaraciones de constructores de
3 instancia y declaraciones de constructor estático sobrecargadas, una declaración de miembro de clase o
4 estructura no puede introducir un miembro del mismo nombre como la clase o la estructura. Una clase,
5 estructura o interfaz permite la declaración de métodos e indizadores sobrecargados. Asimismo, las clases y
6 estructuras admiten la declaración de operadores y constructores de instancia sobrecargados. Por ejemplo,
7 una clase, una estructura o una interfaz pueden contener varias declaraciones de método con el mismo
8 nombre, a condición de que dichas declaraciones difieran en su firma (§3.6). Debe tenerse en cuenta que las
9 clases base no contribuyen al espacio de declaración de una clase, y que las interfaces base no contribuyen
10 al espacio de declaración de una interfaz. Por lo tanto, una clase o interfaz derivada tiene permitido declarar
11 un miembro con el mismo nombre que un miembro heredado. Se dice que un miembro como éste oculta al
12 miembro heredado.
13• Una declaración de enumeración crea un espacio de declaración nuevo. Los nombres se introducen en este
14 espacio de declaración mediante declaraciones de miembros de enumeración (enum-member-declarations).
15• Cada bloque (block) o bloque switch (switch-block) , , así como una instrucción for, foreach y using crea un
16 espacio de declaración para variables y constantes locales denominado espacio de declaración de la
17 variable local. Los nombres se introducen en este espacio de declaración mediante declaraciones de variable
18 local (local-variable-declarations) y declaraciones de constante local (local-constant-declarations). Si un
19 bloque es el cuerpo de un constructor de instancia, de un método o una declaración de operador o de un
20 descriptor de acceso get o set para una declaración de indizador, los parámetros declarados en dicha
21 declaración serán miembros del espacio de declaración de la variable local del bloque. Que dos miembros
22 de un espacio de declaración de la variable local tengan el mismo nombre supone un error. Que el espacio
23 de declaración de la variable local de un bloque y el espacio de declaración de la variable local anidado
24 contengan elementos con el mismo nombre supone un error. Por lo tanto, dentro de un espacio de
25 declaración anidado no es posible declarar una variable o constante local con el mismo nombre que una
26 variable o constante local contenida en un espacio de declaración. Es posible que dos espacios de
27 declaración contengan elementos con el mismo nombre siempre y cuando ninguno de los espacios de
28 declaración contenga al otro.
29• Cada bloque (block) o bloque switch (switch-block) crea un espacio de declaración independiente para las
30 etiquetas. Los nombres se introducen en este espacio de declaración mediante instrucciones con etiquetas
31 (labeled-statements), y se hace referencia a ellos mediante instrucciones goto (goto-statements). El espacio
32 de declaración de etiquetas de un bloque incluye bloques anidados. Por lo tanto, dentro de un bloque
33 anidado no es posible declarar una etiqueta del mismo nombre que una etiqueta contenida en un bloque
34 contenedor.
35El orden textual de declaración de los nombres generalmente carece de importancia. Así, el orden textual no
36resulta significativo para la declaración o el uso de espacios de nombres, constantes, métodos, propiedades,
37eventos, indizadores, operadores, constructores de instancia, destructores, constructores estáticos o tipos. El
38orden de declaración es significativo de las siguientes formas:
39• El orden de declaración de las declaraciones de campos y de variables locales determina el orden en que se
40 ejecutan sus inicializadores (si existen).
41• Las variables locales no se pueden utilizar antes de su definición (§3.7).
42• El orden de declaración para las declaraciones de miembros de enumeración (§14.3) es importante cuando
43 se omiten los valores de expresión constante (constant-expression).

151Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 63


152Especificación del lenguaje C#

1El espacio de declaración de un espacio de nombres es “de extremo abierto”, es decir, dos declaraciones de
2espacio de nombres con un mismo nombre completo contribuirán al mismo espacio de declaración. Por ejemplo:

3 namespace Megacorp.Data

4 {

5 class Customer

6 {

7 ...
10 namespace Megacorp.Data
8
11 {
9
12 class Order

13 {

14Las dos declaraciones


17 ... de espacios de nombres anteriores contribuyen al mismo espacio de declaración, en este
18caso declarando dos clases con los nombres completos Megacorp .Data .Cus tomery Megacorp .Data .O rde.r
15
19Como las dos declaraciones contribuyen al mismo espacio de declaración, si cada una contiene una declaración
20
16de clase con el mismo nombre se producirá un error en tiempo de compilación.
21Como se especificó anteriormente, el espacio de declaración de un bloque incluye cualquier bloque anidado. De
22este modo, en el siguiente ejemplo, los métodos F y G producen errores en tiempo de compilación porque el
23nombre i se declara en el bloque exterior y no se puede volver a declarar en el bloque interior. Sin embargo, los
24métodos H e I son válidos porque los dos caracteres i se declaran en bloques independientes no anidados.

25 class A

26 {

27 void F() {

28 int i = 0;

29 if (true) {
33
30 void G() {

34
31 if (true) {

35
32 int i = 0;

36 }
39
37 void H() {

40
38 if (true) {

41 int i = 0;

42 }

43 if (true) {

44
45
46

15364 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


154 Capítulo 18 Código no seguro

1 void I() {

2 for (int i = 0; i < 10; i++)

3 H();

4 for (int i = 0; i < 10; i++)

5 H();
83.4 Miembros
6
9Los espacios de nombres y los tipos tienen miembros. Los miembros de una entidad están generalmente
107disponibles mediante el uso de un nombre completo compuesto por una referencia a la entidad seguida de un
11símbolo “.” y el nombre del miembro.
12Los miembros de un tipo se declaran en el tipo o se heredan de la clase base del tipo. Si un tipo se deriva de una
13clase base, todos los miembros de dicha clase, excepto los constructores de instancia, destructores y
14constructores estáticos, pasan a ser miembros del tipo derivado. La accesibilidad declarada de un miembro de
15clase base no controla si el miembro se hereda: la herencia se amplía a cualquier miembro que no sea un
16constructores de instancia, un constructor estático o un destructor. No obstante, un miembro heredado puede no
17estar accesible en un tipo derivado, ya sea a causa de su accesibilidad declarada (§3.5.1) o porque está oculto por
18una declaración del propio tipo (§3.7.1.2).

193.4.1 Miembros de espacio de nombres


20Los espacios de nombres y los tipos que no tienen un espacio de nombres envolvente son miembros del espacio
21de nombres global. Esto corresponde directamente a los nombres declarados en el espacio de declaración
22global.
23Los espacios de nombres y los tipos declarados en de un espacio de nombres son miembros de dicho espacio.
24Esto corresponde directamente a los nombres declarados en el espacio de declaración del espacio de nombres.
25Los espacios de nombres no presentan restricciones de acceso. No es posible declarar espacios de nombres
26privados, protegidos o internos, y los nombres de los espacios de nombres siempre son accesibles públicamente.

273.4.2 Miembros de estructura


28Los miembros de una estructura son los miembros declarados en la estructura y los miembros heredados de la
29clase base directa de la estructura Sys tem.Va lueTypey la clase base indirecta ob jec t.
30Los miembros de un tipo simple se corresponden directamente con los del tipo de la estructura con alias por el
31tipo simple:
32• Los miembros de sby te son los miembros de la estructura Sys tem.SByte.
33• Los miembros de byte son los miembros de la estructura Sys tem.By te.
34• Los miembros de shor tson los miembros de la estructura Sys tem. In t16.
35• Los miembros de ushor t son los miembros de la estructura Sys tem.U In t16.
36• Los miembros de i n tson los miembros de la estructura Sys tem. In t32.
37• Los miembros de u in tson los miembros de la estructura Sys tem.U In t32.
38• Los miembros de l ong son los miembros de la estructura Sys tem. In t64.
39• Los miembros de u long son los miembros de la estructura Sys tem.U In t64.
40• Los miembros de char son los miembros de la estructura Sys tem.Char.

155Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 65


156Especificación del lenguaje C#

1• Los miembros de f l oa tson los miembros de la estructura Sys tem.S ing le


.
2• Los miembros de doub le son los miembros de la estructura Sys tem.Doub le.
3• Los miembros de dec ima l son los miembros de la estructura Sys tem.Dec ima l.
4• Los miembros de boo l son los miembros de la estructura Sys tem.Boo lean.

53.4.3 Miembros de enumeraciones


6Los miembros de una enumeración son las constantes declaradas en la enumeración, los miembros heredados de
7la clase base directa de la enumeración Sys tem.Enum y las clases base indirectas Sys tem.Va lueTypey
8object.

93.4.4 Miembros de clase


10Los miembros de una clase son aquellos que se declaran en la clase y los que se heredan de la clase base (a
11excepción de la clase object, que no tiene clase base). Entre los miembros heredados de la clase base se
12incluyen las constantes, los campos, los métodos, las propiedades, los eventos, los indizadores, los operadores y
13los tipos de la clase base, pero no los constructores de instancia, los destructores ni los constructores estáticos de
14la clase base. Los miembros de la base clase se heredan con independencia de su accesibilidad.
15La declaración de una clase puede contener declaraciones de constantes, campos, métodos, propiedades,
16eventos, indizadores, operadores, constructores de instancia, destructores, constructores estáticos y tipos.
17Los miembros de object y string se corresponden directamente con los miembros de los tipos de clases de los
18que son alias:
19• Los miembros de ob jec tson los miembros de la clase Sys tem.Ob jec t.
20• Los miembros de s t r i ngson los miembros de la clase Sys tem.S t r i ng
.

213.4.5 Miembros de interfaz


22Los miembros de una interfaz son los miembros declarados en la interfaz y en todas las interfaces base de la
23misma. Los miembros de la clase object no son, estrictamente hablando, miembros de ninguna interfaz (§13.2).
24Sin embargo, los miembros de la clase object están disponibles a través de la búsqueda de miembros en
25cualquier tipo de interfaz (§7.3).

263.4.6 Miembros de matriz


27Los miembros de una matriz son los miembros heredados de la clase System.Array.

283.4.7 Miembros de delegados


29Los miembros de un delegado son los miembros heredados de la clase System.Delegate.

303.5 Acceso a miembros


31Las declaraciones a miembros aportan control sobre el acceso a los miembros. La accesibilidad de un miembro
32se establece mediante la accesibilidad declarada (§3.5.1) del miembro combinada con la accesibilidad del tipo
33contenedor inmediato, si existe.
34Cuando se permite el acceso a un miembro concreto, se dice que éste es accesible. Por otro lado, cuando no se
35permite el acceso a un miembro concreto, se dice que éste es inaccesible. El acceso a un miembro está permitido
36cuando la ubicación textual en la que tiene lugar el acceso está incluida en el dominio de accesibilidad (§3.5.2)
37del miembro.

15766 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


158 Capítulo 18 Código no seguro

13.5.1 Accesibilidad declarada


2La accesibilidad declarada de un miembro puede ser cualquiera de las siguientes:
3• Public, que se selecciona mediante la inclusión de un modificador pub l i cen la declaración del miembro. El
4 significado intuitivo de pub l i ces “acceso sin restricciones”.
5• Protected, que se selecciona mediante la inclusión de un modificador pro tec teden la declaración del
6 miembro. El significado intuitivo de pro tec tedes “acceso restringido para la clase contenedora o los tipos
7 derivados de ella”.
8• Internal, que se selecciona mediante la inclusión de un modificador i n te rnaen
l la declaración del miembro.
9 El significado intuitivo de i n te rnaes
l “acceso restringido al programa”.
10• Protected internal (es decir, protegida o interna), que se selecciona mediante la inclusión de los
11 modificadores pro tec tede i n te rnaen l la declaración del miembro. El significado intuitivo de protected
12 internal es “acceso restringido al programa actual o a los tipos derivados de la clase contenedora”.
13• Private, que se selecciona mediante la inclusión de un modificador private en la declaración del miembro.
14 El significado intuitivo de private es “acceso restringido al tipo contenedor”.
15Según el contexto en el que se produce la declaración de un miembro, sólo se permite declarar ciertos tipos de
16accesibilidad. Además, si una declaración del miembro no incluye modificadores de acceso, el contexto en que
17tiene lugar la declaración determina la accesibilidad declarada predeterminada.
18• Los espacios de nombres tienen implícitamente la accesibilidad declarada public. Los modificadores de
19 acceso no se pueden utilizar en las declaraciones de espacios de nombres.
20• Los tipos declarados en las unidades de compilación o los espacios de nombres pueden tener la accesibilidad
21 declarada public o internal y su valor predeterminado puede ser internal.
22• Los miembros de clase pueden tener cualquiera de las cinco clases de accesibilidad declarada y el valor
23 predeterminado de la accesibilidad declarada private. (Debe tenerse en cuenta que un tipo declarado como
24 miembro de una clase sólo puede tener una de las cinco clases de accesibilidad declarada, mientras que un
25 tipo declarado como miembro de un espacio de nombres sólo puede tener la accesibilidad declarada public
26 o internal.)
27• Los miembros de estructura pueden tener la accesibilidad declarada public, internal o private, siendo el
28 valor predeterminado private, puesto que las estructuras son implícitamente de tipo sealed. Los miembros
29 de estructura introducidos en una estructura (es decir, no heredados por ella) no pueden tener una
30 accesibilidad declarada protected o protected internal. Tenga en cuenta que un tipo declarado como
31 miembro de una estructura sólo puede tener una de las clases de accesibilidad declarada public, internal o
32 private , mientras que un tipo declarado como miembro de un espacio de nombres sólo puede tener la
33 accesibilidad declarada public o internal.
34• Los miembros de interfaz tienen implícitamente la accesibilidad declarada public. Los modificadores de
35 acceso no se pueden utilizar en las declaraciones de miembros de interfaz.
36• Los miembros de enumeración tienen implícitamente la accesibilidad declarada public. Los modificadores
37 de acceso no pueden utilizarse en las declaraciones de miembros de enumeración.

383.5.2 Dominios de accesibilidad


39El dominio de accesibilidad de un miembro está formado por las secciones (posiblemente separadas) de texto
40del programa en las que está permitido el acceso al miembro. Para fines de definición del dominio de
41accesibilidad de un miembro, se dice que un miembro es de nivel superior si no está declarado dentro de un tipo,
42y se dice que está anidado si se declara dentro de otro tipo. Asimismo, el texto de programa de un programa se

159Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 67


160Especificación del lenguaje C#

1define como todo el texto de programa contenido en todos los archivos de código fuente. El texto de programa
2de un tipo se define como todo el texto del programa contenido entre los símbolos de apertura y cierre “{” y “}”
3del cuerpo de clase (class-body), del cuerpo de estructura (struct-body), del cuerpo de interfaz (interface-body) o
4del cuerpo de enumeración (enum-body) del tipo (incluidos, posiblemente, los tipos anidados dentro del tipo).
5El dominio de accesibilidad de un tipo predefinido (como ob jec t, i n to double) es ilimitado.
6El dominio de accesibilidad de un tipo de nivel superior T declarado en un programa P se define como sigue:
7• La accesibilidad declarada de T es public, el dominio de accesibilidad de T es el texto del programa de P y
8 cualquier programa que haga referencia a P.
9• Si la accesibilidad declarada de T es i n te rna, lel dominio de accesibilidad de T es el texto del programa de
10 P.
11A partir de estas definiciones, se deduce que el dominio de accesibilidad de un tipo de nivel superior es, al
12menos, el texto del programa en el que se declara ese tipo.
13El dominio de accesibilidad de un miembro anidado M declarado en un tipo T dentro de un programa P se define
14como sigue (teniendo en cuenta que M probablemente sea un tipo):
15• Si la accesibilidad declarada de M es public, el dominio de accesibilidad de M es el dominio de T.
16• Si la accesibilidad declarada de M es protected internal, D será la unión del texto de programa de P y del
17 texto de programa de cualquier tipo derivado de T que se declare fuera de P. El dominio de accesibilidad de
18 M es la intersección del dominio de accesibilidad de T con D.
19• Si la accesibilidad declarada de M es protected, D será la unión del texto de programa de T y del texto de
20 programa de cualquier tipo derivado de T. El dominio de accesibilidad de M es la intersección del dominio
21 de accesibilidad de T con D.
22• Si la accesibilidad declarada de M es internal, el dominio de accesibilidad de M es la intersección del
23 dominio de accesibilidad de T con el texto de programa de P.
24• Si la accesibilidad declarada de M es private, el dominio de accesibilidad de M es el texto del programa de
25 T.
26De estas definiciones se deduce que el dominio de accesibilidad de un tipo anidado es siempre, al menos, el
27texto de programa del tipo donde el miembro aparece declarado. Asimismo, se concluye que el dominio de
28accesibilidad de un miembro nunca es más inclusivo que el dominio de accesibilidad del tipo en el que se
29declara el miembro.
30En términos intuitivos, cuando se obtiene acceso a un tipo o un miembro M, se evalúan los pasos siguientes para
31garantizar que está permitido el acceso:
32• En primer lugar, si M se declara dentro de un tipo (por oposición a una unidad de compilación o un espacio
33 de nombres), se produce un error en tiempo de compilación si el tipo no está accesible.
34• Después, si M es public, el acceso está permitido.
35• O bien, si M es protected internal, el acceso está permitido si se produce dentro del programa en el que
36 está declarado M o si se produce dentro de una clase derivada de la clase en la que está declarado M y tiene
37 lugar por medio del tipo de la clase derivada (§3.5.3).
38• O bien, si M es protected, el acceso está permitido si se produce dentro de la clase en la que está declarado
39 M o si se produce dentro de una clase derivada de la clase en la que está declarado M y tiene lugar por medio
40 del tipo de la clase derivada (§3.5.3).
41• O bien, si M es internal, el acceso está permitido si ocurre dentro del programa en el que está declarado M.

16168 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


162 Capítulo 18 Código no seguro

1• O bien, si M es pr i va te
, el acceso está permitido si ocurre dentro del programa en el que está declarado M.
2• De lo contrario, el tipo o el miembro es inaccesible y se produce un error en tiempo de compilación.

163Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 69


164Especificación del lenguaje C#

1En el siguiente ejemplo:

2 public class A

3 {

4 public static int X;

5 internal static int Y;


86 internal class B

97 {

10 public static int X;

11
13 public class C
12
14 {

15 public static int X;

16 internal static int Y;


19
17 private class D

20
18 {

21 public static int X;

22 internal static int Y;

23las clases y los miembros


26 private tienen
static int Z; dominios de accesibilidad:
los siguientes
24
27• El dominio de accesibilidad de A y A.X es ilimitado.
25•
28 El dominio de accesibilidad de A.Y, B, B.X, B.Y, B.C, B.C.X, y B.C.Y es el texto del programa contenedor.
29• El dominio de accesibilidad de A.Z es el texto del programa de A.
30• El dominio de accesibilidad de B .Z y B .D es el texto de programa de B, incluido el texto de programa de
31 B.C y de B.D.
32• El dominio de accesibilidad de B.C.Z es el texto del programa de B.C.
33• El dominio de accesibilidad de B.D.X y B.D.Y es el texto de programa de B, incluido el texto de programa
34 de B.C y de B.D.
35• El dominio de accesibilidad de B.D.Z es el texto del programa de B.D.
36Como muestra el ejemplo, el dominio de accesibilidad de un miembro nunca es mayor que el de un tipo
37contenedor. Por ejemplo, aunque todos los miembros X tengan una accesibilidad declarada como public, todos
38excepto A.X tienen dominios de accesibilidad que están restringidos por un tipo contenedor.
39Como se explica en la §3.4, todos los miembros de una clase base, a excepción de los constructores de instancia,
40destructores y constructores estáticos, son heredados por tipos derivados. Esto incluye incluso a los miembros
41privados de una clase base. No obstante, el dominio de accesibilidad de un miembro privado sólo incluye el
42texto del programa del tipo donde el miembro aparece declarado. En el siguiente ejemplo:
43 c lass A
44 {
45 int x;

16570 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


166 Capítulo 18 Código no seguro

1 static void F(B b) {

2 b.x = 1; // Ok

3 }
5 class B: A
4
6 {

7 static void F(B b) {

8 b.x = 1; // Error, x not accessible


11la clase B hereda el miembro privado x de la clase A. Dado que el miembro es privado, sólo está accesible
129dentro del cuerpo de clase (class-body) de A. Por lo tanto, el acceso a b.x es correcto en el método A.F, pero no
13
10en el método B.F.
143.5.3 Acceso protegido para miembros de instancia
15Cuando se obtiene acceso a un miembro de instancia protected fuera del texto del programa de la clase en la
16que está declarado, y cuando se obtiene acceso a un miembro de instancia protected internal fuera del texto
17del programa en el que está declarado, el acceso debe tener lugar a través de una instancia del tipo de la clase
18derivada en la que se produce el acceso. Supongamos que B es una clase base que declara un miembro de
19instancia M protegido, y que D es una clase que se deriva de B. Dentro del cuerpo de clase (class-body) de D, el
20acceso a M puede tener uno de los siguientes formatos:
21• Un nombre de tipo (type-name) o expresión primaria (primary-expression) sin calificar de la forma M.
22• Una expresión primaria (primary-expression) de la forma E.M, a condición de que el tipo de E sea D o una
23 clase derivada de D.
24• Una expresión primaria (primary-expression) de la forma base.M.
25Además de estas formas de acceso, una clase derivada puede tener acceso a un constructores de instancia
26protegido de una clase base en un inicializador de constructor (constructor-initializer) (§10.10.1).
27En el siguiente ejemplo:
28 pub l i c c lass A
29 {
30 pro tec ted i n t x ;
31 s ta t i c vo id F (A a , B b) {
32 a .x = 1 ; / / Ok
33 b .x = 1 ; / / Ok
34 }
35
36 pub l i c c lass B: A
37 {
38 s ta t i c vo id F (A a , B b) {
39 a .x = 1 ; / / Er ro r , must access th rough i n s tance o f B
40 b .x = 1 ; / / Ok
41 }
43dentro de A, es posible obtener acceso a x mediante instancias de A y de B, puesto que en cualquiera de los dos
42casos el acceso tiene lugar mediante una instancia de A o una clase derivada de A. No obstante, dentro de B, no
44
45es posible el acceso a x por medio de una instancia de A, puesto que A no se deriva de B.

167Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 71


168Especificación del lenguaje C#

13.5.4 Restricciones de accesibilidad


2Algunas construcciones del lenguaje C# requieren que un tipo sea al menos tan accesible como un miembro u
3otro tipo. Se dice que un tipo T es por lo menos tan accesible como un miembro o un tipo M si el dominio de
4accesibilidad de T es un supraconjunto del dominio de accesibilidad de M. Es decir, T es por lo menos tan
5accesible como M si T es accesible en todos los contextos donde M es accesible.
6Existen las siguientes restricciones de la accesibilidad:
7• La clase base directa de un tipo de clase debe ser al menos tan accesible como el propio tipo de clase.
8• Las interfaces base explícitas de un tipo de interfaz deben ser al menos tan accesibles como el propio tipo de
9 interfaz.
10• El tipo de valor devuelto y los tipos de los parámetros de un tipo delegado deben ser al menos tan accesibles
11 como el propio tipo delegado.
12• El tipo de una constante debe ser al menos tan accesible como la propia constante.
13• El tipo de un campo debe ser al menos tan accesible como el propio campo.
14• El tipo de valor devuelto y los tipos de parámetros de un método deben ser al menos tan accesibles como el
15 propio método.
16• El tipo de una propiedad debe ser al menos tan accesible como la misma propiedad.
17• El tipo de un evento debe ser al menos tan accesible como el propio evento.
18• El tipo y los tipos de parámetros de un indizador deben ser al menos tan accesibles como el propio
19 indizador.
20• El tipo de valor devuelto y los tipos de parámetros de un operador deben ser al menos tan accesibles como el
21 propio operador.
22• Los tipos de parámetros de un constructores de instancia deben ser al menos tan accesibles como el propio
23 constructores de instancia.
24En el siguiente ejemplo:
25 c lass A { . . . }
26 pub l i c c lass B: A { . . . }
27la clase B produce un error en tiempo de compilación porque A no tiene una capacidad de acceso mínima como
28B.
29Análogamente, en el ejemplo:
30 c lass A { . . . }
31 pub l i c c lass B
32 {
33 A F( ) {. . .}
34 i n te rna l A G( ) { . . . }
35 pub l i c A H( ) { . . . }
36 }
37el método H de B produce un error en tiempo de compilación porque el tipo de valor devuelto A no es por lo
38menos tan accesible como el método.

16972 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


170 Capítulo 18 Código no seguro

13.6 Firmas y sobrecargas


2Los métodos, constructores de instancia, indizadores y operadores están caracterizados por sus firmas:
3• La firma de un método se compone del nombre del método y del tipo y la categoría (valor, referencia o
4 resultado) de sus parámetros formales, considerados de izquierda a derecha. La firma de un método no
5 incluye específicamente el tipo de valor devuelto ni el modificador params que puede especificarse para el
6 parámetro situado más a la derecha.
7• La firma de un constructor de instancias se compone del tipo y la categoría (valor, referencia o resultado) de
8 sus parámetros formales, considerados de izquierda a derecha. La firma de un constructor de instancias no
9 incluye específicamente el modificador params , que puede especificarse para el parámetro situado más a la
10 derecha.
11• La firma de un indizador está formada por el tipo de sus parámetros formales, considerados de izquierda a
12 derecha. La firma de un indizador no incluye específicamente el tipo de elemento ni el modificador params
13 que puede estar especificado para el parámetro situado más a la derecha.
14• La firma de un operador se compone del nombre del operador y del tipo de sus parámetros formales,
15 considerados de izquierda a derecha. La firma de un operador no incluye específicamente el tipo del
16 resultado.
17Las firmas constituyen el mecanismo de habilitación que permite sobrecargar los miembros de clases,
18estructuras e interfaces:
19• La sobrecarga de los métodos permite que una clase, estructura o interfaz declare varios métodos con el
20 mismo nombre, siempre que sus firmas sean únicas dentro de esa clase, estructura o interfaz.
21• La sobrecarga de los constructores de instancia permite que una clase o una estructura declare varios
22 constructores de instancia, a condición de que sus firmas sean únicas dentro de esa clase o estructura.
23• La sobrecarga de los indizadores permite que una clase, estructura o interfaz declare varios indizadores,
24 siempre que sus firmas sean únicas dentro de esa clase, estructura o interfaz.
25• La sobrecarga de los operadores permite que una clase o una estructura declare varios operadores con el
26 mismo nombre, siempre que sus firmas sean únicas dentro de esa clase o estructura.
27Aunque los modificadores de parámetros out y re fse consideran como parte de una firma, los miembros
28declarados en un tipo único no pueden diferir en la firma únicamente por ref y out. Si dos miembros se declaran
29en el mismo tipo con firmas que serían iguales si todos los parámetros de ambos métodos con modificadores out
30se cambiaran a modificadores ref, se produce un error de compilación. Para otros propósitos de coincidencia de
31firma (por ejemplo, para ocultar o reemplazar), ref y out se consideran como parte de la firma y no coinciden
32entre sí. (Esta restricción se utiliza para permitir traducir con facilidad los programas de C# para que se ejecuten
33en Common Language Infrastructure (CLI), que no proporciona una manera de definir métodos que solamente
34se diferencian en ref y out.)
35El siguiente ejemplo muestra un conjunto de declaraciones de métodos sobrecargados con sus firmas.

36 interface ITest

37 {

38
39 void F(int x); // F(int)
40 void F(ref int x); // F(ref int)
41 void F(out int x); // F(out int) error
42 void F(int x, int y); // F(int, int)

171Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 73


172Especificación del lenguaje C#

1 int F(string s); // F(string)


2 int F(int x); // F(int) error
3 void F(string[] a); // F(string[])
4 void F(params string[] a); // F(string[]) error

56Se debe }tener en cuenta que los modificadores de parámetros re fy out (§10.5.1) forman parte de una firma. Por
7lo tanto, F(int) y F(ref int) son firmas únicas. Sin embargo, F(refint) y F(outint) no se pueden declarar dentro
8de la misma interfaz porque sus firmas se diferencian únicamente en ref y out. Asimismo, el tipo de valor
9devuelto y el modificador params no forman parte de una firma, por lo que no es posible sobrecargar
10basándose exclusivamente en el tipo de valor devuelto o en la inclusión o exclusión del modificador params.
11Como tales, las declaraciones de los métodos F(int) y F(params string[]) anteriormente identificadas producen
12un error en tiempo de compilación.

133.7 Ámbitos
14El ámbito de un nombre es la región del texto del programa en la cual es posible referirse a la entidad declarada
15por el nombre sin la calificación de éste. Los ámbitos se pueden anidar y un ámbito interno puede volver a
16declarar el significado de un nombre de un ámbito exterior (sin embargo, de esta forma no se quita la restricción
17impuesta por §3.3 de que, dentro de un bloque anidado, no se puede declarar una variable local con el mismo
18nombre de una variable local de un bloque contenedor). En este caso, se dice que el nombre del ámbito externo
19está oculto en la región del texto del programa cubierta por el ámbito interno, y el acceso al nombre externo sólo
20es posible si se califica el nombre.
21• El ámbito de un miembro de espacio de nombres declarado por una declaración de miembro de espacio de
22 nombres (namespace-member-declaration) (§9.4) sin declaración de espacio de nombres (namespace-
23 declaration) envolvente es todo el texto del programa.
24• El ámbito de un miembro de espacio de nombres declarado mediante una declaración de miembro de
25 espacio de nombres (namespace-member-declaration), dentro de una declaración de espacio de nombres
26 (namespace-declaration) cuyo nombre completo sea N, será el cuerpo de espacio de nombres (namespace-
27 body) de todas las declaraciones de espacio de nombres (namespace-declaration) cuyo nombre completo sea
28 N o empiece por N, seguido por un punto.
29• El ámbito de un nombre definido o importado por una directiva using (using-directive) (§9.3) se amplía a las
30 declaraciones de miembro de espacio de nombres (namespace-member-declarations) de la unidad de
31 compilación (compilation-unit) o del cuerpo de espacio de nombres (namespace-body) en que tiene lugar la
32 directiva using (using-directive). Una directiva using (using-directive) puede ofrecer cero o más espacios de
33 nombres o nombres de tipos disponibles dentro de una unidad de compilación (compilation-unit) o cuerpo
34 de espacio de nombres (namespace-body) en particular, pero no aporta ningún miembro nuevo al espacio de
35 declaración subyacente. Es decir, una directiva using (using-directive) no es transitiva, sino que afecta
36 únicamente a la unidad de compilación (compilation-unit) o al cuerpo de espacio de nombres (namespace-
37 body) en que tiene lugar.
38• El ámbito de un miembro declarado por una declaración de miembro de clase (class-member-declaration)
39 (§10.2) es el cuerpo de clase (class-body) en que se produce la declaración. Asimismo, el ámbito de un
40 miembro de clase se amplía al cuerpo de clase (class-body) de las clases derivadas que estén incluidas en el
41 dominio de accesibilidad (§3.5.2) del miembro.
42• El ámbito de un miembro declarado por una declaración de miembro de estructura (struct-member-
43 declaration) (§11.2) es el cuerpo de estructura (struct-body) en que se produce la declaración.

17374 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


174 Capítulo 18 Código no seguro

1• El ámbito de un miembro declarado por una declaración de miembro de enumeración (enum-member-


2 declaration) (§14.3) es el cuerpo de enumeración (enum-body) en el que se produce la declaración.
3• El ámbito de un parámetro declarado en una declaración de método (method-declaration) (§10.5) es el
4 cuerpo del método (method-body) de dicha declaración de método (method-declaration).
5• El ámbito de un parámetro declarado en una declaración de indizador (indexer-declaration) (§10.8) es la
6 declaración de descriptor de acceso (accessor-declaration) de dicha declaración de indizador (indexer-
7 declaration).
8• El ámbito de un parámetro declarado en una declaración de operador (operator-declaration) (§10.9) es el
9 bloque (block) de dicha declaración de operador (operator-declaration).
10• El ámbito de un parámetro declarado en una declaración de constructor (constructor-declaration) (§10.10)
11 es el inicializador de constructor (constructor-initializer) y el bloque (block) de dicha declaración de
12 constructor (constructor-declaration).
13• El ámbito de una etiqueta declarada en una instrucción con etiqueta (labeled-statement) (§8.4) es el bloque
14 (block) en que se produce la declaración.
15• El ámbito de una variable local declarada en una declaración de variable local (local-variable-declaration)
16 (§8.5.1) es el bloque donde se produce la declaración.
17• El ámbito de una variable local declarada en un bloque switch (switch-block) de una instrucción sw i t ch
18 (§8.7.2) es el bloque switch (switch-block).
19• El ámbito de una variable local declarada en un inicializador for (for-initializer) de una instrucción f o r
20 (§8.8.3) es el inicializador for (for-initializer), la condición for (for-condition), el iterador for (for-iterator) y
21 la instrucción (statement) contenida de la instrucción f o r.
22• El ámbito de una constante local declarada en una declaración de constante local (local-constant-
23 declaration) (§8.5.2) es el bloque donde se produce la declaración. Es un error en tiempo de compilación
24 hacer referencia a una constante local en una posición textual que precede a su declarador de constante
25 (constant-declarator).
26Dentro del ámbito de un miembro de espacio de nombres, clase, estructura o enumeración es posible hacer
27referencia al miembro en una posición textual que precede a la declaración del miembro. Por ejemplo:
28 c lass A
29 {
30 vo id F ( ) {
31 i = 1;
32
33 i n t i = 0;
34 }
35Por lo tanto, es válido que F haga referencia a i antes de su declaración.
36En el ámbito de una variable local, es un error en tiempo de compilación hacer referencia a la variable local en
37una posición textual que preceda al declarador de la variable local (local-variable-declarator). Por ejemplo:
38 c lass A
39 {
40 i n t i = 0;

175Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 75


176Especificación del lenguaje C#

1 void F() {

2 i = 1; // Error, use precedes declaration

3 int i;

46 void G() {
57 int j = (j = 1); // Valid

89 void H() {

10 int a = 1, b = ++a; // Valid

11 }
13En el método F anterior, la primera asignación de i no hace ninguna referencia concreta al campo declarado en el
14ámbito externo. En lugar de ello, hace referencia a la variable local, con lo que se produce un error durante la
12
15compilación, porque precede textualmente a la declaración de la variable. En el método G, el uso de j en el
16inicializador de la declaración de j es válido porque no precede al declarador de variable local (local-variable-
17declarator). En el método H, un declarador de variable local (local-variable-declarator) posterior hace
18referencia correctamente a una variable local declarada en un declarador de variable local (local-variable-
19declarator) anterior con la misma declaración de variable local (local-variable-declarator).
20Las reglas que rigen el ámbito de las variables locales pretenden garantizar que el significado de un nombre
21utilizado en el contexto de una expresión no varíe dentro de un bloque. Si el ámbito de una variable local sólo se
22extiende desde su declaración hasta el final del bloque, entonces, en el ejemplo anterior, la primera asignación
23asignará a la variable de instancia y la segunda a la variable local, lo que posiblemente producirá errores en
24tiempo de compilación si más adelante fuera necesario reorganizar las instrucciones del bloque.
25El significado de un nombre contenido en un bloque puede diferir según el contexto en que se utilice el nombre.
26En el siguiente ejemplo:
27 us ing Sys tem;
28 c lass A {}
29 c lass Tes t
30 {
31 s ta t i c vo id Main ( ) {
32 s t r i ng A = "he l l o , wor ld" ;
33
34 Type t = t ypeo f (A ) ; / / t ype contex t
35 Conso le .Wr i teL ine (s ) ; / / wr i tes "he l l o , wor ld"
36 Conso le .Wr i teL ine ( t ) ; / / wr i tes "A"
37 }
38 } A se utiliza en el contexto de una expresión para hacer referencia a la variable local A y en un
39el nombre
40contexto de tipo para hacer referencia a la clase A.

413.7.1 Ocultar nombres


42El ámbito de una entidad habitualmente abarca más texto del programa que el espacio de declaración de la
43entidad. En concreto, el ámbito de una entidad puede incluir declaraciones que introducen nuevos espacios de
44declaración que contienen entidades con el mismo nombre. Las declaraciones de este tipo hacen que la entidad
45original quede oculta (hidden). A la inversa, se dice que una entidad es visible cuando no está oculta.

17776 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


178 Capítulo 18 Código no seguro

1La ocultación de nombres se produce cuando los ámbitos se superponen a causa del anidamiento y cuando se
2superponen por herencia. Las características de los dos tipos de ocultación se explican en las próximas
3secciones.

43.7.1.1 Ocultar mediante anidación


5La ocultación de nombres por medio del anidamiento puede ser el resultado del anidamiento de espacios de
6nombres o tipos contenidos en espacios de nombres, la consecuencia del anidamiento de tipos dentro de clases o
7estructuras y el resultado de las declaraciones de parámetros y variables locales.

179Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 77


180Especificación del lenguaje C#

1En el siguiente ejemplo:

2 class A

3 {

45 void F() {

6 int i = 1;

78 void G() {

9 i = 1;

10 }
12dentro del método F, la variable de instancia i queda oculta por la variable local i, pero dentro del método G, i se
13
11sigue refiriendo a la variable de instancia.
14Cuando un nombre en un ámbito interno oculte otro nombre en el ámbito externo, ocultará también todas sus
15apariciones sobrecargadas. En el siguiente ejemplo:

16 class Outer

17 {

18
19 static void F(string s) {}
20 class Inner

21 {

22 void G() {

23 F(1); // Invokes Outer.Inner.F


26
24 static void F(long l) {}

27
25 }
29
28la llamada F(1) invoca la F declarada en Inner porque la declaración interna oculta todas las apariciones
30externas de F. Por el mismo motivo, la llamada F("Hello") produce un error en tiempo de compilación.

313.7.1.2 Ocultar mediante herencia


32La ocultación de nombres por medio de la herencia ocurre cuando clases o estructuras vuelven a declarar
33nombres que se han heredado de clases base. Este tipo de ocultación de nombres toma una de las siguientes
34formas:
35• Una constante, campo, propiedad, evento o tipo introducido en una clase o estructura que oculta todos los
36 miembros de la clase base con el mismo nombre.
37• Un método introducido en una clase o estructura que oculta todos los miembros de clase base no de método
38 con el mismo nombre, y todos los métodos de clase base con la misma firma (nombre de método y número
39 de parámetros, modificadores y tipos).
40• Indizador introducido en una clase o estructura que oculta todos los indizadores de la clase base con la
41 misma firma (número de parámetros y tipos).
42Las reglas que gobiernan las declaraciones de operador (§10.9) imposibilitan la declaración por una clase
43derivada de un operador con la misma firma que un operador de una clase base. Por lo tanto, los operadores
44nunca se ocultan mutuamente.

18178 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


182 Capítulo 18 Código no seguro

1Al contrario que cuando se oculta un nombre de un ámbito externo, cuando se oculta un nombre accesible desde
2un ámbito heredado se genera una advertencia. En el siguiente ejemplo:

3 class Base

4 {

5 public void F() {}


7 class Derived: Base
6
8 {

9 public void F() {} // Warning, hiding an inherited name


11la declaración de F en Der i vedgenera una advertencia. Ocultar un nombre heredado no es específicamente un
12
10error, puesto que esto impediría la evolución independiente de las clases base. Por ejemplo, la situación anterior
13podría haberse producido porque una versión posterior de Base introdujo un método F que no estaba presente
14en una versión anterior de la clase. Si la situación anterior hubiera sido un error, cualquier cambio realizado a
15una clase base en una versión independiente de la biblioteca de clases podría haber invalidado las clases
16derivadas.
17La advertencia causada por la ocultación de un nombre heredado puede eliminarse mediante uso del
18modificador new:

19 class Base

20 {

21 public void F() {}


23 class Derived: Base
22
24 {

25 new public void F() {}


27El modificador new indica que F de Derived es “nuevo”, y que su propósito real es ocultar el miembro
28
26heredado.
29Una declaración de un miembro nuevo oculta un miembro heredado sólo dentro del ámbito del nuevo miembro.

30 class Base

31 {

32 public static void F() {}


34 class Derived: Base
33
35 {

36 new private static void F() {} // Hides Base.F in Derived only


38 class MoreDerived: Derived
37
39 {

40 static void G() { F(); } // Invokes Base.F


42En el ejemplo anterior, la declaración de método F en la clase Derived oculta el método F que se heredó de
43
41Base pero, como el nuevo método F de Derived tiene acceso de tipo privado, su ámbito no se extiende a
44MoreDerived. De este modo, la llamada F() de MoreDerived.G es válida e invocará a Base.F.

183Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 79


184Especificación del lenguaje C#

13.8 Espacios de nombres y nombres de tipos


2Algunos contextos de un programa de C# requieren que se especifique un nombre de espacio de nombres
3(namespace-name) o un nombre de tipo (type-name). Cualquiera de los dos formatos de nombre se escribe como
4uno o más identificadores separados por símbolos (token) ".".
5 namespace-name:
6 namespace-or-type-name
7 type-name:
8 namespace-or-type-name
9 namespace-or-type-name:
10 identifier
11 namespace-or-type-name . identifier
12Un nombre de tipo (type-name) es un nombre de espacio de nombres o de tipo (namespace-or-type-name) que
13hace referencia a un tipo. Conforme a la resolución explicada más adelante, el nombre de espacio de nombres o
14de tipo (namespace-or-type-name) de un nombre de tipo (type-name) debe hacer referencia a un tipo o, de lo
15contrario, se producirá un error en tiempo de compilación.
16Un nombre de espacio de nombres (namespace-name) es un nombre de espacio de nombres o de tipo
17(namespace-or-type-name) que hace referencia a un espacio de nombres. Conforme a la resolución explicada
18más adelante, el nombre de espacio de nombres o de tipo (namespace-or-type-name) de un nombre de espacio
19de nombres (namespace-name) debe hacer referencia a un espacio de nombres o, de lo contrario, se producirá un
20error en tiempo de compilación.
21El significado de un nombre de espacio de nombres o de tipo (namespace-or-type-name) se determina como
22sigue:
23• Si el nombre de espacio de nombres o de tipo (namespace-or-type-name) consta de un solo identificador:
24 o Si el nombre de espacio de nombres o de tipo (namespace-or-type-name) aparece dentro del cuerpo de
25 la declaración de una clase o estructura, entonces, empezando con esa declaración de clase o estructura
26 y siguiendo con la declaración de cada clase o estructura envolvente (si la hay), si existe un miembro
27 con el nombre dado, es accesible y denota un tipo, el nombre de espacio de nombres o de tipo
28 (namespace-or-type-name) hace referencia a dicho miembro. Téngase en cuenta que los miembros que
29 no son de tipo (constantes, campos, métodos, propiedades, indizadores, operadores, constructores de
30 instancia, destructores y constructores estáticos) se omiten a la hora de determinar el significado de un
31 nombre de espacio de nombres o de tipo (namespace-or-type-name).
32 o O bien, empezando por el espacio de nombres en el que se produce el nombre de espacio de nombres o
33 de tipo (namespace-or-type-name), continuando por cada uno de los espacios de nombres envolventes
34 (si los hubiera) y terminando por el espacio de nombres global, se irán evaluando los siguientes pasos
35 hasta que se localice una entidad:
36 • Si el espacio de nombres contiene un miembro de espacio de nombres con el nombre dado, entonces
37 el nombre de espacio de nombres o de tipo (namespace-or-type-name) hace referencia a dicho
38 miembro y, dependiendo del miembro, se clasifica como un espacio de nombres o como un tipo.
39 • O bien, si el espacio de nombres tiene una declaración de espacio de nombres correspondiente que
40 abarca la ubicación donde se produce el nombre de espacio de nombres o de tipo (namespace-or-
41 type-name), entonces:
42 o Si la declaración de espacio de nombres contiene una directiva alias using (using-alias-
43 directive) que asocia el nombre dado a un espacio de nombres o tipo importado, entonces el

18580 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


186 Capítulo 18 Código no seguro

1 nombre de espacio de nombres o de tipo (namespace-or-type-name) hace referencia a dicho


2 espacio de nombres o tipo.
3 o O bien, si los espacios de nombres importados por las directivas using de espacio de nombres
4 (using-namespace-directives) de la declaración del espacio de nombres contienen exactamente
5 un único tipo con el nombre dado, entonces el nombre de espacio de nombres o de tipo
6 (namespace-or-type-name) hace referencia a dicho tipo.
7 o O bien, si los espacios de nombres importados por las directivas using de espacio de nombres
8 (using-namespace-directives) de la declaración del espacio de nombres contienen más de un
9 tipo con el mismo nombre dado, entonces el nombre de espacio de nombres o de tipo
10 (namespace-or-type-name) será ambiguo y se producirá un error.
11 o De lo contrario, el nombre de espacio de nombres o de tipo (namespace-or-type-name) no está definido
12 y se produce un error en tiempo de compilación.
13• O bien, el nombre de espacio de nombres o de tipo (namespace-or-type-name) tiene el formato N. I, donde N
14 es un nombre de espacio de nombres o de tipo (namespace-or-type-name) compuesto por todos los
15 identificadores excepto el situado más a la derecha, e I es el identificador situado más a la derecha. N se
16 resuelve en primer lugar como nombre de espacio de nombres o de tipo (namespace-or-type-name). Si la
17 resolución de N no es correcta, se produce un error en tiempo de compilación. De lo contrario, N.I se
18 resuelve como sigue:
19 o Si N es un espacio de nombres e I es el nombre de un miembro accesible de dicho espacio de nombres,
20 entonces N.I hace referencia a dicho miembro y, dependiendo del miembro, se clasifica como un espacio
21 de nombres o un tipo.
22 o Si N es un tipo de clase o estructura e I es el nombre de un tipo accesible contenido en N, entonces N.I
23 hace referencia a dicho tipo.
24 o De lo contrario, N.I es un nombre de espacio de nombres o de tipo no válido (invalid namespace-or-
25 type-name) y se produce un error en tiempo de compilación.

263.8.1 Nombres completos


27Todos los espacios de nombres y todos los tipos tienen un nombre completo, que los identifica de forma
28exclusiva. El nombre completo de un espacio de nombres o un tipo N se determina como sigue:
29• Si N es un miembro del espacio de nombres global, su nombre completo es N.
30• De lo contrario, su nombre completo es S.N, donde S es el nombre completo del espacio de nombres o el
31 tipo donde N está declarado.
32Esto es, el nombre completo de N es la ruta de acceso jerárquica completa de los identificadores que conducen a
33N, empezando desde el espacio de nombres global. Dado que todos los miembros de un espacio de nombres o un
34tipo deben tener un nombre único, se deduce que el nombre completo de un espacio de nombres o tipo siempre
35es único.
36En el ejemplo siguiente se muestran varias declaraciones de espacio de nombres y tipo con sus nombres
37completos asociados.
38 c lass A {} // A

187Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 81


188Especificación del lenguaje C#

1 namespace X // X

2 {

3 class B // X.B

4 {
75 namespace Y // X.Y

86 {

9 class D {} // X.Y.D

10
12 namespace X.Y // X.Y
11
13 {

14 class E {} // X.Y.E
163.9 Administración automática de la memoria
15
17C# usa la administración automática de memoria, que exime a los programadores de la asignación manual y la
18liberación de la memoria ocupada por objetos. Las directivas de administración automática de la memoria se
19implementan mediante un recolector de elementos no utilizados. El ciclo de vida de la administración de la
20memoria de un objeto es:
211. Cuando se crea el objeto, se le asigna memoria, se ejecuta el constructor y el objeto se considera activo.
222. En caso de que no se pueda obtener acceso al objeto o alguna de sus partes por medio de las posibles
23 continuaciones de la ejecución, en lugar de iniciar los destructores, el objeto dejará de considerarse en uso y
24 quedará expuesto al proceso de destrucción. El compilador de C# y el recolector de elementos no utilizados
25 pueden optar por analizar el código para determinar qué referencias a un objeto podrán utilizarse en el
26 futuro. Por ejemplo, si una variable local que se encuentra dentro del ámbito es la única referencia existente
27 a un objeto, pero no se hace ninguna referencia a tal variable local en ninguna continuación posible de la
28 ejecución desde el punto actual de ejecución dentro del procedimiento, el recolector de elementos no
29 utilizados puede tratar los objetos (aunque no necesariamente) como si ya no estuvieran en uso.
303. Cuando el objeto ya es candidato a la destrucción, el destructor del objeto (si existe) se ejecuta en un
31 momento posterior no especificado (§10.12). Salvo que se realicen llamadas explícitas, el destructor del
32 objeto sólo se ejecuta una vez.
334. Si, una vez ejecutado el destructor de un objeto, las posibles continuaciones de la ejecución (incluida la
34 ejecución de los destructores) no pudieran obtener acceso al objeto o a alguna de sus partes, se considerará
35 que ya no se encuentra accesible y quedará expuesto al proceso de recolección.
365. Por último, cuando el objeto ya es candidato para la recolección, en un momento posterior, el recolector de
37 elementos no utilizados libera la memoria asociada al objeto.
38El recolector de elementos no utilizados mantiene información acerca de la utilización de objetos y la utiliza
39para tomar decisiones sobre la administración de memoria. Por ejemplo, contiene datos como en qué parte de la
40memoria se ubica un objeto recién creado, cuándo se reubica un objeto y cuándo un objeto ya no se utiliza o está
41inaccesible.
42Igual que otros lenguajes que dan por supuesta la existencia de un recolector de elementos no utilizados, C# se
43ha diseñado para que el recolector de elementos no utilizados pueda implementar una amplia gama de directivas
44de administración de la memoria. Por ejemplo, C# no exige que se lleve a cabo la ejecución de destructores ni la
45recolección de objetos en el momento en que pasen a ser candidatos a ella, ni que los destructores se ejecuten en
46un orden concreto o en un subproceso concreto.

18982 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


190 Capítulo 18 Código no seguro

1El comportamiento del recolector de elementos no utilizados puede controlarse, en cierta medida, mediante
2métodos estáticos de la clase Sys tem.GC , una clase que puede utilizarse para solicitar la ejecución de la
3recolección o de los destructores (o su no ejecución), etc.
4Dado que el recolector de elementos no utilizados tiene una amplia libertad para decidir cuándo debe recolectar
5objetos y ejecutar destructores, una implementación compatible puede producir resultados diferentes de los
6mostrados en el código siguiente. El programa

7 using System;
8 class A

9 {

10 ~A() {

11 Console.WriteLine("Destruct instance of A");


14
12 class B

15
13 {

16
17 public B(object o) {

18 Ref = o;

19
20 ~B() {

21 Console.WriteLine("Destruct instance of B");

22 }
24 class Test
23
25 {

26 static void Main() {

27 B b = new B(new A());

28 b = null;

29 GC.Collect();
33crea una instancia de la clase A y otra de la clase B. Estos objetos se convierten en candidatos a la recolección de
34elementos no utilizados cuando se asigna a la variable b el valor null, puesto que, después de ello, no se puede
30
35obtener acceso a ellos mediante ningún código escrito por el usuario. El resultado podría ser
31
36 Destruct instance of A
32
37o bien Destruct instance of B
38

39 Destruct instance of B

40 Destruct
41puesto que instance
el lenguaje no imponeof A
restricciones al orden en que se lleva a cabo la recolección de elementos de los
42objetos no utilizados.
43En casos concretos, la distinción entre “candidato a la destrucción” y “candidato a la recolección” puede ser
44importante. Por ejemplo,

45 using System;

191Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 83


192Especificación del lenguaje C#

1 class A

2 {

3 ~A() {

46 public void F() {


57 Console.WriteLine("A.F");

8 Test.RefA = this;

119 class B
10
12 {

13
14 ~B() {

15 Console.WriteLine("Destruct instance of B");

16 Ref.F();

17
19 class Test
18
20 {

21 public static A RefA;


23 static void Main() {
22
24 RefB = new B();

25 RefA = new A();

26 RefB.Ref = RefA;
29
27 // A and B now eligible for destruction

30
28 GC.Collect();

31
32 // B now eligible for collection, but A is not

33 if (RefA != null)

34 Console.WriteLine("RefA is not null");


37En el programa anterior, si el recolector de elementos no utilizados decide ejecutar el destructor de A antes que
35
38el de B, el resultado de este programa puede ser:
36
39 Destruct instance of A

40 Destruct instance of B

41 A.F
43Debe tenerse en cuenta que, aunque la instancia de A no estaba en uso cuando se ejecutó el destructor de A,
44
42todavía es posible llamar a los métodos de A (en este caso, F) desde otro destructor. Asimismo, no debe
45olvidarse que la ejecución de un destructor puede hacer que un objeto vuelva a ser utilizable desde el programa
46principal. En este caso, la ejecución del destructor de B hace que una instancia de A que no estaba anteriormente
47en uso esté ahora accesible desde la referencia activa Test.RefA. Tras la llamada a WaitForPendingFinalizers,
48la instancia de B pasa a quedar expuesta a la recolección, pero no así la instancia de A, debido a la referencia
49Test.RefA.

19384 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


194 Capítulo 18 Código no seguro

1Para evitar confusiones y comportamientos inesperados, generalmente es recomendable que los destructores
2solamente realicen la limpieza de los datos almacenados en los campos propios de sus objetos, y que no realicen
3acciones en los objetos a los que se hace referencia o en campos estáticos.

43.10 Orden de ejecución


5La ejecución de los programas de C# se realiza de tal modo que los efectos secundarios de cada subproceso en
6ejecución se van conservando en puntos críticos de ejecución. Un efecto secundario se define como la lectura o
7escritura de un campo volátil, la escritura en una variable no volátil, la escritura en un recurso externo o el inicio
8de una excepción. Los puntos de ejecución críticos en los que deben conservarse tales efectos secundarios son
9las referencias a campos volátiles (§10.4.3), las instrucciones de bloqueo l ock(§8.12) y la creación y
10terminación de subprocesos. El entorno de ejecución puede cambiar el orden de ejecución de un programa de
11C#, con las siguientes restricciones:
12• Se conservará la dependencia de datos dentro de un subproceso de ejecución. Es decir, el valor de cada
13 variable se calculará como si todas las instrucciones del subproceso se ejecutaran en el orden del programa
14 original.
15• Se conservan las reglas de orden de inicialización (§10.4.4 y §10.4.5).
16• Se conserva el orden de los efectos secundarios respecto a las lecturas y escrituras volátiles (§10.4.3).
17 Además, el entorno de ejecución no necesitará evaluar parte de una expresión si puede deducir que el valor
18 de esa expresión no se utiliza y que no se producen efectos secundarios necesarios (incluidos los causados
19 por la llamada a un método o el acceso a un campo volátil). Cuando un evento asincrónico (tal como una
20 excepción iniciada por otro subproceso) interrumpe la ejecución de un programa, no habrá garantías de que
21 los efectos secundarios observables resulten visibles en el orden del programa original.

195Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 85


196Especificación del lenguaje C#

1 4.Tipos

2Los tipos del lenguaje C# se dividen en dos categorías principales: tipos de valor y tipos de referencia.
3 type:
4 value-type
5 reference-type
6Una tercera categoría de tipos, los punteros, sólo está disponible en el código no seguro. Esta categoría se
7explica con más detalle en la sección §18.2.
8Los tipos de valor se diferencian de los tipos de referencia en que sus variables contienen directamente sus
9datos, mientras que las variables de los tipos de referencia contienen referencias a sus datos, que se conocen
10como objetos. En el caso de los tipos de referencia, es posible que dos variables hagan referencia al mismo
11objeto y, por tanto, que las operaciones en una variable afecten al objeto al que hace referencia la otra variable.
12En el caso de los tipos de valor, cada variable tiene su propia copia de los datos, de manera que no es posible
13que las operaciones de una afecten a la otra.
14El sistema de tipos de C# está unificado, de manera que un valor de cualquier tipo puede tratarse como un
15objeto. Todos los tipos de C# se derivan directa o indirectamente del tipo de clase ob jec t, que es la clase base
16definitiva de todos los tipos. Los valores de los tipos de referencia se tratan como objetos considerándolos
17sencillamente como del tipo ob jec t. Los valores de los tipos de valor se tratan como objetos mediante la
18realización de operaciones de conversión boxing y unboxing (§4.3).

194.1 Tipos de valor


20Un tipo de valor es un tipo struct o un tipo enum. C# proporciona un conjunto de tipos struct predefinidos
21denominados tipos simples. Los tipos simples se identifican mediante palabras reservadas.
22 value-type:
23 struct-type
24 enum-type
25 struct-type:
26 type-name
27 simple-type
28 simple-type:
29 numeric-type
30 boo l
31 numeric-type:
32 integral-type
33 floating-point-type
34 dec ima l

19786 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


198 Capítulo 18 Código no seguro

1 integral-type:
2 sby te
3 byte
4 shor t
5 ushor t
6 int
7 u in t
8 l ong
9 u long
10 char
11 floating-point-type:
12 f l oa t
13 doub le
14 enum-type:
15 type-name
16Una variable de un tipo de valor siempre contiene un valor de dicho tipo. A diferencia de los tipos de referencia,
17el valor de un tipo de valor no puede ser nu l lni puede hacer referencia a un objeto de un tipo más derivado.
18La asignación a una variable de un tipo de valor crea una copia del valor que se asigna, lo cual difiere de la
19asignación a una variable de un tipo de referencia, que copia la referencia, pero no el objeto identificado por
20ella.

214.1.1 Tipo System.ValueType


22Todos los tipos de valor se heredan implícitamente de la clase Sys tem.Va lueType, que, a su vez, se hereda de
23la clase ob jec t. No es posible que cualquier tipo se derive de un tipo de valor y, por lo tanto, los tipos de valor
24son implícitamente tipos sealed (§10.1.1.2).
25Observe que Sys tem.Va lueTypeno es en sí ningún tipo de valor (value-type). Más bien, es un tipo de clase
26(class-type) del que se derivan automáticamente todos los tipos de valores (value-types).

274.1.2 Constructores predeterminados


28Todos los tipos de valor declaran implícitamente un constructor de instancia público sin parámetros denominado
29constructor predeterminado. El constructor predeterminado devuelve una instancia inicializada en cero
30conocida como el valor predeterminado del tipo de valor:
31• Para todos los tipos simples (simple-types), el valor predeterminado es el generado por un modelo de bits de
32 todo ceros:
33 o Para sby te, byte, short, ushort, int, uint, long y ulong, el valor predeterminado es 0.
34 o Para char, el valor predeterminado es ' \ x0000. '
35 o Para f l oa,tel valor predeterminado es 0 .0 f.
36 o Para doub le, el valor predeterminado es 0 .0d.
37 o Para dec ima l, el valor predeterminado es 0 .0m.
38 o Para boo l, el valor predeterminado es f a l se
.
39• Para un tipo enum (enum-type) E, el valor predeterminado es 0.
40• Para un tipo de estructura (struct-type), el valor predeterminado es el que se genera al configurar todos los
41 campos de tipo de valor en su valor predeterminado y todos los tipos de referencia en nu l .l

199Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 87


200Especificación del lenguaje C#

1Igual que con cualquier otro constructores de instancia, se llama al constructor predeterminado de un tipo de
2valor mediante el operador new . Por razones de eficacia, el objetivo de este requisito no es lograr que la
3implementación genere una llamada de constructor. En el ejemplo siguiente, las dos variables i y j se inicializan
4en cero.

5 class A

6 {

7 void F() {

8 int i = 0;

129Dado que todos los


inttipos
j =denew
valorint();
implícitamente tienen un constructor de instancias público sin parámetros, un
13tipo struct no puede contener una declaración explícita de un constructor sin parámetros. No obstante, en un tipo
10
14struct se pueden declarar constructores de instancia con parámetros (§11.3.8).
11
154.1.3 Tipos de estructura
16Un tipo struct es un tipo de valor que puede declarar constantes, campos, métodos, propiedades, indizadores,
17operadores, constructores de instancia, constructores estáticos y tipos anidados. Los tipos de estructura se
18describen en §11.

194.1.4 Tipos simples


20C# proporciona un conjunto de tipos struct predefinidos denominados tipos simples. Los tipos simples se
21identifican mediante palabras reservadas, pero éstas son sencillamente alias de tipos struct predefinidos del
22espacio de nombres System, como se explica en la tabla siguiente.
23

Palabra reservada Tipo con alias


sbyte System.SByte
byte System.Byte
short System.Int16
ushort System.UInt16
int System.Int32
uint System.UInt32
long System.Int64
ulong System.UInt64
char System.Char
float System.Single
double System.Double
bool System.Boolean
decimal System.Decimal
24
25Como un tipo simple equivale a un tipo struct, todos los tipos simples tienen miembros. Por ejemplo, int tiene
26los miembros declarados en System.Int32 y los miembros heredados de System.Object, y se permiten las
27siguientes instrucciones:

20188 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


202 Capítulo 18 Código no seguro

1 int i = int.MaxValue; // System.Int32.MaxValue constant

2 string s = i.ToString(); // System.Int32.ToString() instance method


43Los tipos simples se diferencian de otros tipos struct en que permiten determinadas operaciones adicionales:
5• La mayoría de los tipos simples permiten la creación de valores mediante la escritura de literales (§2.4.4).
6 Por ejemplo, 123 es un literal del tipo i n ty 'a' es un literal del tipo char. C# no proporciona literales de
7 tipos struct en general, y los valores no predeterminados de otros tipos struct se crean siempre en último
8 término mediante constructores de instancia de dichos tipos struct.
9• Cuando todos los operandos de una expresión son constantes de tipo simple, es posible que el compilador
10 evalúe la expresión en tiempo de compilación. Dicha expresión se conoce por el nombre de expresión
11 constante (constant-expression) (§7.15). Las expresiones que incluyen operadores definidos por otros tipos
12 struct no se consideran expresiones constantes.
13• Mediante las declaraciones const es posible declarar constantes de los tipos simples (§10.3). No es posible
14 tener constantes de otros tipos struct, pero se consigue un efecto similar mediante campos static readonly.
15• Las conversiones que involucran tipos simples pueden participar en la evaluación de los operadores de
16 conversión definidos por otros tipos struct, pero un operador de conversión definido por el usuario no puede
17 participar en la evaluación de otros operadores definidos por el usuario (§6.4.2).

184.1.5 Tipos integrales


19C# admite nueve tipos integrales: sbyte, byte, short, ushort, int, uint, long, ulong y char. Los tipos
20integrales tienen los siguientes tamaños e intervalos de valores:
21• El tipo sbyte, que representa enteros de 8 bits con signo con valores comprendidos entre -128 y 127.
22• El tipo byte, que representa enteros de 8 bits sin signo con valores comprendidos entre 0 y 255.
23• El tipo short, que representa enteros de 16 bits con signo con valores comprendidos entre –32768 y 32767.
24• El tipo ushor t, que representa enteros de 16 bits sin signo con valores comprendidos entre 0 y 65535.
25• El tipo int, que representa enteros de 32 bits con signo con valores comprendidos entre –2147483648 y
26 2147483647.
27• El tipo u in t, que representa enteros de 32 bits sin signo con valores comprendidos entre 0 y 4294967295.
28• El tipo long, que representa enteros de 64 bits con signo con valores comprendidos entre –
29 9223372036854775808 y 9223372036854775807.
30• El tipo u long, que representa enteros de 64 bits sin signo con valores comprendidos entre 0 y
31 18446744073709551615.
32• El tipo char representa enteros sin signo de 16 bits con valores entre 0 y 65535. El conjunto de valores
33 posibles para el tipo char corresponde al conjunto de caracteres Unicode. Aunque char tiene la misma
34 representación que ushort, no todas las operaciones permitidas en un tipo están permitidas en el otro.
35Los operadores unarios y binarios de tipos integrales siempre trabajan con precisión de 32 bits con signo,
36precisión de 32 bits sin signo, precisión de 64 bits con signo o precisión de 64 bits sin signo:
37• Para los operadores unarios + y ~, el operando se convierte al tipo T, donde T es el primero de int, uint,
38 long y ulong que puede representar completamente todos los valores posibles del operando. Después se
39 ejecuta la operación con la precisión del tipo T, y el tipo del resultado es T.

203Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 89


204Especificación del lenguaje C#

1• Para el operador unario –, el operando se convierte al tipo T, donde T es el primero de int y long que puede
2 representar completamente todos los valores posibles del operando. Después se ejecuta la operación con la
3 precisión del tipo T, y el tipo del resultado es T. El operador unario - no se puede aplicar a operandos del
4 tipo ulong.
5• Para los operadores binarios +, –, *, /, %, &, ^, |, ==, !=, >, <, >= y <=, los operandos se convierten al
6 tipo T, donde T es el primero de int, uint, long y ulong que puede representar completamente todos los
7 valores posibles de los dos operandos. Después se ejecuta la operación con la precisión del tipo T, y el tipo
8 del resultado es T (o bool para los operadores relacionales). Los operadores binarios no permiten que un
9 operando sea de tipo long y el otro de tipo ulong.
10• Para los operadores binarios << y >>, el operando izquierdo se convierte al tipo T, donde T es el primero
11 de int, uint, long y ulong que puede representar completamente todos los valores posibles del operando.
12 Después se ejecuta la operación con la precisión del tipo T, y el tipo del resultado es T.
13El tipo char se clasifica como un tipo integral, pero difiere de otros tipos integrales en dos aspectos:
14• No existen conversiones implícitas desde otros tipos al tipo char. En concreto, aunque los tipos sbyte,
15 byte y ushort tienen intervalos de valores que son totalmente representables mediante el tipo char, las
16 conversiones implícitas de sbyte, byte o ushort a char no existen.
17• La constantes de tipo char deben escribirse como literales de caracteres (character-literals) o como literales
18 de enteros (integer-literals) en combinación con una conversión al tipo char. Por ejemplo, (char)10 es lo
19 mismo que '\x000A'.
20Los operadores e instrucciones checked y unchecked se utilizan para controlar la comprobación del
21desbordamiento para las operaciones y conversiones aritméticas de tipos integrales (§7.5.12). En un contexto
22checked, un desbordamiento produce un error de tiempo de compilación o causa una excepción
23System.OverflowException. En un contexto unchecked, los desbordamientos no se tienen en cuenta y los
24bits de orden superior que no son aceptables para el tipo de destino se descartan.

254.1.6 Tipos de punto flotante


26C# admite dos tipos de punto flotante: float y double. Los tipos float y double se representan mediante los
27formatos IEEE 754 de 32 bits de precisión simple y de 64 bits de precisión doble, que proporcionan los
28siguientes conjuntos de valores:
29• Cero positivo y cero negativo. En la mayoría de las situaciones, cero positivo y cero negativo tienen un
30 comportamiento idéntico al del valor simple cero, pero algunas operaciones distinguen entre los dos
31 (§7.7.2).
32• Infinito positivo e infinito negativo. Los infinitos son generados por operaciones como dividir por cero un
33 número distinto de cero. Por ejemplo, el resultado de 1.0 / 0.0 es infinito positivo, y el de –1.0 / 0.0 es
34 infinito negativo.
35• El valor no numérico (Not-a-Number), abreviado normalmente como NaN. Los valores NaN se generan por
36 operaciones de punto flotante no válidas, como dividir cero por cero.
37• El conjunto finito de valores distintos de cero con el formato s × m × 2e, donde s es 1 o −1, y m y e están
38 determinados por el tipo de punto flotante concreto: para float, 0 < m < 224 y −149 ≤ e ≤ 104, y para
39 double, 0 < m < 253 y −1075 ≤ e ≤ 970. Los números de punto flotante sin normalizar se consideran valores
40 válidos distintos de cero.
41El tipo float puede representar valores comprendidos entre aproximadamente 1.5 × 10−45 y 3.4 × 1038 con una
42precisión de 7 dígitos.

20590 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


206 Capítulo 18 Código no seguro

1El tipo doub le puede representar valores comprendidos entre aproximadamente 5.0 × 10−324 y 1.7 × 10308 con
2una precisión de 15-16 dígitos.
3Si uno de los operandos de un operador binario es un tipo de punto flotante, el otro debe ser un tipo integral o un
4tipo de punto flotante, y la operación se evalúa como sigue:
5• Si uno de los operandos es un tipo integral, se convierte al tipo de punto flotante del otro operando.
6• Después, si uno de los operandos es de tipo doub le, el otro se convierte a doub le, la operación se ejecuta
7 utilizando por lo menos el intervalo y la precisión double, y el tipo del resultado es double (o bool para los
8 operadores relacionales).
9• O bien, la operación se ejecuta utilizando por lo menos el intervalo y la precisión float y el tipo del
10 resultado es float (o bool para los operadores relacionales).
11Los operadores de punto flotante, incluidos los operadores de asignación, nunca producen excepciones. En lugar
12de ello, en situaciones excepcionales, las operaciones de punto flotante producen cero, infinito o NaN, como se
13explica a continuación:
14• Si el resultado de una operación de punto flotante es demasiado pequeño para el formato de destino, el
15 resultado de la operación es cero positivo o cero negativo.
16• Si el resultado de una operación de punto flotante es demasiado grande para el formato de destino, el
17 resultado de la operación es infinito positivo o infinito negativo.
18• Si una operación de punto flotante no es válida, el resultado de la operación es NaN.
19• Si uno o los dos operandos de una operación de punto flotante es NaN, el resultado de la operación es NaN.
20Las operaciones de punto flotante pueden realizarse con mayor precisión que el tipo de resultado de la
21operación. Por ejemplo, algunas arquitecturas de hardware admiten el tipo de punto flotante “extended” o “long
22double” con un intervalo y precisión mayores que el tipo double, e implícitamente realizan todas las
23operaciones de punto flotante utilizando este tipo de mayor precisión. Se puede conseguir que las arquitecturas
24de hardware de esta clase realicen operaciones de punto flotante con menor precisión sólo a cambio de un costo
25excesivo en el rendimiento; en vez de requerir una implementación que penalice tanto el rendimiento como la
26precisión, C# permite utilizar un tipo de mayor precisión para todas las operaciones de punto flotante. Aparte de
27proporcionar resultados más precisos, esto rara vez tiene efectos medibles. No obstante, en expresiones con la
28forma x * y / z, donde la multiplicación genera un resultado que sale del intervalo de double, pero la siguiente
29división devuelve el resultado temporal al intervalo de double, el hecho de que la expresión se evalúe en un
30formato de intervalo mayor puede producir un resultado finito en lugar de infinito.

314.1.7 Tipo decimal


32El tipo decimal es un tipo de datos de 128 bits apto para cálculos financieros y monetarios. El tipo decimal
33puede representar valores comprendidos entre aproximadamente 1.0 × 10−28 y 7.9 × 1028 con 28-29 dígitos
34significativos.
35El conjunto finito de valores de tipo decimal tiene la forma (–1)s × c × 10-e, donde el signo s es 0 o 1, el
36coeficiente c viene dado por 0 ≤ c < 296 y la escala e es 0 ≤ e ≤ 28. El tipo decimal no admite ceros con signo,
37valores infinitos ni valores NaN. Un decimal se representa como un entero de 96 bits elevado a diez. Para
38valores decimal con un valor absoluto menor que 1.0m, el valor es exacto sólo hasta la posición decimal 28.
39Para valores decimal con un valor absoluto mayor o igual que 1.0m, el valor es exacto hasta el dígito 28 o 29.
40Al contrario que los tipos de datos float y double, los números fraccionarios decimales como 0,1 pueden
41representarse exactamente en la representación decimal. En las representaciones float y double, estos números
42con frecuencia son fracciones infinitas, lo que las hace más susceptibles de errores de redondeo.

207Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 91


208Especificación del lenguaje C#

1Si uno de los operandos de un operador binario es de tipo dec ima l, el otro debe ser un tipo integral o un tipo
2dec ima l. Si está presente un operando de tipo integral, se convertirá a decimal antes de realizar la operación.
3El resultado de una operación con valores de tipo decimal es el que resultaría de calcular un resultado exacto
4(conservando la escala, según lo definido por cada operador) y, a continuación, redondearlo para ajustarse a la
5representación. El resultado se redondea hasta el valor representable más próximo y, en caso de que un resultado
6tenga la misma proximidad con dos valores representables, se redondea al valor que tenga un número par en la
7posición del dígito menos significativo (esto se conoce como “redondeo de banca”). Un resultado cero siempre
8tiene un signo 0 y una escala 0.
9Si una operación aritmética decimal genera un valor menor o igual que 5 x 10-29 en valor absoluto, el resultado
10de la operación es cero. Si una operación aritmética decimal genera un resultado demasiado grande para el
11formato decimal, se origina una excepción System.OverflowException.
12El tipo decimal tiene una precisión mayor, pero un intervalo menor, que los tipos de punto flotante. Por lo
13tanto, las conversiones de los tipos de punto flotante a decimal pueden producir excepciones de
14desbordamiento, y las conversiones de decimal a los tipos de punto flotante pueden causar una pérdida de
15precisión. Por estos motivos, no existen conversiones implícitas entre los tipos de punto flotante y decimal, y
16sin conversiones explícitas, no es posible combinar operandos de punto flotante y decimal en la misma
17expresión.

184.1.8 Tipo bool


19El tipo bool representa cantidades lógicas booleanas. Los valores posibles del tipo bool son true y false.
20No existen conversiones estándar entre bool y otros tipos. En concreto, el tipo bool es único y exclusivo de los
21tipos integrales, y un valor bool no puede sustituir a un valor integral, ni viceversa.
22En los lenguajes C y C++, un valor integral o de punto flotante cero, o un puntero de valor null, pueden
23convertirse al valor booleano false, y un valor integral o de punto flotante distinto de cero o un puntero con un
24valor distinto de null pueden convertirse al valor booleano true. En C#, las conversiones de esta categoría se
25llevan a cabo mediante la comparación explícita de un valor integral o de punto flotante con cero, o mediante la
26comparación explícita de una referencia de objeto con null.

274.1.9 Tipos de enumeración


28Un tipo de enumeración es un tipo exclusivo con constantes con nombre. Todos los tipos de enumeración tienen
29un tipo subyacente, que debe ser byte, sbyte, short, ushort, int, uint, long o ulong. El conjunto de valores
30del tipo de enumeración es el mismo que el del conjunto de valores del tipo subyacente. Los valores del tipo de
31la enumeración no se restringen a los valores de las constantes nombradas. Los tipos de enumeración se definen
32mediante declaraciones de enumeración (§14.1).

334.2 Tipos de referencia


34Un tipo de referencia es un tipo de clase, un tipo de interfaz, un tipo de matriz o un tipo delegado.
35 reference-type:
36 class-type
37 interface-type
38 array-type
39 delegate-type
40 class-type:
41 type-name
42 ob jec t
43 s t r i ng

20992 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


210 Capítulo 18 Código no seguro

1 interface-type:
2 type-name
3 array-type:
4 non-array-type rank-specifiers
5 non-array-type:
6 type
7 rank-specifiers:
8 rank-specifier
9 rank-specifiers rank-specifier
10 rank-specifier:
11 [ dim-separatorsopt ]
12 dim-separators:
13 ,
14 dim-separators ,
15 delegate-type:
16 type-name
17Un valor de tipo de referencia es una referencia a una instancia del tipo, que se conoce como objeto. El valor
18especial nu l les compatible con todos los tipos de referencia e indica la ausencia de una instancia.

194.2.1 Tipos de clase


20Un tipo de clase define una estructura de datos que contiene miembros de datos (constantes y campos),
21miembros de función (métodos, propiedades, eventos, indizadores, operadores, constructores de instancia,
22destructores y constructores estáticos) y tipos anidados. Los tipos de clase admiten la herencia, un mecanismo
23mediante el cual una clase derivada puede extender y especializar a las clases base. Las instancias de los tipos de
24clase se crean mediante expresiones de creación de objetos (object-creation-expressions) (§7.5.10.1).
25Los tipos de clase se describen en §10.
26Ciertos tipos de clases predefinidos tienen un significado especial en el lenguaje C#, tal como se describe en la
27siguiente tabla.
28

Tipo de clase Descripción


System.Object Clase base definitiva de todos los demás tipos. Vea §4.2.2.
System.String El tipo de cadena del lenguaje de C#. Vea §4.2.3.
System.ValueType La clase base de todos los tipos de valor. Vea §4.1.1.
System.Enum La clase base de todos los tipos enum. Vea §14.
System.Array La clase base de todos los tipos de matriz. Vea §12.
System.Delegate La clase base de todos los tipos de delegado. Vea §15.
System.Exception La clase base de todos los tipos de excepción. Vea §16.
29
304.2.2 Tipo object
31El tipo de clase ob jec tes la clase base definitiva de todos los demás tipos. Todos los tipos de C# se derivan
32directa o indirectamente del tipo de clase ob jec t.

211Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 93


212Especificación del lenguaje C#

1La palabra clave ob jec tno es más que un alias de la clase predefinida Sys tem.Ob jec t.

24.2.3 Tipo string


3El tipo s t r i nges un tipo de clase sealed que hereda directamente de ob jec t. Las instancias de la clase string
4representan cadenas de caracteres Unicode.
5Los valores del tipo string pueden escribirse como literales de cadena (§2.4.4).
6La palabra clave s t r i ngno es más que un alias de la clase predefinida Sys tem.S t r i ng
.

74.2.4 Tipos de interfaz


8Una interfaz define un contrato. Una clase o estructura que implementa una interfaz debe adherirse a su
9contrato. Una interfaz puede derivarse de varias interfaces base, y una clase o estructura puede implementar
10varias interfaces.
11Los tipos de interfaz se describen en §13.

124.2.5 Tipos de matriz


13Una matriz es una estructura de datos que contiene cero o más variables, a las que se obtiene acceso a través de
14índices calculados. Las variables contenidas en una matriz, también conocidas como elementos de la matriz, son
15todas del mismo tipo, denominado tipo de elemento de la matriz.
16Los tipos de matriz se describen en §12.

174.2.6 Tipos de delegados


18Un delegado es una estructura de datos que se refiere a uno o más métodos y, por métodos de instancia, se
19refiere también a sus correspondientes instancias de objeto.
20El equivalente más cercano de un delegado en C o C++ es un puntero a una función pero, mientras que éste sólo
21puede hacer referencia a funciones estáticas, un delegado puede hacer referencia tanto a métodos estáticos como
22a métodos de instancia. En este caso, el delegado no sólo almacena una referencia al punto de entrada del
23método, sino que también almacena una referencia a la instancia de objeto para el que se invoca el método.
24Los tipos de delegado se describen en §15.

254.3 Boxing y Unboxing


26El concepto de boxing y unboxing desempeña un papel central en el sistema de tipos de C#. Sirven como enlace
27entre los tipos de valor (value-types) y los tipos de referencia (reference-types), al permitir la conversión de
28cualquier valor de un tipo de valor (value-type) al tipo ob jec ty viceversa. Las conversiones boxing y unboxing
29habilitan una visión unificada del sistema de tipos en la que un valor de cualquier tipo puede tratarse
30básicamente como un objeto.

314.3.1 Conversiones boxing


32Una conversión boxing permite que un tipo de valor (value-type) se convierta implícitamente en un tipo de
33referencia (reference-type). Existen los siguientes tipos de conversiones boxing:
34• De cualquier tipo de valor (value-type), incluidos los tipos enum (enum-type), al tipo ob jec t.
35• De cualquier tipo de valor (value-type), incluidos los tipos enum (enum-type), al tipo Sys tem.Va lueType.
36• De cualquier tipo de valor (value-type) a cualquier tipo de interfaz (interface-type) implementada por el tipo
37 de valor (value-type).

21394 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


214 Capítulo 18 Código no seguro

1• De cualquier tipo enum (enum-type) al tipo Sys tem.Enum .


2La conversión boxing de un valor a un tipo de valor (value-type) consiste en asignar una instancia del objeto y
3después copiar en ella el valor del tipo de valor (value-type).
4El proceso real de conversión boxing del valor de un tipo de valor (value-type) se entiende mejor si uno se
5imagina la existencia de una clase boxing para ese tipo. Para cualquier tipo de valor (value-type) T, la clase
6boxing se comporta como si se declarara de la manera siguiente:
7 sea led c lass T_Box : Sys tem.Va lueType
8 {
9 T va lue ;
10 pub l i c T_Box(T t ) {
11 va lue = t ;
12 }
13 }
14La conversión boxing de un valor v de tipo T consiste ahora en ejecutar la expresión new T_Box(v) y devolver
15la instancia resultante como un valor de tipo object. Por lo tanto, las instrucciones
16 i n t i = 123 ;
17 ob jec t box = i ;
18conceptualmente se corresponden con
19 i n t i = 123 ;
20 ob jec t box = new i n t _Box ( i ) ;
21La conversión boxing de clases como T_Box e int_Box mencionadas anteriormente no existe en realidad y el
22tipo dinámico de un valor al que se ha aplicado la conversión boxing no es realmente un tipo de clase. En lugar
23de ello, un valor convertido mediante boxing de tipo T tiene el tipo dinámico T, y una comprobación tipo
24dinámica que usa el operador is sencillamente puede hacer referencia al tipo T. Por ejemplo,
25 i n t i = 123 ;
26 ob jec t box = i ;
27 i f (box i s i n t ) {
28 Conso le .Wr i te ( "Box conta ins an i n t " ) ;
29
30devolverá la cadena “Box contains an int” en la consola.
31Una conversión boxing implica la creación de una copia del valor al que se aplica la conversión. Esto es distinto
32de la conversión de un tipo de referencia (reference-type) a un tipo ob jec t, en la cual el valor sigue haciendo
33referencia a la misma instancia y sencillamente se considera como el tipo ob jec tmenos derivado. Por ejemplo,
34dada la declaración
35 s t ruc t Po in t
36 {
37 pub l i c i n t x , y ;
38 pub l i c Po in t ( i n t x , i n t y ) {
39 th i s . x = x ;
40 th i s . y = y ;
41 }
42
43las siguientes instrucciones

215Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 95


216Especificación del lenguaje C#

1 Point p = new Point(10, 10);

2 object box = p;

3 p.x = 20;
5muestran en la consola el valor 10, porque la operación boxing implícita que ocurre en la asignación de p a box
64causa la copia del valor de p. En cambio, si se hubiera declarado Point como class, el resultado sería 20, puesto
7que p y box harían referencia a la misma instancia.

84.3.2 Conversiones Unboxing


9Una conversión unboxing permite que un tipo de referencia (reference-type) se convierta explícitamente en un
10tipo de valor (value-type). Existen los siguientes tipos de conversiones unboxing:
11• Del tipo ob jec ta cualquier tipo de valor (value-type), incluidos los tipos enum (enum-type).
12• Del tipo Sys tem.Va lueTypea cualquier tipo de valor (value-type), incluidos los tipos enum (enum-type).
13• De cualquier tipo de interfaz (interface-type) a cualquier tipo de valor (value-type) que implemente el tipo
14 de interfaz (interface-type).
15• Del tipo Sys tem.Enum a cualquier tipo enum (enum-type).
16Una operación unboxing consiste en comprobar primero que la instancia del objeto es un valor al que se ha
17aplicado la conversión boxing del tipo de valor (value-type) dado, y copiar después el valor fuera de la instancia.
18En cuanto a la clase boxing imaginaria descrita en la sección anterior, una conversión unboxing de un objeto
19box a un tipo de valor T consiste en ejecutar la expresión ((T_Box)box).value. Por lo tanto, las instrucciones
20 ob jec t box = 123 ;
21 i n t i = ( i n t )box ;
22conceptualmente se corresponden con
23 ob jec t box = new i n t _Box (123 ) ;
24 i n t i = ( ( i n t _Box )box ) . va lue ;
25Para que una conversión unboxing a un tipo de valor (value-type) dado se ejecute correctamente en tiempo de
26ejecución, el valor del operando de origen debe ser una referencia a un objeto que se creó anteriormente
27mediante una conversión boxing de un valor de ese tipo de valor (value-type). Si el operando de origen tiene
28valor null, se producirá una excepción System.NullReferenceException. Si el operando de origen es una
29referencia a un objeto incompatible, se producirá una excepción System.InvalidCastException.

21796 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


218 Capítulo 18 Código no seguro

1 5.Variables

2Las variables representan ubicaciones de almacenamiento. Toda variable tiene un tipo, que determina los valores
3que pueden almacenarse en ella. C# es un lenguaje con seguridad de tipos, y el compilador de C# garantiza que
4los valores almacenados en variables siempre son del tipo apropiado. El valor de una variable puede modificarse
5mediante una asignación o con el uso de los operadores ++ y --.
6Una variable debe estar definitivamente asignada (§5.3) para que pueda obtenerse su valor.
7Como se explica en las secciones siguientes, las variables pueden ser asignadas inicialmente o no asignadas
8inicialmente. Una variable asignada inicialmente tiene un valor inicial bien definido y siempre se considera
9asignada definitivamente. Una variable no asignada inicialmente no tiene un valor inicial. Para que una variable
10no asignada inicialmente se considere asignada definitivamente en una ubicación determinada, debe ocurrir una
11asignación de la variable en todas las rutas de ejecución posibles que conduzcan a dicha ubicación.

125.1 Categorías de variables


13C# define siete categorías de variables: variables estáticas, variables de instancia, elementos de matriz,
14parámetros de valores, parámetros de referencia, parámetros de salida y variables locales. En las próximas
15secciones se describen estas categorías.
16En el siguiente ejemplo:

17 class A

18 {

19 public static int x;


21 void F(int[] v, int a, ref int b, out int c) {
20
22 int i = 1;

23 c = a + b++;
26
24x es una variable estática, y es una variable de instancia, v[0] es un elemento de matriz, a es un parámetro de
27valores, b es un parámetro de referencias, c es un parámetro de salida e i es una variable local.
25
285.1.1 Variables estáticas
29Un campo declarado con el modificador static se denomina variable estática. Una variable estática se genera
30antes de la ejecución del constructor estático (§10.11) de su tipo contenedor, y deja de existir a la vez que el
31dominio de aplicación asociado.
32El valor inicial de una variable estática es el valor predeterminado (§5.2) del tipo de la variable.
33Para los fines de comprobación de asignación definitiva, una variable estática se considera asignada
34inicialmente.

355.1.2 Variables de instancia


36Un campo declarado sin el modificador static se denomina variable de instancia.

219Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 97


220Especificación del lenguaje C#

15.1.2.1 Variables de instancia en clases


2Una variable de instancia de una clase se genera cuando se crea una nueva instancia de esa clase, y deja de
3existir cuando no hay referencias a esa instancia y se ha ejecutado el destructor de la instancia (si existe).
4El valor inicial de una variable de instancia de una clase es el valor predeterminado (§5.2) del tipo de la variable.
5Para los fines de comprobación de asignación definitiva, una variable de instancia de una clase se considera
6asignada inicialmente.

75.1.2.2 Variables de instancia en estructuras


8Una variable de instancia de una estructura tiene exactamente el mismo período de duración que la variable de
9estructura a la que pertenece. Es decir, cuando se genera o deja de existir una variable de tipo de estructura, le
10ocurre lo mismo a las variables de instancia de la estructura.
11El estado de asignación inicial de una variable de instancia de una estructura es el mismo que el de la variable
12que contiene la estructura. Es decir, si una variable de estructura se considera asignada inicialmente, lo mismo le
13ocurre a sus variables de instancia, y si una variable de estructura se considera no asignada inicialmente, sus
14variables de instancia tampoco lo están.

155.1.3 Elementos matriciales


16Los elementos de una matriz comienzan a existir cuando se crea una instancia de la matriz y cesa su existencia
17cuando dejan de existir referencias a dicha instancia.
18El valor inicial de los elementos de una matriz es el valor predeterminado (§5.2) del tipo de los elementos de la
19matriz.
20Para los fines de comprobación de asignación definitiva, un elemento de matriz se considera asignado
21inicialmente.

225.1.4 Parámetros de valor


23Un parámetro de valor es un parámetro que se declara sin re fu out.
24Un parámetro de valor se genera al invocar el miembro de función (método, constructores de instancia,
25descriptor de acceso u operador) al que pertenece el parámetro, y se inicializa con el valor del argumento
26especificado en la invocación. Un parámetro de valores deja de existir al regresar el miembro de función.
27Para los fines de comprobación de asignación definitiva, un parámetro de valores se considera asignado
28inicialmente.

295.1.5 Parámetros de referencia


30Un parámetro de referencia es un parámetro que se declara con un modificador re f.
31Un parámetro de referencia no crea una nueva ubicación de almacenamiento. En lugar de ello, un parámetro de
32referencia representa la misma ubicación de almacenamiento que la variable especificada como argumento en la
33invocación del miembro de función. Por lo tanto, el valor de un parámetro de referencia siempre es el mismo
34que el de la variable subyacente.
35Se aplican las siguientes reglas de asignación definitiva a los parámetros de referencia. Deben tenerse en cuenta
36las distintas reglas de los parámetros de resultados explicadas en §5.1.6.
37• Una variable debe estar asignada definitivamente (§5.3) para que pueda pasarse como parámetro de
38 referencia en la invocación de un miembro de función.
39• Dentro de un miembro de función, un parámetro de referencia se considera asignado inicialmente.

22198 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


222 Capítulo 18 Código no seguro

1Dentro de un método de instancia o un descriptor de acceso de instancia de tipo struct, la palabra clave th i s
2tiene exactamente el mismo comportamiento que un parámetro de referencia de tipo struct (§7.5.7).

35.1.6 Parámetros de salida


4Un parámetro de salida es un parámetro que se declara con un modificador out.
5Un parámetro de salida no crea una nueva ubicación de almacenamiento. En lugar de ello, un parámetro de
6salida representa la misma ubicación de almacenamiento que la variable especificada como argumento en la
7invocación del miembro de función. Por lo tanto, el valor de un parámetro de salida siempre es el mismo que el
8de la variable subyacente.
9Se aplican las siguientes reglas de asignación definitiva a los parámetros de salida. Deben tenerse en cuenta las
10distintas reglas de los parámetros de referencia explicadas en §5.1.5.
11• Una variable no tiene por qué estar asignada definitivamente para poder pasarla como parámetro de salida
12 en una invocación de miembro de función.
13• Tras la finalización normal de la invocación de un miembro de función, cada variable que se pase como
14 parámetro de salida se considera asignada en esa ruta de ejecución.
15• Dentro de un miembro de función, un parámetro de salida se considera no asignado inicialmente.
16• Todos los parámetros de resultado de un miembro de función deben estar asignados definitivamente (§5.3)
17 antes de que se devuelva normalmente el miembro de función.
18Dentro de un constructores de instancia del tipo struct, la palabra clave th i stiene exactamente el mismo
19comportamiento que un parámetro de salida de tipo struct (§7.5.7).

205.1.7 Variables locales


21Una variable local se declara mediante una declaración de variable local (local-variable-declaration), lo cual
22puede ocurrir en un bloque (block), una instrucción for (for-statement), una instrucción switch (switch-
23statement)o una instrucción using (using-statement).
24El período de duración de una variable local representa la parte de la ejecución del programa durante la cual se
25garantiza que hay un espacio de almacenamiento reservado para ella. Este período se prolonga desde la entrada
26en el bloque (block), instrucción for (for-statement), instrucción switch (switch-statement) o instrucción using
27(using-statement) a los que está asociada, hasta que la ejecución de dichos bloque, instrucción for, instrucción
28switch o instrucción using finaliza de cualquier modo. (La entrada en un bloque contenedor o la llamada a un
29método suspende, aunque no finaliza, la ejecución del bloque, la instrucción for, la instrucción switch o la
30instrucción using actuales.) Si se entra de manera recursiva en el bloque primario, en la instrucción for, en la
31instrucción switch o en la instrucción using, se creará una instancia nueva de la variable local cada vez, y su
32inicializador de variable local (local-variable-initializer), si existe, se evaluará en cada ocasión.
33Una variable local no se inicializa automáticamente y, por lo tanto, no tiene un valor predeterminado. Para los
34fines de comprobación de asignación definitiva, una variable local se considera no asignada inicialmente. Una
35declaración de variable local (local-variable-declaration) puede incluir un inicializador de variable local (local-
36variable-initializer), en cuyo caso la variable se considera asignada definitivamente en todo su ámbito, a
37excepción de la expresión suministrada en el inicializador de la variable local.
38Dentro del ámbito de una variable local, hacer referencia a la variable local en una posición textual anterior a su
39declarador de variable local (local-variable-declarator) produce un error en tiempo de compilación.
40La duración real de una variable local depende de la implementación. Por ejemplo, un compilador puede
41determinar estáticamente que una variable local de un bloque sólo se utiliza para una pequeña parte del bloque,

223Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 99


224Especificación del lenguaje C#

1y usar este análisis para que el compilador genere un código que dé como resultado que el almacenamiento de la
2variable tenga una duración más corta que la de su bloque contenedor.
3El almacenamiento a que se refiere una variable de referencia local se reclama independientemente de la
4duración de esa variable de referencia local (§3.9).
5Una variable local también se declara mediante una instrucción foreach (foreach-statement) y mediante una
6cláusula catch específica (specific-catch-clause) en una instrucción try (try-statement). Para una instrucción
7foreach (foreach-statement), la variable local es una variable de iteración (§8.8.4). Para una cláusula catch
8específica (a specific-catch-clause), la variable local es una variable de excepción (§8.10). Una variable local
9declarada por una instrucción foreach (foreach-statement) o una cláusula catch específica (specific-catch-clause)
10se considera asignada de forma definitiva en todo su ámbito.

115.2 Valores predeterminados


12Las categorías de variables siguientes se inicializan de forma automática a sus valores predeterminados:
13• Variables estáticas.
14• Variables de instancia de instancias de clase.
15• Elementos matriciales.
16El valor predeterminado de una variable depende de su tipo y se determina como sigue:
17• Para una variable de un tipo de valor (value-type), el valor predeterminado es el mismo que el valor
18 calculado por el constructor predeterminado del tipo de valor (value-type) (§4.1.1).
19• Para una variable de tipo de referencia (reference-type), el valor predeterminado es nu l .l
20La inicialización a valores predeterminados, la suelen llevar a cabo el administrador de la memoria o el
21recolector de elementos no utilizados, que inicializan la memoria a todos los bits cero antes de asignarla para su
22uso. Por este motivo, es conveniente utilizar todos los bits cero para representar la referencia nula.

235.3 Estado de asignación definitiva


24En una ubicación dada del código ejecutable de un miembro de función, se dice que una variable está asignada
25definitivamente si el compilador puede probar, mediante un análisis de flujo estático concreto (§5.3.3), que la
26variable se ha inicializado automáticamente o ha sido el destino de por lo menos una asignación. Dicho de una
27manera informal, las reglas de la asignación definitiva son:
28• Una variable asignada inicialmente (§5.3.1) siempre se considera asignada definitivamente.
29• Una variable no asignada inicialmente (§5.3.2) se considera asignada definitivamente en una ubicación dada
30 si todas las rutas de ejecución posibles que llevan a ella contienen por lo menos uno de los siguientes
31 elementos:
32 o Una asignación simple (§7.13.1) en la cual la variable es el operando izquierdo.
33 o Una expresión de llamada (§7.5.5) o una expresión de creación de objeto (§7.5.10.1) que pasa la
34 variable como parámetro de salida.
35 o Para una variable local, una declaración de variable local (§8.5) que incluye un inicializador de variable.
36La especificación formal subyacente a las reglas informales anteriores se describe en los apartados §5.3.1,
37§5.3.2 y §5.3.3.

225100 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


226 Capítulo 18 Código no seguro

1El estado de asignación definitiva de las variables de instancia de una variable de tipo struct (struct-type) puede
2tener un seguimiento individual y también colectivo. Además de las reglas anteriores, se aplican las reglas
3siguientes a las variables de tipo struct (struct-type) y a sus variables de instancia:
4• Una variable de instancia se considera asignada definitivamente si la variable de tipo struct (struct-type) que
5 la contiene se considera asignada definitivamente.
6• Una variable de tipo struct (struct-type) se considera asignada definitivamente si todas sus variables de
7 instancia se consideran asignadas definitivamente.
8La asignación definitiva es un requisito obligatorio en los contextos siguientes:
9• Una variable debe estar asignada definitivamente en cada ubicación donde se obtenga su valor. Esto asegura
10 que nunca puedan ocurrir valores no definidos. Se considera que la ocurrencia de una variable en una
11 expresión obtiene el valor de la variable, excepto cuando:
12 o la variable es el operando izquierdo de una asignación simple,
13 o la variable se pasa como un parámetro de salida, o
14 o la variable es de tipo struct (struct-type) y se produce como el operando izquierdo de un acceso a
15 miembros.
16• Una variable debe estar asignada definitivamente en cada ubicación donde se pase como un parámetro de
17 referencia. Esto asegura que el miembro de función que se invoca puede considerar al parámetro de
18 referencias como asignado inicialmente.
19• Todos los parámetros de resultado de un miembro de función deben estar asignados definitivamente en cada
20 ubicación donde regrese el miembro de función (mediante una instrucción re tu rno cuando la ejecución
21 llega al final del cuerpo del miembro de función). Esto asegura que los miembros de función no devuelven
22 valores no definidos en los parámetros de salida, lo que permite al compilador considerar la invocación de
23 un miembro de función que llame a una variable como un parámetro de salida equivalente a una asignación
24 de la variable.
25• La variable th i sde un constructores de instancia de un tipo struct (struct-type) debe estar asignada
26 definitivamente en cada ubicación a la que regrese el constructor de instancia.

275.3.1 Variables asignadas inicialmente


28Las siguientes categorías de variables se clasifican como asignadas inicialmente:
29• Variables estáticas.
30• Variables de instancia de instancias de clase.
31• Variables de instancia de variables de estructura asignadas inicialmente.
32• Elementos matriciales.
33• Parámetros de valor.
34• Parámetros de referencia.
35• Variables declaradas en una cláusula ca tch o en una instrucción f o reach.

365.3.2 Variables no asignadas inicialmente


37Las siguientes categorías de variables se clasifican como no asignadas inicialmente:

227Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 101


228Especificación del lenguaje C#

1• Variables de instancia de variables de estructura no asignadas inicialmente.


2• Parámetros de salida, incluida la variable th i sde constructores de instancia de estructuras.
3• Variables locales, excepto aquellas declaradas en una cláusula ca tch o en una instrucción f o reach.

45.3.3 Reglas precisas para determinar asignaciones definitivas


5Para determinar que cada variable utilizada está asignada definitivamente, el compilador debe utilizar un
6proceso equivalente al descrito en esta sección.
7El compilador procesa el cuerpo de cada miembro de función que tenga una o más variables no asignadas
8inicialmente. El compilador determina un estado de asignación definitiva para cada variable v no asignada
9inicialmente, en cada uno de los siguientes puntos del miembro de función:
10• Al principio de cada instrucción
11• En el punto final (§8.1) de cada instrucción
12• En cada arco que transfiere el control a otra instrucción o al punto final de una instrucción
13• Al principio de cada expresión
14• Al final de cada expresión
15El estado de asignación definitiva de v puede ser:
16• Asignada definitivamente. Indica que en todos los posibles flujos de control para este punto, a v se le ha
17 asignado un valor.
18• No asignada definitivamente. Para el estado de una variable al final de una expresión de tipo boo l, el estado
19 de una variable que no se ha asignado definitivamente puede caer (aunque no necesariamente) en uno de los
20 siguientes subestados:
21 o Asignada definitivamente después de una expresión true. Este estado indica que si la expresión booleana
22 se evalúa como true, v estará asignada definitivamente, pero si la expresión booleana se evalúa como
23 false, no tiene por qué estar asignada.
24 o Asignada definitivamente después de una expresión false. Este estado indica que si la expresión
25 booleana se evalúa como false, v estará asignada definitivamente, pero si la expresión booleana se
26 evalúa como true, no tiene por qué estar asignada.
27Las reglas siguientes rigen la forma en la que se determina el estado de una variable v en cada posición.

285.3.3.1 Reglas generales para instrucciones


29• v no se asigna definitivamente al principio del cuerpo de un miembro de función.
30• v se asigna definitivamente al principio de cualquier instrucción inalcanzable.
31• El estado de asignación definitiva de v al principio de cualquier otra instrucción se determina mediante la
32 comprobación del estado de asignación definitiva de v en todas las transferencias de flujo de control que
33 apunten al principio de esa instrucción. Si (y sólo en este caso) v se asigna definitivamente en todas estas
34 transferencias de flujo de control, se asigna también definitivamente al principio de la instrucción. El
35 conjunto de posibles transferencias de flujo de control se determina del mismo modo que en la
36 comprobación del alcance de la instrucción (§8.1).
37• El estado de asignación definitiva de v en el punto final de una instrucción de bloque, checked,
38 unchecked, if, while, do, for, foreach, lock, using o switch se determina mediante la comprobación del

229102 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


230 Capítulo 18 Código no seguro

1 estado de asignación definitiva de v en todas las transferencias de flujo de control que apunten al punto final
2 de esa instrucción. Si v se asigna definitivamente en todas estas transferencias de flujo de control, entonces
3 v se asigna definitivamente en el punto final de la instrucción. De lo contrario, v no se asigna
4 definitivamente en el punto final de la instrucción. El conjunto de posibles transferencias de flujo de control
5 se determina del mismo modo que en la comprobación del alcance de la instrucción (§8.1).

65.3.3.2 Instrucciones de bloques e instrucciones checked y unchecked


7El estado de asignación definitiva de v, en la transferencia de control a la primera instrucción de la lista de
8instrucciones del bloque (o al punto final del bloque, si la lista de instrucciones está vacía), es el mismo que el
9de la instrucción de asignación definitiva de v antes de la instrucción de bloque checked o unchecked.

105.3.3.3 Instrucciones de expresiones


11Para una instrucción de expresión stmt que consta de la expresión expr:
12• v tiene el mismo estado de asignación definitiva al principio de expr que al principio de stmt.
13• Si v se asigna definitivamente al final de expr, también lo hará en el punto final de stmt; de lo contrario, este
14 punto no recibirá tal asignación.

155.3.3.4 Instrucciones de declaración


16• Si stmt es una instrucción de declaración sin inicializadores, entonces v tiene el mismo estado de asignación
17 definitiva en el punto final de stmt que al principio de stmt.
18• Si stmt es una instrucción de declaración con inicializadores, el estado de asignación definitiva de v se
19 determina como si stmt fuera una lista de instrucciones, con una instrucción de asignación para cada
20 declaración con un inicializador (en el orden de la declaración).

215.3.3.5 Instrucciones If
22Para una instrucción i fstmt con la estructura:
23 i f ( expr ) then-stmt else else-stmt
24• v tiene el mismo estado de asignación definitiva al principio de expr que al principio de stmt.
25• Si v está definitivamente asignada al final de expr, también lo estará en la transferencia de flujo de control a
26 then-stmt y a else-stmt, o bien al punto final de stmt si no hay cláusula else.
27• Si v está “definitivamente asignada después de una expresión true” al final de expr, entonces también lo
28 estará en la transferencia de flujo de control a then-stmt; si no hay cláusula else, no estará definitivamente
29 asignada en la transferencia de flujo de control a else-stmt o al punto final de stmt.
30• Si v tiene el estado “definitivamente asignada después de expresión false” al final de expr, entonces está
31 definitivamente asignada en la transferencia de flujo de control a else-stmt y no lo está a then-stmt en la
32 transferencia de flujo de control. Está definitivamente asignada al punto final de stmt solamente si está
33 también definitivamente asignada al punto final de then-stmt.
34• De lo contrario, v se considera no asignada definitivamente a then-stmt o a else-stmt en la transferencia de
35 flujo de control o, si no hay cláusula else, al punto final de stmt.

365.3.3.6 Instrucciones Switch


37En una instrucción switch stmt con una expresión de control expr:

231Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 103


232Especificación del lenguaje C#

1• El estado de asignación definitiva de v al principio de expr es el mismo que el estado de v al principio de


2 stmt.
3• El estado de asignación definitiva de v en la transferencia de flujo de control a una lista de instrucciones de
4 bloque switch localizable es el mismo que al final de expr.

233104 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


234 Capítulo 18 Código no seguro

15.3.3.7 Instrucciones While


2Para una instrucción whi l estmt con la siguiente estructura:
3 whi l e ( expr ) while-body
4• v tiene el mismo estado de asignación definitiva al principio de expr que al principio de stmt.
5• Si v está asignada definitivamente al final de expr, entonces está definitivamente asignada en la
6 transferencia de flujo de control a while-body y al punto final de stmt.
7• Si el estado de v al final de expr es “definitivamente asignada después de una expresión true”, está
8 definitivamente asignada en la transferencia de flujo de control a while-body, pero no en el punto final de
9 stmt.
10• Si el estado de v al final de expr es “definitivamente asignada después de una expresión false”, está
11 definitivamente asignada en la transferencia de flujo de control al punto final de stmt, pero no a while-body
12 en la transferencia de flujo de control.

135.3.3.8 Instrucciones Do
14Para una instrucción do stmt con la siguiente estructura:
15 do do-body while ( expr ) ;
16• v tiene el mismo estado de asignación definitiva en la transferencia de flujo de control, desde el principio de
17 stmt a do-body, que al principio de stmt.
18• v tiene el mismo estado de asignación definitiva al principio de expr que en el punto final de do-body.
19• Si v está asignada definitivamente al final de expr, también lo está en la transferencia de flujo de control al
20 punto final de stmt.
21• Si el estado de v al final de expr es “asignada definitivamente después de una expresión false”, entonces está
22 definitivamente asignada en la transferencia de flujo de control al punto final de stmt.

235.3.3.9 Instrucciones For


24La comprobación de asignación definitiva de una instrucción f o rcon la estructura
25 f o r ( for-initializer ; for-condition ; for-iterator ) embedded-statement
26se realiza como si la instrucción estuviera escrita de la siguiente forma:
27 {
28 for-initializer ;
29 while ( for-condition ) {
30 embedded-statement ;
31 for-iterator ;
32 }
33 }
34Si se omite la condición for (for-condition) en la instrucción for, la evaluación o la asignación definitiva
35continuará como si se sustituyera la condición for por true en la expansión anterior.

365.3.3.10 Instrucciones Break, Continue y Goto


37El estado de asignación definitiva de v en la transferencia de flujo de control causada por una instrucción break,
38continue o goto es el mismo que el de v al principio de la instrucción.

235Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 105


236Especificación del lenguaje C#

15.3.3.11 Instrucciones Throw


2Para una instrucción stmt con la estructura:

3 throw expr ;
4El estado de asignación definitiva de v al principio de expr es el mismo que el de v al principio de stmt.

55.3.3.12 Instrucciones Return


6Para una instrucción stmt con la estructura:

7 return expr ;
8• El estado de asignación definitiva de v al principio de expr es el mismo que el de v al principio de stmt.
9• Si v es un parámetro de salida, debe asignarse definitivamente:
10 o después de expr
11 o o bien al final del bloque f i na l lde
y t r y - f i na lol try-catch-finally
y que incluya la instrucción return.
12Para una instrucción stmt con la estructura:

13 return ;
14• Si v es un parámetro de salida, debe asignarse definitivamente:
15 o antes de stmt
16 o o bien al final del bloque finally de try-finally o try-catch-finally que incluya la instrucción return.

175.3.3.13 Instrucciones Try-catch


18Para una instrucción stmt con la estructura:

19 try try-block
20 catch(...) catch-block-1
21 ...
22 catch(...) catch-block-n
23• El estado de asignación definitiva de v al principio de try-block es igual al de v al principio de stmt.
24• El estado de asignación definitiva de v al principio de catch-block-i (para cualquier i) es el mismo que el de
25 v al principio de stmt.
26• El estado de asignación definitiva de v en el punto final de stmt está asignado definitivamente si (y sólo si) v
27 está definitivamente asignada en el punto final de try-block y de todos los catch-block-i (para cada i de 1
28 a n).

295.3.3.14 Instrucciones Try-finally


30Para una instrucción try stmt con la siguiente estructura:

31 try try-block finally finally-block


32• El estado de asignación definitiva de v al principio de try-block es igual al de v al principio de stmt.
33• El estado de asignación definitiva de v al principio de finally-block es igual al de v al principio de stmt.
34• v estará definitivamente asignada en el punto final de stmt si (y sólo si) al menos una de las siguientes
35 afirmaciones es verdadera:

237106 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


238 Capítulo 18 Código no seguro

1 o v está asignada definitivamente en el punto final de try-block


2 o v está asignada definitivamente en el punto final de finally-block
3Si se realiza una transferencia de flujo de control (por ejemplo, una instrucción goto) que se inicia dentro de un
4bloque try (try-block) y termina fuera del bloque try, entonces v también se considerará asignada definitivamente
5en esa transferencia de flujo de control si también lo está en el punto final del bloque finally (finally-block).
6(Esta condición no es exclusiva: si v está asignada definitivamente por otra razón en esta transferencia de flujo
7de control, se considerará asignada definitivamente).

85.3.3.15 Instrucciones Try-finally-catch


9El análisis de asignación definitiva de una instrucción try-catch-finally con la estructura:
10 t r y try-block
11 catch(...) catch-block-1
12 ...
13 catch(...) catch-block-n
14 finally finally-block
15se realiza como si la instrucción fuera una instrucción t r y - f i na lque
l y incluyera una instrucción t r y - ca tch
:
16 t ry {
17 t r y try-block
18 catch(...) catch-block-1
19 ...
20 catch(...) catch-block-n
21 }
22 finally finally-block
23El ejemplo siguiente demuestra cómo afectan los diferentes bloques de una instrucción t ry(§8.10) a la
24asignación definitiva.
25 c lass A
26 {
27 s ta t i c vo id F ( ) {
28 int i , j ;
29 t ry {
30 goto LABEL ;
31 / / ne i the r i nor j de f i n i te l y ass igned
32 i = 1;
35
33 ca tch {
36
34 / / ne i the r i nor j de f i n i te l y ass igned
37 i = 3;
38 / / i de f i n i te l y ass igned
39

239Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 107


240Especificación del lenguaje C#

1 finally {

2 // neither i nor j definitely assigned

3 j = 5;

4 // j definitely assigned

5 }

6 // i and j definitely assigned

7 LABEL:;
1285.3.3.16 Instrucciones Foreach
13Para una instrucción f o reachstmt con la siguiente estructura:
9
14 f o reach ( type identifier in expr ) embedded-statement
10
15• El estado de asignación definitiva de v al principio de expr es el mismo que el estado de v al principio de
11
16 stmt.
17• El estado de asignación definitiva de v en la transferencia de flujo de control a una instrucción incrustada
18 (embedded-statement) o al punto final de stmt es el mismo que el estado de v al final de expr.

195.3.3.17 Instrucciones Using


20Para una instrucción using stmt con la siguiente estructura:
21 us ing ( resource-acquisition ) embedded-statement
22• El estado de asignación definitiva de v al principio de adquisición de recurso (resource-acquisition) es igual
23 al estado de v al principio de stmt.
24• El estado de asignación definitiva de v en la transferencia de flujo de control a una instrucción incrustada
25 (embedded-statement) es igual al de v al final de la adquisición de recurso (resource-acquisition).

265.3.3.18 Instrucciones Lock


27Para una instrucción lock stmt con la siguiente estructura:
28 l ock ( expr ) embedded-statement
29• El estado de asignación definitiva de v al principio de expr es el mismo que el estado de v al principio de
30 stmt.
31• El estado de asignación definitiva de v en la transferencia de flujo de control a una instrucción incrustada
32 (embedded-statement) es igual que el estado de v al final de la expresión (expr).

335.3.3.19 Reglas generales para expresiones simples


34La siguiente regla se aplica a estos tipos de expresiones: literales (§7.5.1), nombres simples (§7.5.2),
35expresiones de acceso a miembros (§7.5.4), expresiones de acceso a bases no indizadas (§7.5.8) y expresiones
36typeof (§7.5.11).
37• El estado de asignación definitiva de v al final de una expresión de este tipo es igual que el de v al principio
38 de la expresión.

241108 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


242 Capítulo 18 Código no seguro

15.3.3.20 Reglas generales para expresiones con expresiones incrustadas


2Las reglas siguientes se aplican a estos tipos de expresiones: expresiones entre paréntesis (§7.5.3), expresiones
3de acceso a elementos (§7.5.6), expresiones de acceso a bases con indización (§7.5.8), expresiones de
4incremento y decremento (§7.5.9, §7.6.5), expresiones de conversión de tipos (§7.6.6), expresiones unarias +, -,
5~, *, expresiones binarias +, -, *, /, %, <<, >>, <, <=, >, >=, ==, !=, is, as, &, |, ^ (§7.7, §7.8, §7.9,
6§7.10), expresiones de asignación compuestas (§7.13.2), expresiones checked y unchecked (§7.5.12), y
7expresiones de creación de matrices y de delegados (§7.5.10).
8Cada una de estas expresiones tiene una o más subexpresiones que se evalúan incondicionalmente en un orden
9fijo. Por ejemplo, el operador binario % evalúa el lado izquierdo del operador y, a continuación, el lado derecho.
10Una operación de indización evalúa la expresión indizada y, a continuación, cada una de las expresiones de
11índice por orden, de izquierda a derecha. Para una expresión expr que tiene las subexpresiones expr1, expr2, ...,
12exprn, evaluadas en ese orden:
13• El estado de asignación definitiva de v al principio de expr1 es igual que el estado de asignación definitiva al
14 principio de expr.
15• El estado de asignación definitiva de v al principio de expri (siendo i mayor que uno) es igual que el estado
16 al final de expri-1.
17• El estado de asignación definitiva de v al principio de expr es igual que el estado al final de exprn.

185.3.3.21 Expresiones de invocación y expresiones de creación de objetos


19Para una expresión de invocación expr con la estructura:
20 primary-expression ( arg1 , arg2 , … , argn )
21o una expresión de creación de objeto con la estructura:

22 new type ( arg1 , arg2 , … , argn )


23• Para una expresión de invocación, el estado de asignación definitiva de v antes de la expresión primaria
24 (primary-expression) es igual que el estado de v antes de expr.
25• Para una expresión de invocación, el estado de asignación definitiva de v antes de arg1 es igual que el estado
26 de v después de la expresión primaria (primary-expression).
27• Para una expresión de creación de objeto, el estado de asignación definitiva de v antes de arg1 es igual que el
28 estado de v antes de expr.
29• Para cada argumento argi, el estado de asignación definitiva de v después de argi está determinado por las
30 reglas de expresión normales omitiendo los modificadores ref u out.
31• Para cada argumento argi, siendo i mayor que uno, el estado de asignación definitiva de v antes de argi es el
32 mismo que el de v después de argi-1.
33• Si se pasa la variable v como un argumento out (es decir, un argumento de forma "out v") en cualquiera de
34 los argumentos, el estado de v después de expr se asigna definitivamente. De lo contrario, el estado de v
35 después de expr es el mismo que el estado de v después de argn.

365.3.3.22 Expresiones de asignación simples


37Para una expresión expr con la estructura w = expr-rhs:
38• El estado de asignación definitiva de v antes de expr-rhs es igual que el de v antes de expr.

243Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 109


244Especificación del lenguaje C#

1• Si w es la misma variable que v, el estado de asignación definitiva de v después de expr se asigna


2 definitivamente. De lo contrario, el estado de asignación definitiva de v después de expr será igual que el de
3 v después de expr-rhs.

45.3.3.23 Expresiones &&


5Para una expresión expr con la estructura expr-first && expr-second:
6• El estado de asignación definitiva de v antes de expr-first es igual que el de v antes de expr.
7• El estado de asignación definitiva de v antes de la segunda expresión (expr-second) se asigna
8 definitivamente, si el estado de v después de la primera expresión (expr-first) se asigna definitivamente o es
9 “asignado definitivamente después de una expresión true”. En cualquier otro caso, no se asigna
10 definitivamente.
11• El estado de asignación definitiva de v después de expr se determina de la siguiente forma:
12 o Si el estado de v después de expr-first se asigna definitivamente, también lo hará el estado de v después
13 de expr.
14 o De lo contrario, si el estado de v después de la segunda expresión (expr-second) se asigna
15 definitivamente y el estado de v después de la primera expresión (expr-first) es “asignado
16 definitivamente después de una expresión false”, entonces el estado de v después de la expresión (expr)
17 se asigna definitivamente.
18 o De lo contrario, si el estado de v después de la segunda expresión (expr-second) se asigna
19 definitivamente o es “asignado definitivamente después de expresión true”, entonces el estado de v
20 después de la expresión (expr) es “asignado definitivamente después de una expresión true”.
21 o De lo contrario, si el estado de v después de la primera expresión (expr-first) es “asignado
22 definitivamente después de una expresión false” y el estado de v después de segunda expresión (expr-
23 second) es “asignado definitivamente después de una expresión false”, entonces el estado de v después
24 de la expresión (expr) es “asignado definitivamente después de una expresión false”.
25 o De lo contrario, el estado de v después de expr no se asignará definitivamente.
26En el siguiente ejemplo:
27 c lass A
28 {
29 s ta t i c vo id F ( i n t x , i n t y ) {
30 int i ;
31 i f ( x >= 0 && ( i = y ) >= 0) {
32 / / i de f i n i te l y ass igned
33 }
34 e l se {
35 / / i not de f i n i te l y ass igned
36 }
37
40la variable i se considera asignada definitivamente en una de las instrucciones incrustadas de una instrucción i f
41pero no en la otra. En la instrucción if del método F, la variable i está asignada definitivamente en la primera
38
42
39instrucción incrustada porque la ejecución de la expresión (i = y) siempre precede a la ejecución de esta
43instrucción incrustada. Por contra, la variable i no está asignada definitivamente en la segunda instrucción
44incrustada, puesto que x >= 0 podría dar como resultado false y causar que la variable i no se asigne.

245110 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


246 Capítulo 18 Código no seguro

15.3.3.24 Expresiones ||
2Para una expresión expr con la estructura expr-first | |expr-second:
3• El estado de asignación definitiva de v antes de expr-first es igual que el de v antes de expr.
4• El estado de asignación definitiva de v antes de la segunda expresión (expr-second) se asigna
5 definitivamente si el estado de v después de la primera expresión (expr-first) se asigna definitivamente o es
6 “asignado definitivamente después de una expresión false”. En cualquier otro caso, no se asigna
7 definitivamente.
8• El estado de asignación definitiva de v después de expr se determina de la siguiente forma:
9 o Si el estado de v después de expr-first se asigna definitivamente, también lo hará el estado de v después
10 de expr.
11 o De lo contrario, si el estado de v después de la segunda expresión (expr-second) se asigna
12 definitivamente y el estado de v después de la primera expresión (expr-first) es “asignado
13 definitivamente después de una expresión true”, entonces el estado de v después de expr se asignará
14 definitivamente.
15 o De lo contrario, si el estado de v después de la segunda expresión (expr-second) se asigna
16 definitivamente o es “asignado definitivamente después de una expresión false”, entonces el estado de v
17 después de la expresión (expr) es “asignado definitivamente después de una expresión false”.
18 o De lo contrario, si el estado de v después de la primera expresión (expr-first) es “asignado
19 definitivamente después de una expresión true” y el estado de v después de la segunda expresión (expr-
20 second) es “asignado definitivamente después de una expresión true”, entonces el estado de v después
21 de expr es “asignado definitivamente después de una expresión true”.
22 o De lo contrario, el estado de v después de expr no se asignará definitivamente.
23En el siguiente ejemplo:
24 c lass A
25 {
26 s ta t i c vo id G( in t x , i n t y ) {
27 int i ;
28 i f ( x >= 0 | | ( i = y ) >= 0) {
29 / / i not de f i n i te l y ass igned
30 }
31 e l se {
32 / / i de f i n i te l y ass igned
33 }
34
37la variable i se considera asignada definitivamente en una de las instrucciones incrustadas de una instrucción i f
38
35pero no en la otra. En la instrucción if del método G, la variable i está asignada definitivamente en la segunda
39
36instrucción incrustada porque la ejecución de la expresión (i = y) siempre precede a la ejecución de esta
40instrucción incrustada. Por contra, la variable i no está asignada definitivamente en la primera instrucción
41incrustada, puesto que x >= 0 podría dar como resultado true y causar que la variable i no se asigne.

425.3.3.25 ! Expresiones !
43Para una expresión expr con la estructura ! expr-operand:

247Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 111


248Especificación del lenguaje C#

1• El estado de asignación definitiva de v antes de expr-operand es igual que el estado de asignación definitiva
2 de v antes de expr.
3• El estado de asignación definitiva de v después de expr se determina de la siguiente forma:
4 o Si el estado de v después de expr-operand se asigna definitivamente, el estado de v después de expr se
5 asigna definitivamente.
6 o Si el estado de v después de expr-operand no se asigna definitivamente, el estado de v después de expr
7 no se asigna definitivamente.
8 o Si el estado de v después del operando de la expresión (expr-operand) es “asignado definitivamente
9 después de una expresión false”, entonces el estado de v después de la expresión (expr) es “asignado
10 definitivamente después de una expresión true”.
11 o Si el estado de v después del operando de la expresión (expr-operand) es “asignado definitivamente
12 después de una expresión true”, entonces el estado de v después de la expresión (expr) es “asignado
13 definitivamente después de una expresión false”.

145.3.3.26 Expresiones ?:
15Para una expresión expr con la estructura expr-cond ? expr-true : expr-false:
16• El estado de asignación definitiva de v antes de expr-cond es igual al estado de v antes de expr.
17• El estado de asignación definitiva de v antes de la expresión para true (expr-true) se asigna definitivamente
18 solamente en el caso de que el estado de v después de la expresión condicional (expr-cond) se asigne
19 definitivamente o sea “asignado definitivamente después de una expresión true”.
20• El estado de asignación definitiva de v antes de la expresión para false (expr-false) se asigna definitivamente
21 solamente en el caso de que el estado de v después de la expresión condicional (expr-cond) se asigne
22 definitivamente o sea “asignado definitivamente después de una expresión false”.
23• El estado de asignación definitiva de v después de expr se determina de la siguiente forma:
24 o Si la expresión condicional (expr-cond) es una expresión constante (§7.15) con valor t rue, entonces el
25 estado de v después de la expresión (expr) es el mismo que el estado de v después de la expresión para
26 true (expr-true).
27 o De lo contrario, si la expresión condicional (expr-cond) es una expresión constante (§7.15) con valor
28 , entonces el estado de v después de la expresión (expr) es el mismo que el estado de v después de
f a l se
29 la expresión para false (expr-false).
30 o De lo contrario, si el estado de v después de expr-true y el estado de v después de expr-false se asignan
31 definitivamente, entonces el estado de v después de expr también se asignará definitivamente.
32 o De lo contrario, el estado de v después de expr no se asignará definitivamente.

335.4 Referencias de variables


34Una referencia de variable (variable-reference) es una expresión que se clasifica como una variable. Una
35referencia de variable (variable-reference) es una ubicación de almacenamiento a la que se puede obtener
36acceso, tanto para obtener el valor actual, como para almacenar un valor nuevo.
37 variable-reference:
38 expression
39En C y C++, una referencia de variable (variable-reference) se conoce como lvalue.

249112 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


250 Capítulo 18 Código no seguro

15.5 Atomicidad de las referencias de variable


2Las lecturas y escrituras de los siguientes tipos de datos son atómicas: boo l, char, byte, sbyte, short, ushort,
3uint, int, float y tipos de referencia. Además, las lecturas y escrituras de tipos enum cuyo tipo subyacente está
4en la lista anterior son también atómicas. No se garantiza que las lecturas y escrituras de otros tipos (como long,
5ulong, double o decimal, así como los tipos definidos por el usuario) sean atómicas. Aparte de las funciones
6de biblioteca diseñadas para este propósito, no hay garantía de lectura, modificación ni escritura atómicas, como
7en el caso de incrementos o decrementos.

251Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 113


252Especificación del lenguaje C#

1 6.Conversiones

2Una conversión permite que una expresión de un tipo sea tratada como de otro tipo. Las conversiones pueden
3ser implícitas o explícitas, y esto determina si se requiere una conversión de tipo explícita. Por ejemplo, la
4conversión del tipo i n tal tipo l ong es implícita, por lo que las expresiones de tipo int pueden tratarse
5implícitamente como del tipo long. La conversión opuesta, del tipo long a int, es explícita y, por ello, se
6requiere una conversión explícita.

7 int a = 123;

8 long b = a; // implicit conversion from int to long


109Algunas conversiones están definidas por el lenguaje. Los programas también pueden definir sus propias
11conversiones (§6.4).

126.1 Conversiones implícitas


13Las siguientes conversiones se clasifican como conversiones implícitas:
14• Conversiones de identidad
15• Conversiones numéricas implícitas
16• Conversiones de enumeración implícitas
17• Conversiones de referencia implícitas
18• Conversiones boxing
19• Conversiones implícitas de expresión constante
20• Conversiones definidas por el usuario implícitas
21Las conversiones implícitas pueden ocurrir en distintas situaciones, incluyendo las invocaciones de miembros de
22función (§7.4.3), las expresiones de conversión (§7.6.6) y las asignaciones (§7.13).
23Las conversiones implícitas predefinidas siempre tienen éxito y nunca causan excepciones. Las conversiones
24implícitas definidas por el usuario correctamente diseñadas también deben exhibir estas características.

256.1.1 Conversiones de identidad


26Una conversión de identidad convierte de cualquier tipo al mismo tipo. Esta conversión existe solamente para
27que una entidad que ya sea de un tipo requerido pueda considerarse convertible a dicho tipo.

286.1.2 Conversiones numéricas implícitas


29Las conversiones implícitas numéricas son:
30• De sby te a shor t, int, long, float, double o decimal.
31• De byte a short, ushort, int, uint, long, ulong, float, double o decimal.
32• De short a int, long, float, double o decimal.
33• De ushort a int, uint, long, ulong, float, double o decimal.

253114 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


254 Capítulo 18 Código no seguro

1• De i n ta l ong, float, double o decimal.


2• De uint a long, ulong, float, double o decimal.
3• De long a float, double o decimal.
4• De ulong a float, double o decimal.
5• De char a ushort, int, uint, long, ulong, float, double o decimal.
6• De f l oa ta doub le.
7Las conversiones de i n t, u in t, long o ulong a ulong o float y de long o ulong a double pueden producir una
8pérdida de precisión, pero no una pérdida de magnitud. En las demás conversiones implícitas numéricas nunca
9se pierde información.
10No hay conversiones implícitas al tipo char, por tanto los valores de otros tipos integrales no se convierten
11automáticamente al tipo char.

126.1.3 Conversiones de enumeración implícitas


13Una conversión implícita de enumeración permite convertir el literal entero decimal (decimal-integer-literal) 0
14al tipo enum (enum-type).

156.1.4 Conversiones de referencia implícitas


16Las conversiones implícitas de referencia son:
17• De cualquier tipo de referencia (reference-type) a object.
18• De cualquier tipo de clase (class-type) S a cualquier tipo de clase T, a condición de que S se derive de T.
19• De cualquier tipo de clase (class-type) S a cualquier tipo de interfaz (interface-type) T, a condición de que S
20 implemente a T.
21• De cualquier tipo de interfaz (interface-type) S a cualquier tipo de interfaz (interface-type) T, a condición de
22 que S se derive de T.
23• De un tipo matricial (array-type) S con un tipo de elemento SE a un tipo matricial T con un tipo de elemento
24 TE, siempre que todo lo siguiente sea verdadero:
25 o S y T difieren solamente en el tipo de elemento. Esto es, S y T tienen el mismo número de dimensiones.
26 o Tanto SE como TE son tipos de referencia (reference-types).
27 o Existe una conversión implícita de referencia de SE a TE.
28• De cualquier tipo matricial (array-type) a System.Array.
29• De cualquier tipo delegado (delegate-type) a System.Delegate.
30• Del tipo nulo a cualquier tipo de referencia (reference-type).
31Las conversiones implícitas de referencia son aquellas entre tipos de referencia (reference-types) que se puede
32demostrar que siempre se realizan con éxito y, por lo tanto, no requieren comprobaciones en tiempo de
33ejecución.
34Las conversiones de referencia, tanto implícitas como explícitas, nunca cambian la identidad referencial del
35objeto que se convierte. Es decir, si bien una conversión de referencia puede cambiar el tipo de la referencia,
36nunca cambia el tipo o el valor del objeto al que se refiere.

255Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 115


256Especificación del lenguaje C#

16.1.5 Conversiones boxing


2Una conversión boxing permite convertir implícitamente cualquier tipo de valor (value-type) al tipo ob jec to
3Sys tem.Va lueType, o a cualquier tipo de interfaz (interface-type) implementado por el tipo de valor (value-
4type). La conversión boxing de un valor a un tipo de valor (value-type) consiste en asignar una instancia del
5objeto y después copiar en ella el valor del tipo de valor (value-type). Se puede aplicar a una estructura la
6conversión boxing al tipo System.ValueType, puesto que ésa es una clase base para todas las
7estructuras (§11.3.2).
8Las conversiones boxing se describen más detalladamente en §4.3.1.

96.1.6 Conversiones implícitas de expresión constante


10Una conversión implícita de expresión constante permite las siguientes conversiones:
11• Una expresión constante (constant-expression) (§7.15) de tipo i n tse puede convertir al tipo sby te, byte,
12 short, ushort, uint o ulong, siempre que el valor de la expresión constante (constant-expression) quede
13 dentro del intervalo del tipo de destino.
14• Una expresión constante (constant-expression) de tipo long puede convertirse al tipo ulong, a condición de
15 que el valor de la expresión constante no sea negativo.

166.1.7 Conversiones definidas por el usuario implícitas


17Una conversión implícita definida por el usuario consta de una conversión implícita opcional estándar, seguida
18por la ejecución de un operador de conversión implícita definido por el usuario, seguido por otra conversión
19implícita opcional estándar. Las reglas exactas para la evaluación de conversiones definidas por el usuario se
20describen en §6.4.3.

216.2 Conversiones explícitas


22Las siguientes conversiones se clasifican como conversiones explícitas:
23• Todas las conversiones implícitas.
24• Conversiones explícitas numéricas.
25• Conversiones explícitas de enumeración.
26• Conversiones explícitas de referencia.
27• Conversiones explícitas de interfaz.
28• Conversiones Unboxing.
29• Conversiones explícitas definidas por el usuario.
30Las conversiones explícitas pueden producirse en las expresiones de conversión de tipos (§7.6.6).
31El conjunto de conversiones explícitas incluye todas las conversiones implícitas. Esto quiere decir que están
32permitidas las expresiones de conversión de tipos redundantes.
33Las conversiones explícitas son conversiones que no se puede demostrar que siempre se realizan correctamente,
34conversiones en las que se sabe que se puede producir pérdida de información y conversiones en dominios de
35tipos que, por sus diferencias, merecen una mención explícita.

257116 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


258 Capítulo 18 Código no seguro

16.2.1 Conversiones explícitas numéricas


2Las conversiones numéricas explícitas son las conversiones de un tipo numérico (numeric-type) a otro tipo
3numérico para el que no existe ya una conversión de tipo numérico implícita (§6.1.2):
4• De sby te a byte, ushort, uint, ulong o char.
5• De byte a sby te y char.
6• De short a sbyte, byte, ushort, uint, ulong o char.
7• De ushort a sbyte, byte, short o char.
8• De int a sbyte, byte, short, ushort, uint, ulong o char.
9• De uint a sbyte, byte, short, ushort, int o char.
10• De long a sbyte, byte, short, ushort, int, uint, ulong o char.
11• De ulong a sbyte, byte, short, ushort, int, uint, long o char.
12• De char a sbyte, byte o short.
13• De float a sbyte, byte, short, ushort, int, uint, long, ulong, char o decimal.
14• De double a sbyte, byte, short, ushort, int, uint, long, ulong, char, float o decimal.
15• De decimal a sbyte, byte, short, ushort, int, uint, long, ulong, char, float o double.
16Dado que las conversiones explícitas incluyen todas las conversiones numéricas implícitas y explícitas, siempre
17es posible convertir de cualquier tipo numérico (numeric-type) a cualquier otro tipo numérico (numeric-type)
18mediante una expresión de conversión de tipos (§7.6.6).
19Las conversiones explícitas numéricas pueden producir pérdida de información o incluso provocar excepciones.
20Una conversión explícita numérica se procesa como se explica a continuación:
21• Para una conversión de un tipo integral a otro tipo integral, el procesamiento depende del contexto de
22 comprobación de desbordamiento (§7.5.12) en el que tiene lugar la conversión:
23 o En un contexto checked, la conversión termina correctamente si el valor del operando de origen queda
24 dentro del intervalo del tipo de destino, pero inicia una excepción System.OverflowException si
25 queda fuera de dicho intervalo.
26 o En un contexto unchecked, la conversión siempre termina correctamente y procede como sigue.
27 • Si el tipo de origen es más grande que el de destino, el valor de origen se trunca descartando sus bits
28 “extra” más significativos. Después el resultado se trata como un valor del tipo de destino.
29 • Si el tipo de origen es menor que el tipo de destino, el valor de origen se amplía con un signo o con
30 ceros, de forma que tenga el mismo tamaño que el tipo de destino. La ampliación con signo se
31 utiliza si el tipo de origen tiene signo; se utiliza la ampliación con ceros si el tipo de origen no lleva
32 signo. Después el resultado se trata como un valor del tipo de destino.
33 • Si el tipo del origen tiene el mismo tamaño que el tipo de destino, el valor de origen se trata como
34 un valor del tipo de destino.
35• Cuando se convierte un valor de tipo decimal a un tipo integral, el valor de origen se redondea hacia cero
36 hasta el valor entero más próximo, y éste pasa a ser el resultado de la conversión. Si el valor integral
37 resultante queda fuera del intervalo del tipo de destino, se produce una excepción
38 System.OverflowException.

259Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 117


260Especificación del lenguaje C#

1• Para la conversión de un tipo f l oa to doub le a otro tipo integral, el procesamiento depende del contexto de
2 comprobación de desbordamiento (§7.5.12) en el que tiene lugar la conversión:
3 o En un contexto checked, la conversión sigue este procedimiento:
4 • Si el valor del operando es NaN o infinito, se inicia una excepción Sys tem.Over f l owExcept i on
.
5 • De lo contrario, el operando de origen se redondea hacia cero, hasta el valor integral más cercano.
6 Si dicho valor se encuentra dentro del intervalo del tipo de destino, pasará a ser el resultado de la
7 conversión.
8 • De lo contrario, se inicia una excepción Sys tem.Over f l owExcept i on
.
9 o En un contexto unchecked, la conversión siempre termina correctamente y procede como sigue.
10 • Si el valor del operando es NaN o infinito, el resultado de la conversión es un valor no especificado
11 del tipo de destino.
12 • De lo contrario, el operando de origen se redondea hacia cero, hasta el valor integral más cercano.
13 Si dicho valor se encuentra dentro del intervalo del tipo de destino, pasará a ser el resultado de la
14 conversión.
15 • De no ser así, el resultado de la conversión será un valor no especificado del tipo de destino.
16• Para una conversión de doub le a f l oa,tel valor double se redondea al valor float más próximo. Si el valor
17 double es demasiado pequeño para representarlo como float, el resultado se convierte en cero positivo o
18 cero negativo. Si el valor double es demasiado grande para representarlo como float, el resultado se
19 convierte en infinito positivo o infinito negativo. Si el valor double es NaN, el resultado también es NaN.
20• Para una conversión de float o double a decimal, el valor de origen se convierte a la representación
21 decimal y se redondea hasta al número más próximo después de la posición decimal 28 si es necesario
22 (§4.1.7). Si el valor de origen es demasiado pequeño para representarlo como decimal, el resultado es cero.
23 Si el valor de origen es NaN, infinito o demasiado grande para representarlo como decimal, se inicia una
24 excepción System.OverflowException.
25• Para una conversión de decimal a float o double, el valor decimal se redondea al valor float o double
26 más próximo. Aunque esta conversión puede perder precisión, nunca produce una excepción.

276.2.2 Conversiones de enumeración explícitas


28Las conversiones explícitas de enumeración son:
29• De sby te, byte, short, ushort, int, uint, long, ulong, char, float, double o decimal a cualquier tipo
30 enum (type-enum).
31• De cualquier tipo enum (type-enum) a sbyte, byte, short, ushort, int, uint, long, ulong, char, float,
32 double o decimal.
33• De cualquier tipo enum (enum-type) a cualquier otro tipo enum (enum-type).
34Una conversión explícita de enumeración entre dos tipos se procesa tratando a uno de los tipos enum (enum-
35type) como tipo subyacente del otro, y realizando, a continuación, una conversión numérica implícita o explícita
36entre los tipos resultantes. Por ejemplo, dado un tipo enum (enum-type) E con un tipo subyacente de int, una
37conversión de E a byte se trata como una conversión explícita numérica (§6.2.1) de int a byte, y una
38conversión de byte a E se procesa como una conversión implícita numérica (§6.1.2) de byte a int.

261118 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


262 Capítulo 18 Código no seguro

16.2.3 Conversiones explícitas de referencia


2Las conversiones explícitas de referencia son:
3• De ob jec ta cualquier otro tipo de referencia (reference-type).
4• De cualquier tipo de clase (class-type) S a cualquier tipo de clase (class-type) T, siempre que S sea una clase
5 base de T.
6• De cualquier tipo de clase (class-type) S a cualquier tipo de interfaz (interface-type) T, siempre que S no sea
7 sealed y que S no implemente a T.
8• De cualquier tipo de interfaz (interface-type) S a cualquier tipo de clase (class-type) T, siempre que T no sea
9 sealed ni implemente a S.
10• De cualquier tipo de interfaz (interface-type) S a cualquier tipo de interfaz (interface-type) T, a condición de
11 que S no se derive de T.
12• De un tipo matricial (array-type) S con un tipo de elemento SE a un tipo matricial T con un tipo de elemento
13 TE, siempre que todo lo siguiente sea verdadero:
14 o S y T difieren solamente en el tipo de elemento. Esto es, S y T tienen el mismo número de dimensiones.
15 o Tanto SE como TE son tipos de referencia (reference-types).
16 o Existe una conversión explícita de referencia de SE a TE.
17• De System.Array y las interfaces que implementa, a cualquier tipo matricial (array-type).
18• De System.Delegate y las interfaces que implementa a cualquier tipo delegado (delegate-type).
19Las conversiones explícitas de referencia son aquellas conversiones entre tipos de referencias que requieren
20comprobaciones en tiempo de ejecución para asegurar que son correctas.
21Para que una conversión explícita de referencia se realice correctamente en tiempo de ejecución, el valor del
22operando de origen debe ser una referencia null, o el tipo real del objeto al que hace referencia el operando de
23origen debe ser un tipo convertible al tipo de destino mediante una conversión implícita de referencia (§6.1.4).
24Si una conversión explícita de referencia produce un error, se inicia una excepción
25System.InvalidCastException.
26Las conversiones de referencia, tanto implícitas como explícitas, nunca cambian la identidad referencial del
27objeto que se convierte. Es decir, si bien una conversión de referencia puede cambiar el tipo de la referencia,
28nunca cambia el tipo o el valor del objeto al que se refiere.

296.2.4 Conversiones Unboxing


30Una conversión unboxing permite una conversión explícita del tipo object o System.ValueType a cualquier
31tipo de valor (value-type), o desde cualquier tipo de interfaz (interface-type) a cualquier tipo de valor (value-
32type) que implementa el tipo de interfaz. Una operación unboxing consiste en comprobar primero que la
33instancia del objeto es un valor al que se ha aplicado la conversión boxing del tipo de valor (value-type) dado, y
34copiar después el valor fuera de la instancia. Se puede aplicar a una estructura la conversión unboxing desde el
35tipo System.ValueType, porque ésa es una clase base para todas las estructuras (§11.3.2).
36Las conversiones unboxing se explican más detalladamente en in §4.3.2.

376.2.5 Conversiones explícitas definidas por el usuario


38Una conversión explícita definida por el usuario consta de una conversión explícita opcional estándar, seguida
39por la ejecución de un operador de conversión implícita o explícita definida por el usuario, seguido por otra

263Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 119


264Especificación del lenguaje C#

1conversión explícita opcional estándar. Las reglas exactas para la evaluación de conversiones definidas por el
2usuario se describen en §6.4.4.

36.3 Conversiones estándar


4Las conversiones estándar son conversiones predefinidas que pueden ocurrir como parte de una conversión
5definida por el usuario.

66.3.1 Conversiones implícitas estándar


7Las siguientes conversiones implícitas se clasifican como conversiones implícitas estándar:
8• Conversiones de identidad (§6.1.1)
9• Conversiones numéricas implícitas (§6.1.2)
10• Conversiones implícitas de referencia (§6.1.4)
11• Conversiones boxing (§6.1.5)
12• Conversiones implícitas de expresión constante (§6.1.6)
13Las conversiones implícitas estándar excluyen de forma específica las conversiones implícitas definidas por el
14usuario.

156.3.2 Conversiones explícitas estándar


16Las conversiones explícitas estándar son todas las conversiones implícitas estándar más el subconjunto de las
17conversiones explícitas para las cuales existe una conversión implícita estándar opuesta. Es decir, si existe una
18conversión implícita estándar de un tipo A a un tipo B, entonces existe una conversión explícita estándar del tipo
19A al tipo B y del tipo B al tipo A.

206.4 Conversiones definidas por el usuario


21C# permite la ampliación de las conversiones explícitas e implícitas predefinidas mediante conversiones
22definidas por el usuario. Las conversiones definidas por el usuario se introducen mediante la declaración de
23operadores de conversión (§10.9.3) en tipos de clase y struct.

246.4.1 Conversiones permitidas definidas por el usuario


25C# sólo permite la declaración de algunas conversiones definidas por el usuario. En concreto, no es posible
26redefinir una conversión explícita o implícita ya existente. Una clase o estructura tiene permitido declarar una
27conversión de un tipo de origen S a un tipo de destino T solamente si son verdaderos todos los siguientes:
28• S y T son tipos diferentes

29• S o T es el tipo de clase o estructura en el que tiene lugar la declaración del operador.

30• Ni S ni T son de tipo object ni un tipo de interfaz (interface-type).


31• T no es una clase base de S, y S tampoco lo es de T.
32Las restricciones aplicables a las conversiones definidas por el usuario se explican en §10.9.3.

336.4.2 Evaluación de conversiones definidas por el usuario


34Una conversión definida por el usuario convierte un valor de su tipo, denominado tipo de origen, a otro tipo,
35denominado tipo de destino. La evaluación de una conversión definida por el usuario se centra en descubrir el

265120 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


266 Capítulo 18 Código no seguro

1operador de conversión definido por el usuario más específico para los tipos de origen y de destino concretos.
2Esta determinación se divide en varios pasos:
3• Buscar el conjunto de clases y estructuras a partir del cual se consideran los operadores de conversión
4 definida por el usuario. Este conjunto consta del tipo de origen y sus clases base y el tipo de destino y sus
5 clases base (con los supuestos implícitos de que sólo las clases y estructuras pueden declarar operadores
6 definidos por el usuario y de que los tipos no de clase no tienen clases base).
7• A partir del conjunto de tipos, determinar qué operadores de conversión definida por el usuario son
8 aplicables. Para que un operador de conversión sea aplicable, debe ser posible realizar una conversión
9 estándar (§6.3) del tipo de origen al tipo de operando del operador, y debe ser posible realizar una
10 conversión estándar del tipo del resultado del operador al tipo de destino.
11• A partir del conjunto de operadores definidos por el usuario que puedan aplicarse, determinar qué operador
12 es el más específico sin ninguna ambigüedad. En términos generales, el operador más específico es aquél
13 cuyo tipo de operando es el “más próximo” al tipo de origen y cuyo tipo de resultado es el “más próximo” al
14 tipo de destino. En las próximas secciones se definen las reglas exactas para establecer el operador de
15 conversión definido por el usuario más específico.
16Una vez identificado un operador de conversión definido por el usuario más específico, la ejecución de la
17conversión definida por el usuario implica hasta tres pasos:
18• Primero, si se requiere, una conversión estándar del tipo de origen al tipo de operando del operador de
19 conversión definido por el usuario.
20• Después, invocar al operador de conversión definido por el usuario para que realice la conversión.
21• Por último, si se requiere, realizar una conversión estándar del tipo del resultado del operador de conversión
22 definido por el usuario al tipo de destino.
23La evaluación de una conversión definida por el usuario nunca necesita más de un operador de conversión
24definido por el usuario. Esto es, una conversión del tipo S al tipo T nunca ejecuta en primer lugar una
25conversión definida por el usuario de S a X y después una conversión definida por el usuario de X a T.
26En las próximas secciones se ofrecen las definiciones exactas de la evaluación de conversiones implícitas o
27explícitas definidas por el usuario. En las definiciones se usan los siguientes términos:
28• Si existe una conversión implícita estándar (§6.3.1) de un tipo A a un tipo B, y si ni A ni B are son tipos de
29 interfaz (interface-types), entonces se dice que A está abarcado por B, y que B abarca A.
30• El tipo que más abarca de un conjunto de tipos es aquél que abarca todos los demás tipos del conjunto. Si
31 ninguno de los tipos abarca a todos los demás, entonces el conjunto no tiene tipo que más abarca. En
32 términos más intuitivos, el tipo que más abarca es el “más grande” del conjunto, el tipo al que pueden
33 convertirse implícitamente todos los demás tipos.
34• El tipo más abarcado de un conjunto de tipos es aquél al que abarcan todos los demás tipos del conjunto. Si
35 ninguno de los tipos es abarcado por todos los demás, entonces el conjunto no tiene un tipo más abarcado.
36 En términos más intuitivos, el tipo más abarcado es el “más pequeño” del conjunto, aquél que puede
37 convertirse implícitamente a todos los demás tipos.

386.4.3 Conversiones explícitas definidas por el usuario


39Una conversión implícita definida por el usuario del tipo S al tipo T se procesa como sigue:
40• Se busca un conjunto de tipos, D, a partir del cual se consideran los operadores de conversión definida por el
41 usuario. Este conjunto está formado por S (si S es una clase o una estructura), las clases base de S (si S es
42 una clase) y T (si T es una clase o una estructura).

267Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 121


268Especificación del lenguaje C#

1• Se busca el conjunto de operadores de conversión definida por el usuario aplicables, U. Este conjunto consta
2 de los operadores de conversión implícita definida por el usuario declarados por las clases o estructuras de
3 D que convierten de un tipo incluyente S a un tipo abarcado por T. Si U está vacía, la conversión no estará
4 definida y se producirá un error en tiempo de ejecución.
5• Se busca el tipo de origen más específico, SX, de los operadores de U:
6 o Si uno de los operadores de U se convierte desde S, entonces SX es S.
7 o De lo contrario, SX es el tipo más abarcado del conjunto combinado de tipos de origen de los operadores
8 de U. Si no se encuentra un tipo más abarcado, la conversión será ambigua y se producirá un error en
9 tiempo de compilación.
10• Se busca el tipo de destino más específico, TX, de los operadores de U:
11 o Si uno de los operadores de U se convierte a T, entonces TX es T.
12 o De lo contrario, TX es el tipo más que más abarca del conjunto combinado de tipos de destino de los
13 operadores de U. Si no se encuentra un tipo más incluyente, la conversión será ambigua y se producirá
14 un error en tiempo de compilación.
15• Si U contiene exactamente un operador de conversión definido por el usuario que convierte de SX a TX, éste
16 es el operador de conversión más específico. Si no existe tal operador o si existen varios, la conversión será
17 ambigua y se producirá un error en tiempo de compilación. De lo contrario, se aplica la conversión definida
18 por el usuario:
19 o Si S no es SX, se realiza una conversión implícita estándar de S a SX.
20 o Se llama al operador de conversión más específico definido por el usuario para convertir de SX a TX.
21 o Si TX no es T, se realiza una conversión implícita estándar de TX a T.

226.4.4 Conversiones explícitas definidas por el usuario


23Una conversión explícita definida por el usuario del tipo S al tipo T se procesa como sigue:
24• Se busca un conjunto de tipos, D, a partir del cual se consideran los operadores de conversión definida por el
25 usuario. Este conjunto consta de S (si S es una clase o estructura), las clases base de S (si S es una clase), T
26 (si T es una clase o estructura) y las clases base de T (si T es una clase).
27• Se busca el conjunto de operadores de conversión definida por el usuario aplicables, U. Este conjunto consta
28 de los operadores de conversión implícita o explícita definidos por el usuario, declarados por las clases o
29 estructuras de D que convierten de un tipo que abarca o abarcado por S a un tipo que abarca o es abarcado
30 por T. Si U está vacía, la conversión no estará definida y se producirá un error en tiempo de ejecución.
31• Se busca el tipo de origen más específico, SX, de los operadores de U:
32 o Si uno de los operadores de U se convierte desde S, entonces SX es S.
33 o De lo contrario, si uno de los operadores de U convierte de los tipos que abarca S, entonces SX es el tipo
34 más abarcado del conjunto combinado de tipos de origen de estos operadores. Si no se encuentra un tipo
35 más abarcado, la conversión será ambigua y se producirá un error en tiempo de compilación.
36 o De lo contrario, SX es el tipo que más abarca del conjunto combinado de tipos de origen de los
37 operadores de U. Si no se encuentra un tipo más incluyente, la conversión será ambigua y se producirá
38 un error en tiempo de compilación.
39• Se busca el tipo de destino más específico, TX, de los operadores de U:

269122 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


270 Capítulo 18 Código no seguro

1 o Si uno de los operadores de U se convierte a T, entonces TX es T.


2 o De lo contrario, si uno de los operadores de U convierte a los tipos abarcados por T, entonces TX es el
3 tipo más incluyente del conjunto combinado de tipos de origen de estos operadores. Si no se encuentra
4 un tipo más incluyente, la conversión será ambigua y se producirá un error en tiempo de compilación.
5 o De lo contrario, TX es el tipo más abarcado del conjunto combinado de tipos de destino de los
6 operadores de U. Si no se encuentra un tipo más abarcado, la conversión será ambigua y se producirá un
7 error en tiempo de compilación.
8• Si U contiene exactamente un operador de conversión definido por el usuario que convierte de SX a TX, éste
9 es el operador de conversión más específico. Si no existe tal operador o si existen varios, la conversión será
10 ambigua y se producirá un error en tiempo de compilación. De lo contrario, se aplica la conversión definida
11 por el usuario:
12 o Si S no es SX, se realiza una conversión explícita estándar de S a SX.
13 o Se llama al operador de conversión más específico definido por el usuario para convertir de SX a TX.
14 o Si TX no es T, se realiza una conversión explícita estándar de TX a T.

271Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 123


272Especificación del lenguaje C#

1 7.Expresiones

2Una expresión es una secuencia de operadores y operandos. En este capítulo se definen la sintaxis, el orden de
3evaluación de los operandos y los operadores, así como el significado de las expresiones.

47.1 Clasificaciones de expresión


5Una expresión se puede clasificar como:
6• Un valor. Todos los valores tienen asociado un tipo.
7• Una variable. Todas variables tienen un tipo asociado, esto es, el tipo declarado de la variable.
8• Un espacio de nombres. Una expresión con su clasificación sólo puede aparecer como el miembro izquierdo
9 de un acceso a miembro (member-access) (§7.5.4). En cualquier otro contexto, una expresión que se
10 clasifica como un espacio de nombres produce un error en tiempo de compilación.
11• Tipo. Una expresión con esta clasificación sólo puede aparecer como el lado izquierdo de un acceso a
12 miembro (member-access) (§7.5.4), o como un operando para el operador as (§7.9.10), el operador i s
13 (§7.9.9) o el operador typeof (§7.5.11). En cualquier otro contexto, una expresión que se clasifica como un
14 tipo produce un error en tiempo de compilación.
15• Un grupo de métodos, que es un conjunto de métodos sobrecargados producidos por una búsqueda de
16 miembros (§7.3) Un grupo de métodos puede tener asociada una expresión de instancia. Cuando se invoca
17 un método de instancia, el resultado de la evaluación de la expresión de instancia se convierte en la instancia
18 representada por this (§7.5.7). Un grupo de métodos sólo está permitido en una expresión de invocación
19 (invocation-expression) (§7.5.5) o una expresión de creación de delegado (delegate-creation-expression)
20 (§7.5.10.3). En cualquier otro contexto, una expresión que se clasifica como un grupo de métodos produce
21 un error en tiempo de compilación.
22• Un acceso de propiedad. Todos los accesos de propiedades tienen un tipo asociado, esto es, el tipo declarado
23 de la propiedad. Además, un acceso a propiedad puede tener asociada una expresión de instancia. Si se
24 llama a un descriptor de acceso (el bloque get o set) de un acceso a propiedad de una instancia, el resultado
25 de la evaluación de la expresión de instancia se convierte en la instancia representada por this (§7.5.7).
26• Un acceso a evento. Todos los accesos de evento tienen un tipo asociado, el tipo declarado del evento.
27 Además, un acceso de evento puede tener asociada una expresión de instancia. Un acceso a evento puede
28 aparecer como el operando izquierdo de los operadores += y -= (§7.13.3). En cualquier otro contexto, una
29 expresión que se clasifica como un acceso a evento produce un error en tiempo de compilación.
30• Un acceso a indizador. Todos los accesos a indizadores tienen un tipo asociado, el tipo declarado del
31 indizador. Además, un acceso a indizador tiene asociadas una expresión de instancia y una lista de
32 argumentos. Si se llama a un descriptor de acceso (el bloque get o set) de un indizador, el resultado de la
33 evaluación de la expresión de instancia pasa a ser la instancia representada por this (§7.5.7), y el resultado
34 de la evaluación de la lista de argumentos se convierte en la lista de parámetros de la invocación.
35• Nada. Esto ocurre cuando la expresión es una invocación de un método con el tipo de valor devuelto void.
36 Una expresión clasificada como nada sólo es válida en el contexto de una expresión de instrucción
37 (statement-expression) (§8.6).

273124 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


274 Capítulo 18 Código no seguro

1El resultado final de una expresión nunca es un espacio de nombres, un tipo, un grupo de métodos o un acceso
2de evento. En lugar de ello, como se ha mencionado antes, estas categorías de expresiones son construcciones
3intermedias que sólo están permitidas en determinados contextos.
4Un acceso a propiedad o un acceso a indizador siempre se reclasifican como un valor mediante una llamada del
5descriptor de acceso get (get-accessor) o del descriptor de acceso set (set-accessor). El descriptor de acceso
6concreto viene determinado por el contexto del acceso a propiedad o del acceso a indizador: si el acceso es el
7destino de una asignación, se llama al descriptor de acceso set (set-accessor) para asignar un nuevo valor
8(§7.13.1). En otro caso, se invoca el descriptor de acceso get (get-accessor) para obtener el valor actual (§7.1.1).

97.1.1 Valores de expresiones


10La mayoría de las construcciones que involucran una expresión requieren en definitiva que la expresión denote
11un valor. En estos casos, si la expresión real denota un espacio de nombres, un tipo, un grupo de métodos o
12nada, se produce un error en tiempo de compilación. No obstante, si la expresión denota un acceso a propiedad,
13un acceso a indizador o una variable, el valor de la propiedad, el indizador o la variable se sustituyen de forma
14implícita:
15• El valor de una variable es sencillamente el valor almacenado en la ubicación de almacenamiento que ella
16 misma identifica. Una variable debe considerarse asignada definitivamente (§5.3) para poder obtener su
17 valor o, de lo contrario, se produce un error de tiempo de compilación.
18• El valor de una expresión de acceso a propiedad se obtiene mediante una llamada al descriptor de acceso get
19 (get-accessor) de la propiedad. Si la propiedad no tiene un descriptor de acceso get (get-accessor), se
20 produce un error durante la compilación. En caso contrario, se realiza una llamada a un miembro de función
21 (§7.4.3) y el resultado de la llamada pasa a ser el valor de la expresión de acceso a propiedad.
22• El valor de una expresión de acceso a indizador se obtiene mediante una llamada al descriptor de acceso get
23 (get-accessor) del indizador. Si el indizador no tiene un descriptor de acceso get (get-accessor), se produce
24 un error durante la compilación. En caso contrario, se realiza una llamada a un miembro de función (§7.4.3)
25 con la lista de argumentos asociada a la expresión de acceso al indizador, y el resultado de la llamada se
26 convierte en el valor de la expresión de acceso al indizador.

277.2 Operadores
28Las expresiones se construyen a partir de operandos y operadores. Los operadores de una expresión indican qué
29operaciones se aplican a los operandos. Entre los ejemplos de operadores se incluyen +, -, *, / y new. Son
30ejemplos de operandos los literales, campos, variables locales y expresiones.
31Existen tres tipos de operadores:
32• Operadores unarios. Los operadores unarios tienen un operando y utilizan la notación de prefijo (como –x) o
33 de postfijo (como x++).
34• Operadores binarios. Los operadores binarios tienen dos operandos y utilizan una notación infija (por
35 ejemplo, x + y).
36• Operador ternario. Sólo existe un operador ternario, ?:, tiene tres operandos y utiliza notación infija
37 (c? x: y).
38El orden de evaluación de los operadores de una expresión está determinado por la prioridad y asociatividad de
39los operadores (§7.2.1).
40Los operandos de una expresión se evalúan de izquierda a derecha. Por ejemplo, en F(i) + G(i++) * H(i), se
41llama al método F con el valor antiguo de i, después se llama al método G con el valor antiguo de i y, por último,

275Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 125


276Especificación del lenguaje C#

1se llama al método H con el valor nuevo de i. Esto es aparte y no está relacionado con la prioridad de los
2operadores.
3Algunos operadores no se pueden sobrecargar. La sobrecarga de operadores permite utilizar implementaciones
4de operadores definidas por el usuario en operaciones en las que al menos uno de los operandos es de un tipo
5estructura o clase definido por el usuario (§7.2.2).

67.2.1 Prioridad y asociatividad de los operadores


7Cuando una expresión contiene varios operadores, la prioridad de los operadores controla el orden de
8evaluación de los operadores individuales. Por ejemplo, la expresión x + y * z se evalúa como x + (y * z)
9porque el operador * tiene prioridad sobre el operador binario +. La prioridad de un operador está determinada
10por la definición de su producción gramatical asociada. Por ejemplo, una expresión aditiva (additive-expression)
11consta de una secuencia de expresiones multiplicativas (multiplicative-expressions) separadas por los operadores
12+ o -, lo que da a estos operadores una menor prioridad que a *, / y %.
13En la tabla siguiente se resumen todos los operadores, en orden de prioridad de mayor a menor:
14

Sección Categoría Operadores


7.5 Principal x.y f(x) a[x] x++ x-- new
typeof checked unchecked
7.6 Unario + - ! ~ ++x --x (T)x

7.7 Multiplicativo * / %

7.7 Sumatorio + -

7.8 Desplazamiento << >>

7.9 Comprobación < > <= >= is as


de tipos y
relacionales
7.9 Igualdad == !=

7.10 AND lógico &

7.10 XOR lógico ^

7.10 OR lógico |

7.11 AND condicional &&

7.11 OR condicional ||

7.12 Condicional ?:

7.13 Asignación = *= /= %= += -= <<= >>= &= ^= |=


15
16Cuando un operando se encuentra entre dos operadores con igual grado de prioridad, la asociatividad de los
17operadores controla el orden en que se ejecutan las operaciones.
18• A excepción de los operadores de asignación, todos los operadores binarios son asociativos por la
19 izquierda, lo que significa que las operaciones se realizan de izquierda a derecha. Por ejemplo, x + y + z se
20 evalúa como (x + y) + z.

277126 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


278 Capítulo 18 Código no seguro

1• Los operadores de asignación y el operador condicional (? :) son asociativos por la derecha, lo que significa
2 que las operaciones se ejecutan de derecha a izquierda. Por ejemplo, x = y = z se evalúa como x = (y + z).
3La precedencia y asociatividad pueden controlarse mediante el uso de paréntesis. Por ejemplo, x + y * z primero
4multiplica y por z y después suma el resultado a x, pero (x + y) * z primero suma x e y, y después multiplica el
5resultado por z.

67.2.2 Sobrecarga de operadores


7Todos los operadores unarios y binarios tienen implementaciones predefinidas que están disponibles
8automáticamente en cualquier expresión. Además de las implementaciones predefinidas, pueden introducirse
9implementaciones definidas por el usuario si se incluyen declaraciones operator en las clases y estructuras
10(§10.9). Las implementaciones de operador definidas por el usuario siempre tienen precedencia sobre las
11implementaciones de operador predefinidas: sólo se consideran las implementaciones de operador predefinidas
12cuando no existen implementaciones de operador definidas por el usuario que puedan aplicarse, como se explica
13en §7.2.3 y §7.2.4.
14Los operadores unarios sobrecargables son:

15 + - ! ~ ++ -- true false
16Aunque true y false no se utilizan explícitamente en las expresiones (por lo que no se incluyen en la tabla de
17prioridades de §7.2.1), se consideran operadores porque se los llama en varios contextos de expresión:
18expresiones booleanas (§7.16) y expresiones que implican el condicional (§7.12) y los operadores lógicos
19condicionales (§7.11).
20Los operadores binarios sobrecargables son:

21 + - * / % & | ^ << >> == != > < >= <=


22Sólo los operadores mencionados pueden sobrecargarse. En concreto, no es posible sobrecargar accesos a
23miembros, llamadas a métodos o los operadores =, &&, ||, ?:, checked, unchecked, new, typeof, as e is.
24Cuando se sobrecarga un operador binario, el operador correspondiente de asignación, si lo hay, también se
25sobrecarga de modo implícito. Por ejemplo, una sobrecarga del operador * también es una sobrecarga del
26operador *=. Esta categoría se explica con más detalle en la sección §7.13. Debe tenerse en cuenta que el propio
27operador de asignación (=) no se puede sobrecargar. Una asignación siempre realiza una simple copia bit a bit
28de un valor en una variable.
29Las operaciones de conversión de tipo, como (T)x, se sobrecargan proporcionando conversiones definidas por el
30usuario (§6.4).
31El acceso a elementos, del tipo a[x], no se considera un operador sobrecargable. En lugar de ello, se acepta la
32indización definida por el usuario mediante indizadores (§10.8).
33En las expresiones, las referencias a los operadores se realizan mediante la notación de operadores y, en las
34declaraciones, las referencias a los operadores se realizan mediante la notación funcional. En la tabla siguiente
35se muestra la relación entre las notaciones de operador y funcional para los operadores unarios y binarios. En la
36primera entrada, op denota cualquier operador de prefijo unario sobrecargable. En la segunda entrada, op denota
37los operadores de sufijo unarios ++ y - -. En la primera entrada, op denota cualquier operador binario
38sobrecargable.
39

279Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 127


280Especificación del lenguaje C#

Notación de operador Notación funcional


op x operator op( x )
x op operator op( x )
x op y operator op( x ,y)
40
41Las declaraciones de operador definidas por el usuario siempre requieren que por lo menos uno de los
42parámetros sea del tipo de la clase o estructura que contiene la declaración del operador. Por lo tanto, no es
43posible que un operador definido por el usuario tenga la misma firma que un operador predefinido.
44Las declaraciones de operador definidas por el usuario no pueden modificar la sintaxis, precedencia o
45asociatividad de un operador. Por ejemplo, el operador / siempre es un operador binario, siempre tiene el nivel
46de precedencia especificado en la §7.2.1 y siempre es asociativo por la izquierda.
47Aunque es posible que un operador definido por el usuario realice cualquier cálculo que le interese, no se
48recomiendan las implementaciones que generan resultados distintos de los que intuitivamente pueden esperarse.
49Por ejemplo, una implementación de operator == debe comparar la igualdad de los dos operandos y devolver
50un resultado bool apropiado.
51Las descripciones de los operadores individuales desde la §7.5 a la §7.13 especifican las implementaciones
52predefinidas de los operadores y cualquier regla adicional aplicable a cada operador. En las descripciones se
53utilizan los términos resolución de sobrecarga de operador unario, resolución de sobrecarga de operador
54binario y promoción numérica, cuyas definiciones se encuentran en las siguientes secciones.

557.2.3 Resolución de sobrecarga de operador unario


56Una operación de la forma op x o x op, donde op es un operador unario sobrecargable, y x es una expresión de
57tipo X, se procesa como sigue:
58• El conjunto de operadores definidos por el usuario candidatos suministrados por X para la operación
59 operator op(x) se determina aplicando las reglas de la §7.2.5.
60• Si el conjunto de operadores candidatos definidos por el usuario no está vacío, se convierte en el conjunto de
61 operadores candidatos para la operación. De lo contrario, las implementaciones del operator op unario
62 predefinidas se convierten en el conjunto de operadores candidatos para la operación. Las implementaciones
63 predefinidas de un operador dado se especifican en la descripción del operador (§7.5 y §7.6).
64• Las reglas de resolución de las sobrecargas de §7.4.2 se aplican al conjunto de operadores candidatos para
65 seleccionar el mejor operador con respecto a la lista de argumentos (x), y este operador es el resultado del
66 proceso de resolución de las sobrecargas. Si la resolución de las sobrecargas no puede seleccionar un
67 operador único idóneo, se producirá un error en tiempo de compilación.

687.2.4 Resolución de sobrecarga de operador binario


69Una operación de la forma x op y, donde op es un operador binario sobrecargable, x es una expresión de tipo X
70e y es una expresión de tipo Y, se procesa como sigue:
71• Se determina el conjunto de operadores candidatos definidos por el usuario suministrado por X e Y para la
72 operación operator op(x, y). El conjunto consta de la unión de los operadores candidatos suministrados por
73 X y los operadores candidatos suministrados por Y, cada uno de los cuales se determina mediante las reglas
74 de §7.2.5. Si X e Y son del mismo tipo, o si X e Y se derivan de un tipo base común, los operadores
75 candidatos compartidos sólo se producen una vez en el conjunto combinado.
76• Si el conjunto de operadores candidatos definidos por el usuario no está vacío, se convierte en el conjunto de
77 operadores candidatos para la operación. De lo contrario, las implementaciones del operator op binario

281128 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


282 Capítulo 18 Código no seguro

1 predefinidas pasan a ser el conjunto de operadores candidatos para la operación. Las implementaciones
2 predefinidas de un operador dado se especifican en la descripción del operador (§7.7 a §7.13).
3• Las reglas de resolución de las sobrecargas de §7.4.2 se aplican al conjunto de operadores candidatos para
4 seleccionar el mejor operador con respecto a la lista de argumentos ( x , y, )y este operador es el resultado del
5 proceso de resolución de las sobrecargas. Si la resolución de las sobrecargas no puede seleccionar un
6 operador único idóneo, se producirá un error en tiempo de compilación.

77.2.5 Operadores candidatos definidos por el usuario


8Dados un tipo T y una operación opera to rop(A), donde op es un operador sobrecargable y A es una lista de
9argumentos, el conjunto de operadores candidatos definidos por el usuario suministrado por T para el operator
10op(A) se determina como sigue:
11• Para todas las declaraciones de operator op en T, si por lo menos un operador es aplicable (§7.4.2.1) con
12 respecto a la lista de argumentos A, entonces el conjunto de operadores candidatos consta de todas las
13 declaraciones aplicables de operator op en T.
14• De lo contrario, si T es ob jec t, el conjunto de operadores candidatos está vacío.
15• En otros casos, el conjunto de operadores candidatos suministrado por T es el conjunto de operadores
16 candidatos suministrado por la clase base directa de T.

177.2.6 Promociones numéricas


18Una promoción numérica consiste en realizar de forma automática determinadas conversiones implícitas de los
19operandos de los operadores numéricos unarios y binarios predefinidos. La promoción numérica no es un
20mecanismo exclusivo, sino más bien un efecto de la aplicación de la resolución de las sobrecargas a los
21operadores predefinidos. La promoción numérica en concreto no afecta a la evaluación de los operadores
22definidos por el usuario, aunque dichos operadores puedan implementarse de manera que presenten efectos
23similares.
24Como ejemplo de promoción numérica, consideremos las implementaciones predefinidas del operador binario *:
25 i n t opera to r * ( i n t x , i n t y ) ;
26 u in t opera to r * (u in t x , u in t y ) ;
27 l ong opera to r * ( l ong x , l ong y ) ;
28 u long opera to r * (u long x , u long y ) ;
29 f l oa t opera to r * ( f l oa t x , f l oa t y ) ;
30 doub le opera to r * (doub le x , doub le y ) ;
32Cuando se aplican las reglas de resolución de las sobrecargas (§7.4.2) a este conjunto de operadores, el efecto
31que se produce es la selección del primero de los operadores para el cual existen conversiones implícitas de los
33
34tipos de los operandos. Por ejemplo, para la operación b * s, donde b es un byte y s es short, la resolución de
35las sobrecargas selecciona operator *(int, int) como el mejor operador. De esta forma, el efecto producido es
36que b y s se convierten a int, y el tipo del resultado es int. De modo similar, para la operación i * d, donde i es
37un int y d es double, la resolución de sobrecargas selecciona operator *(double, double) como el mejor
38operador.

397.2.6.1 Promociones numéricas unarias


40Una promoción numérica unaria se produce para los operandos de los operadores unarios predefinidos +, – y ~.
41Una promoción numérica unaria sencillamente es la conversión de operandos de tipo sbyte, byte, short,
42ushort o char al tipo int. Asimismo, para el operador unario –, la promoción numérica unaria convierte los
43operandos del tipo uint al tipo long.

283Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 129


284Especificación del lenguaje C#

17.2.6.2 Promociones numéricas binarias


2La promoción numérica binaria se produce para los operandos de los operadores binarios predefinidos +, –, *, /,
3%, &, |, ^, ==, !=, >, <, >= y <=. La promoción numérica binaria convierte implícitamente los dos
4operandos a un tipo común que, en caso de los operadores no relacionales, también se convierte en el tipo del
5resultado de la operación. Una promoción numérica binaria consiste en aplicar las reglas siguientes, en el orden
6en que se exponen aquí:
7• Si uno de los operandos es de tipo decimal, el otro se convierte al tipo decimal o se produce un error en
8 tiempo de compilación si el otro operando es de tipo float o double.
9• O bien, si uno de los operandos es de tipo doub le, el otro se convierte al tipo doub le.
10• O bien, si uno de los operandos es de tipo f l oa,tel otro se convierte al tipo f l oa.t
11• O bien, si uno de los operandos es de tipo ulong, el otro se convierte al tipo ulong o se produce un error en
12 tiempo de compilación si el otro operando es de tipo sbyte, short, int o long.
13• O bien, si uno de los operandos es de tipo l ong, el otro se convierte al tipo l ong.
14• O bien, si uno de los operandos es de tipo uint y el otro es de tipo sbyte, short o int, los dos operandos se
15 convierten al tipo long.
16• O bien, si uno de los operandos es de tipo u in t, el otro se convierte al tipo u in t.
17• O bien, los dos operandos se convierten al tipo int.
18Téngase en cuenta que la primera regla no permite las operaciones que mezclan el tipo decimal con los tipos
19double y float. La regla se basa en que no hay conversiones implícitas entre el tipo decimal y los tipos double
20y float.
21También debe tenerse en cuenta que un operando no puede ser de tipo ulong si el otro es de un tipo integral con
22signo. El motivo es que no existe un tipo integral que pueda representar la gama completa de ulong así como
23los tipos integrales con signo.
24En los dos casos anteriores, puede utilizarse una expresión de conversión de tipos para convertir de forma
25explícita uno de los operandos a un tipo que sea compatible con el otro.
26En el siguiente ejemplo:
27 dec ima l AddPe rcen t (dec ima l x , doub le percen t ) {
28 re tu rn x * (1 .0 + percen t / 100 .0 ) ;
29 }
30se produce un error de tiempo de compilación porque un decimal no puede multiplicarse por un double. El
31error se resuelve mediante la conversión explícita del segundo operando a decimal, de la manera siguiente:
32 dec ima l AddPe rcen t (dec ima l x , doub le percen t ) {
33 re tu rn x * (dec ima l ) (1 .0 + percen t / 100 .0 ) ;
34 }
357.3 Búsqueda de miembros
36Una búsqueda de miembros es el proceso por el cual se determina el significado de un nombre en el contexto de
37un tipo. Una búsqueda de miembros puede ocurrir como parte de la evaluación de un nombre simple (simple-
38name) (§7.5.2) o un acceso a miembro (member-access) (§7.5.4) en una expresión.
39Una búsqueda de miembros de un nombre N en un tipo T se procesa como sigue:

285130 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


286 Capítulo 18 Código no seguro

1• En primer lugar, se construye el conjunto de todos los miembros accesibles (§3.5) llamados N declarados en
2 T y los tipos base (§7.3.1) de T. Se excluyen del conjunto las declaraciones que incluyen un modificador
3 override. Si no existen y están accesibles miembros llamados N, la búsqueda no produce resultados y no se
4 evalúan los siguientes pasos.
5• A continuación, se eliminan del conjunto los miembros que están ocultos por otros miembros. Por cada
6 miembro S.M del conjunto, donde S es el tipo en el que se declara el miembro M, se aplican las siguientes
7 reglas:
8 o Si M es un miembro de constante, campo, propiedad, evento, tipo o enumeración, entonces se eliminan
9 del conjunto todos los miembros declarados en un tipo base de S.
10 o Si M es un método, entonces todos los miembros no de método declarados en un tipo base de S se
11 eliminan del conjunto, así como todos los métodos con la misma firma que M declarados en un tipo base
12 de S.
13• Por último, una vez eliminados los miembros ocultos, se determina el resultado de la búsqueda:
14 o Si el conjunto está formado por un solo miembro no de método, entonces este miembro es el resultado
15 de la búsqueda.
16 o O bien, si el conjunto sólo contiene métodos, entonces este grupo de métodos es el resultado de la
17 búsqueda.
18 o O bien, la búsqueda es ambigua y ocurre un error de tiempo de compilación (esta situación sólo puede
19 ocurrir en una búsqueda de miembros en una interfaz que contiene varias interfaces base directas).
20Para búsquedas de miembros en tipos que no sean interfaces, y búsquedas de miembros en interfaces que sean
21estrictamente de herencia simple (cada interfaz en la cadena de la herencia tienen exactamente cero o una
22interfaz base directa), el efecto de las reglas de búsqueda es sencillamente que los miembros derivados ocultan a
23los miembros base del mismo nombre o la misma firma. Este tipo de búsquedas de herencia simple nunca son
24ambiguas. Las ambigüedades que pueden surgir de las búsquedas de miembros en interfaces de herencia
25múltiple se describen en la §13.2.5.

267.3.1 Tipos base


27Para los fines de búsqueda de miembros, se considera que un tipo T tiene los siguientes tipos base:
28• Si T es object, entonces T no tiene tipo base.
29• Si T es un tipo de enumeración (enum-type), los tipos base de T son los tipos de clase System.Enum,
30 System.ValueType y object.
31• Si T es un tipo de estructura (struct-type), los tipos base de T son los tipos de clase System.ValueType y
32 object.
33• Si T es un tipo de clase (class-type), los tipos base de T son las clases base de T, incluido el tipo de clase
34 object.
35• Si T es un tipo de interfaz (interface-type), los tipos base de T son las interfaces base de T y el tipo de clase
36 object.
37• Si T es un tipo de matriz (array-type), los tipos base de T son los tipos de clase System.Array y object.
38• Si T es un tipo delegado (delegate-type), los tipos base de T son los tipos de clase System.Delegate y
39 object.

287Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 131


288Especificación del lenguaje C#

17.4 Miembros de función


2Los miembros de función son miembros que contienen instrucciones ejecutables. Siempre son miembros de
3tipos y no pueden ser miembros de espacios de nombres. C# define las siguientes categorías de miembros de
4función:
5• Métodos
6• Propiedades
7• Eventos
8• Indizadores
9• Operadores definidos por el usuario
10• Constructores de instancia
11• Constructores static
12• Destructores
13Excepto para los destructores y los constructores estáticos (que no se pueden invocar de manera explícita), las
14instrucciones contenidas en miembros de función se ejecutan mediante invocaciones de miembros de función.
15La sintaxis concreta de la programación de invocaciones de miembros de función depende de la categoría del
16miembro concreto.
17La lista de argumentos (§7.4.1) de la invocación de un miembro de función proporciona valores reales o
18referencias de variable a los parámetros del miembro de función.
19Las llamadas de métodos, indizadores, operadores y constructores de instancia utilizan la resolución de
20sobrecargas para determinar a qué miembro de un conjunto candidato de miembros de función se debe llamar.
21Este proceso se describe en §7.4.2.
22Una vez identificado un miembro de función concreto en tiempo de compilación, posiblemente mediante
23resolución de sobrecargas, el proceso exacto de invocar el miembro de función en tiempo de ejecución se
24explica en §7.4.3.
25En la tabla siguiente se resume el procesamiento que tiene lugar en las construcciones que implican las seis
26categorías de miembros de función, que se puede invocar explícitamente. En la tabla, e, x, y y value indican
27expresiones clasificadas como variables o valores, T indica una expresión clasificada como un tipo, F es el
28nombre simple de un método y P es el nombre simple de una propiedad.
29

289132 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


290 Capítulo 18 Código no seguro

Construcción Ejemplo Descripción


Invocación de F(x, y ) Se aplica la resolución de sobrecargas para seleccionar el mejor
método método F de la clase o estructura contenedora. Se llama al
método con la lista de argumentos ( x ,y). Si el método no es
static, la expresión de instancia es this.
T.F(x, y ) Se aplica la resolución de sobrecargas para seleccionar el mejor
método F de la clase o estructura T. Se produce un error en
tiempo de compilación si el método no es static. Se llama al
método con la lista de argumentos (x, y).
e.F(x, y ) Se aplica la resolución de sobrecargas para seleccionar el mejor
método F de la clase, estructura o interfaz dada por el tipo de e.
Se produce un error en tiempo de compilación si el método es
static. Se llama al método con la expresión de instancia e y la
lista de argumentos (x, y).
Acceso a la P Se invoca al descriptor de acceso get de la propiedad P en la
propiedad clase o estructura contenedora. Se produce un error en tiempo
de compilación si P es de sólo escritura. Si P no es static, la
expresión de instancia es this.
P = value Se invoca al descriptor de acceso set de la propiedad P en la
clase o estructura contenedora con la lista de argumentos
(value). Se produce un error en tiempo de compilación si P es
de sólo lectura. Si P no es static, la expresión de instancia es
this.
T.P Se invoca al descriptor de acceso get de la propiedad P en la
clase o estructura T. Se produce un error en tiempo de
compilación si P no es static o si es de sólo escritura.
T.P = value Se invoca al descriptor de acceso set de la propiedad P en la
clase o estructura T con la lista de argumentos (value). Se
produce un error en tiempo de compilación si P no es static o
si es de sólo lectura.
e.P Se invoca al descriptor de acceso get de la propiedad P en la
clase, estructura o interfaz dada por el tipo de e con la
expresión de instancia e. Se produce un error en tiempo de
compilación si P es static o si es de sólo escritura.
e.P = value Se invoca el descriptor de acceso set de la propiedad P en la
clase, estructura o interfaz dada por el tipo de e con la
expresión de instancia e y la lista de argumentos (value). Se
produce un error en tiempo de compilación si P es static o si es
de sólo lectura.

291Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 133


292Especificación del lenguaje C#

Construcción Ejemplo Descripción


Acceso a E += value Se invoca al descriptor de acceso add del evento E en la clase o
evento estructura contenedora. Si E no es static, la expresión de
instancia es this.
E -= value Se invoca al descriptor de acceso remove del evento E en la
clase o estructura contenedora. Si E no es static, la expresión de
instancia es this.
T.E += value Se invoca al descriptor de acceso add del evento E en la clase o
estructura T. Si E no es estático, se produce un error en tiempo
de compilación.
T.E -= value Se invoca al descriptor de acceso remove del evento E en la
clase o estructura T. Si E no es estático, se produce un error en
tiempo de compilación.
e.E += value Se invoca al descriptor de acceso add del evento E en la clase,
estructura o interfaz dada por el tipo de e con la expresión de
instancia e. Si E es estático, se produce un error en tiempo de
compilación.
e.E -= value Se invoca al descriptor de acceso remove del evento E en la
clase, estructura o interfaz dada por el tipo de e con la
expresión de instancia e. Si E es estático, se produce un error
en tiempo de compilación.
Acceso al e[x, y] La resolución de sobrecarga se aplica para seleccionar el mejor
indizador indizador de la clase, estructura o interfaz dada por el tipo de e.
El descriptor de acceso get del indizador se invoca con la
expresión de instancia e y la lista de argumentos (x, y). Se
produce un error en tiempo de compilación si el indizador es de
sólo escritura.
e[x, y] = value Se aplica la resolución de sobrecargas para seleccionar el mejor
indizador de la clase, estructura o interfaz dada por el tipo de e.
Se invoca el descriptor de acceso set del indizador con la
expresión de instancia e y la lista de argumentos (x, y, value).
Se produce un error en tiempo de compilación si el indizador es
de sólo lectura.
Invocación de -x Se aplica la resolución de sobrecargas para seleccionar el mejor
operador operador unario de la clase, estructura o interfaz dada por el
tipo de x. Se invoca al operador seleccionado con la lista de
argumentos (x).
x+y Se aplica la resolución de sobrecargas, para seleccionar el
mejor operador binario de las clases o estructuras dadas por los
tipos de x e y. El operador seleccionado se invoca con la lista
de argumentos (x, y).
Invocación de new T(x, y) La resolución de sobrecarga se aplica para seleccionar el mejor
constructores constructor de la clase o estructura T. Se invoca al constructor
de instancias de instancias con la lista de argumentos (x, y).
30

293134 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


294 Capítulo 18 Código no seguro

17.4.1 Listas de argumentos


2Toda invocación de un miembro de función incluye una lista de argumentos que proporciona los valores reales o
3las referencias de variable de los parámetros del miembro de función. La sintaxis para especificar la lista de
4argumentos de una invocación de miembro de función depende de la categoría del miembro de función:
5• Para los constructores de instancia, métodos y delegados, los argumentos se especifican como una lista de
6 argumentos (argument-list), como se explica a continuación.
7• Para las propiedades, la lista de argumentos está vacía cuando se llama al descriptor de acceso get, y está
8 formada por la expresión especificada como el operando derecho del operador de asignación cuando se
9 invoca el descriptor de acceso se t.
10• Para los eventos, la lista de argumentos está formada por la expresión especificada como el operando
11 derecho del operador += o - =.
12• Para los indizadores, la lista de argumentos está formada por las expresiones especificadas entre los
13 corchetes del acceso a indizador. Cuando se invoca el descriptor de acceso se t, la lista de argumentos
14 incluye además la expresión especificada como el operando derecho del operador de asignación.
15• Para los operadores definidos por el usuario, la lista de argumentos está formada por el operando único del
16 operador unario o los dos operandos del operador binario.
17Los argumentos de propiedades (§10.6), eventos (§10.7) y operadores definidos por el usuario (§10.9) siempre
18se pasan como parámetros de valor (§10.5.1.1). Los argumentos de indizadores (§10.8) siempre se pasan como
19parámetros de valor (§ 17.5.1.1) o matrices de parámetros (§10.5.1.4). Los parámetros de referencia y de salida
20no se aceptan para estas categorías de miembros de función.
21Los argumentos de llamadas a constructores de instancia, métodos o delegados se especifican como una lista de
22argumentos (argument-list):
23 argument-list:
24 argument
25 argument-list , argument
26 argument:
27 expression
28 re f variable-reference
29 out variable-reference
30Una lista de argumentos (argument-list) consta de uno o más argumentos (arguments), separados por comas.
31Cada argumento puede tomar una de las siguientes formas:
32• Una expresión (expression), para indicar que el argumento se pasa como un parámetro de valor (§10.5.1.1).
33• La palabra clave re fseguida por una referencia a variable (variable-reference) (§5.4), para indicar que el
34 argumento se pasa como un parámetro de referencia (§10.5.1.2). Una variable debe estar asignada de
35 manera definitiva (§5.3) antes de que se pueda pasar como parámetro de referencia. La palabra clave out
36 seguida por una referencia a variable (variable-reference) (§5.4), para indicar que el argumento se pasa
37 como un parámetro de salida (§10.5.1.3). Una variable se considera asignada de manera definitiva (§5.3)
38 después de una llamada a miembro de función en la que la variable se pasa como un parámetro de salida.
39Durante el procesamiento de una invocación de miembro de función (§7.4.3) en tiempo de ejecución, las
40referencias de expresiones o de variables de una lista de argumentos se evalúan por orden, de izquierda a
41derecha, como sigue:

295Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 135


296Especificación del lenguaje C#

1• Para un parámetro de valor, se evalúa la expresión del argumento y se realiza una conversión implícita
2 (§6.1) al tipo de parámetro correspondiente. El valor resultante pasa a ser el valor inicial del parámetro de
3 valor en la invocación del miembro de función.
4• Para un parámetro de referencia o de salida, se evalúa la referencia de variable, mientras que la ubicación de
5 almacenamiento resultante pasa a ser la ubicación de almacenamiento, representada por el parámetro de la
6 invocación del miembro de función. Si la referencia de variable dada como parámetro de referencia o de
7 salida es un elemento de matriz de tipo de referencia (reference-type), se lleva a cabo una comprobación en
8 tiempo de ejecución para garantizar que el tipo de elemento de la matriz es idéntico al tipo del parámetro. Si
9 esta comprobación produce un error, se inicia una excepción Sys tem.Ar rayTypeMismatchExcept i on .
10Los métodos, indizadores y constructores de instancia pueden declarar su parámetro situado más a la derecha
11como una matriz de parámetros (§10.5.1.4). Estos miembros de función se invocan en su forma normal o en su
12forma expandida, dependiendo de lo que sea aplicable (§7.4.2.1):
13• Si se invoca un miembro de función con una matriz de parámetros en su forma normal, el argumento pasado
14 por la matriz de parámetros debe ser una expresión única de un tipo que sea implícitamente convertible
15 (§6.1) al tipo de la matriz de parámetros. En este caso, la matriz de parámetros actúa exactamente como un
16 parámetro de valor.
17• Si se invoca un miembro de función con una matriz de parámetros en su forma expandida, la invocación
18 debe especificar cero o más argumentos para la matriz de parámetros, donde cada argumento es una
19 expresión de un tipo implícitamente convertible (§6.1) al tipo de los elementos de la matriz de parámetros.
20 En este caso, la invocación crea una instancia del tipo de la matriz de parámetros con una longitud
21 correspondiente al número de argumentos, inicializa los elementos de la instancia de matriz con los valores
22 de los argumentos especificados y utiliza la instancia de matriz recién creada como argumento real.
23Las expresiones de una lista de argumentos siempre se evalúan en el orden en que están escritas. Por lo tanto, en
24el siguiente ejemplo:
25 c lass Tes t
26 {
27 s ta t i c vo id F ( i n t x , i n t y , i n t z ) {
28 Sys tem.Conso le .Wr i teL ine ( "x = {0} , y = {1} , z = {2}" , x , y , z ) ;
29
30 s ta t i c vo id Main ( ) {
31 i n t i = 0;
32 F ( i++, i++, i++) ;
33 }
34produce el resultado
35
36 x = 0, y = 1, z = 2
37Las reglas de covarianza matricial (§12.5) permiten que un valor de un tipo de matriz A[ ] se trate como una
38referencia a una instancia de un tipo matricial B[ ], siempre que exista una conversión implícita de referencias de
39B a A. Debido a estas reglas, si se pasa un elemento de matriz de un tipo de referencia (reference-type) como un
40parámetro de referencia o de resultado, se lleva a cabo una comprobación en tiempo de ejecución para garantizar
41que el tipo de elemento de la matriz es idéntico al tipo del parámetro. En el siguiente ejemplo:
42 c lass Tes t
43 {
44 s ta t i c vo id F ( re f ob jec t x ) { . . . }

297136 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


298 Capítulo 18 Código no seguro

1 static void Main() {

2 object[] a = new object[10];

3 object[] b = new string[10];

4 F(ref a[0]); // Ok

58la segunda invocación


F(refdeb[1]); // ArrayTypeMismatchException
F causa una excepción Sys tem.Ar rayTypeMismatchExcept i on , puesto que el tipo
9del elemento de b es string y no object.
6
10Cuando se llama a un miembro de función con una matriz de parámetros en su forma expandida, la invocación
117se trata exactamente como si se hubiera insertado una expresión de creación de matriz con un inicializador de
12matriz (§7.5.10.2) alrededor de los parámetros expandidos. Por ejemplo, dada la declaración

13 void F(int x, int y, params object[] args);


14las invocaciones siguientes de la forma expandida del método

15 F(10, 20);

16 F(10, 20, 30, 40);


18
17se corresponden exactamente con
19 F(10, 20, new object[] {});

20 F(10, 20, new object[] {30, 40});


22
21En particular, debe tenerse en cuenta que si se pasan cero argumentos como matriz de parámetros, se crea una
23matriz vacía.

247.4.2 Resolución de sobrecargas


25La resolución de sobrecargas es un mecanismo del tiempo de compilación que selecciona el mejor miembro de
26función que puede invocarse dados una lista de argumentos y un conjunto de miembros de función candidatos.
27La resolución de sobrecargas selecciona el miembro de función al que se invoca en los contextos siguientes
28únicos en C#:
29• Invocación de un método con nombre en una expresión de llamada (invocation-expression) (§7.5.5).
30• Invocación de un constructores de instancia con nombre en una expresión de creación de objeto (object-
31 creation-expression) (§7.5.10.1).
32• Invocación de un descriptor de acceso a indizador mediante un acceso a elementos (element-access)
33 (§7.5.6).
34• Llamada de un operador predefinido o definido por el usuario al que se hace referencia en una expresión
35 (§7.2.3 y §7.2.4).
36Cada uno de estos contextos define el conjunto de miembros de función candidatos y la lista de argumentos a su
37propia manera, según se describe detalladamente en las secciones anteriormente citadas. Por ejemplo, el
38conjunto de candidatos para una invocación a un método no incluye métodos marcados con override (§7.3) y
39los métodos de una clase base no son candidatos si ningún método de una clase derivada es aplicable (§7.5.5.1).
40Una vez identificados los miembros de función candidatos y la lista de argumentos, la selección del mejor
41miembro de función es la misma en todos los casos:
42• Dado el conjunto de miembros de función candidatos aplicables, se localiza el mejor miembro de función
43 del conjunto. Si el conjunto sólo contiene un miembro de función, ése es el mejor miembro. O bien, el mejor

299Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 137


300Especificación del lenguaje C#

1 miembro de función es aquél que es mejor que todos los demás miembros de función con respecto a la lista
2 de argumentos dada, a condición de que cada miembro de función se compare con todos los demás
3 miembros aplicando las reglas de la §7.4.2.2. Si no hay exactamente un miembro de función mejor que
4 todos los demás, la llamada de un miembro de función es ambigua y se produce un error durante la
5 compilación.
6En las secciones siguientes se define el significado exacto de los términos miembro de función aplicable y
7mejor miembro de función.

87.4.2.1 Miembro de función aplicable


9Se dice que un miembro de función es un miembro de función aplicable con respecto a una lista de argumentos
10A si todas las condiciones siguientes son verdaderas:
11• El número de argumentos de A es idéntico al número de parámetros de la declaración del miembro de
12 función.
13• Para cada argumento de A, el modo de pasar los parámetros del argumento (es decir, value, re f, o out) es
14 idéntico al modo de pasar los parámetros del parámetro correspondiente, y:
15 o para un parámetro de valores o una matriz de parámetros, existe una conversión implícita (§6.1) del tipo
16 del argumento al tipo del parámetro correspondiente, o
17 o para un parámetro ref o out, el tipo del argumento es idéntico al tipo del parámetro correspondiente
18 (después de todo, un parámetro ref o out es un alias para el argumento que se pasa).
19Para un miembro de función que incluye una matriz de parámetros, si el miembro es aplicable por las reglas
20anteriores, se dice que es aplicable en su forma normal. Si un miembro de función que incluye una matriz de
21parámetros no es aplicable en su forma normal, el miembro puede ser aplicable en su forma expandida:
22• La forma expandida se construye mediante el reemplazo de la matriz de parámetros en la declaración del
23 miembro de función con cero o más parámetros de valor del tipo de los elementos de la matriz de
24 parámetros, de forma que el número de argumentos de la lista de argumentos A coincida con el número total
25 de parámetros. Si A tiene menos argumentos que el número de parámetros fijos de la declaración del
26 miembro de función, la forma expandida del miembro de función no puede construirse y, por lo tanto, no es
27 aplicable.
28• Si la clase, estructura o interfaz en la que está declarado el miembro de función ya contiene otro miembro de
29 función aplicable con la misma firma que la forma expandida, ésta no es aplicable.
30• O bien, la forma expandida es aplicable si, para cada argumento de A, el modo de pasar los parámetros del
31 argumento es idéntico al modo de pasar los parámetros del parámetro correspondiente, y
32 o para un parámetro de valores fijos o parámetro de valores creados por la expansión, existe una
33 conversión implícita (§6.1) del tipo del argumento al tipo del parámetro correspondiente, o
34 o para un parámetro ref o out, el tipo del argumento es idéntico al tipo del parámetro correspondiente

357.4.2.2 Mejor miembro de función


36Dada una lista de argumentos A con un conjunto de tipos de argumento { A1, A2, ..., AN } y dos miembros de
37función aplicables MP y MQ con los tipos de parámetro { P1, P2, ..., PN } y { Q1, Q2, ..., QN }, MP se define como
38un mejor miembro de función que MQ si
39• para cada argumento, la conversión implícita de AX a PX no es peor que la conversión implícita de AX a QX, y
40• por lo menos para un argumento, la conversión de AX a PX es mejor que la conversión de AX a QX.

301138 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


302 Capítulo 18 Código no seguro

1Cuando se realiza esta evaluación, si MP o MQ es aplicable en su forma expandida, entonces PX o QX hace


2referencia a un parámetro en la forma expandida de la lista de parámetros.

37.4.2.3 Mejor conversión


4Dada una conversión implícita C1 que convierte de un tipo S a un tipo T1, y una conversión implícita C2 que
5convierte de un tipo S a un tipo T2, la mejor conversión de las dos se determina como sigue:
6• Si T1 y T2 son del mismo tipo, ninguna de ellas es mejor.
7• Si S es T1, C1 es la mejor conversión.
8• Si S es T2, C2 es la mejor conversión.
9• Si existe una conversión implícita de T1 a T2, y no existe una conversión implícita de T2 a T1, C1 es la mejor
10 conversión.
11• Si existe una conversión implícita de T2 a T1, y no existe una conversión implícita de T1 a T2, C2 es la mejor
12 conversión.
13• Si T1 es sbyte y T2 es byte, ushort, uint o ulong, C1 es la mejor conversión.
14• Si T2 es sbyte y T1 es byte, ushort, uint o ulong, C2 es la mejor conversión.
15• Si T1 es short y T2 es ushort, uint o ulong, C1 es la mejor conversión.
16• Si T2 es short y T1 es ushort, uint o ulong, C2 es la mejor conversión.
17• Si T1 es int y T2 es uint o ulong, C1 es la mejor conversión.
18• Si T2 es int y T1 es uint o ulong, C2 es la mejor conversión.
19• Si T1 es long y T2 es ulong, C1 es la mejor conversión.
20• Si T2 es long y T1 es ulong, C2 es la mejor conversión.
21• En otros casos, ninguna de las conversiones es mejor.
22Si una conversión implícita C1 está definida por estas reglas como una conversión mejor que una conversión
23implícita C2, entonces también es cierto que C2 es una conversión peor que C1.

247.4.3 Invocación de miembros de función


25En esta sección se explica el proceso que tiene lugar en tiempo de ejecución para invocar un miembro de
26función concreto. Se supone que un proceso de tiempo de compilación ya ha determinado el miembro concreto
27que se invoca, posiblemente por la aplicación de la resolución de sobrecargas a un conjunto de miembros de
28función candidatos.
29Para la descripción del proceso de invocación, los miembros de función se dividen en dos categorías:
30• Miembros de función estáticos. Son constructores de instancia, métodos estáticos, descriptores de acceso a
31 propiedad estáticos y operadores definidos por el usuario. Los miembros de función estáticos siempre son
32 no virtuales.
33• Miembros de función de instancia. Son métodos de instancia, descriptores de acceso a propiedad de
34 instancia y descriptores de acceso a indizador. Los miembros de función de instancia son virtuales o no
35 virtuales, y siempre se les llama en una instancia concreta. La instancia se calcula mediante una expresión
36 de instancia, y queda accesible dentro del miembro de función como this (§7.5.7).

303Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 139


304Especificación del lenguaje C#

1El procesamiento en tiempo de ejecución de una invocación de miembro de función requiere los pasos
2siguientes, donde M es el miembro de función y, si M es un miembro de instancia, E es la expresión de instancia:
3• Si M es un miembro de función estático:
4 o La lista de argumentos se evalúa según lo descrito en §7.4.1.
5 o Se invoca M.
6• Si M es un miembro de función de instancia declarado en un tipo de valor (value-type):
7 o Se evalúa E. Si esta evaluación da lugar a una excepción, no se ejecutan nuevos pasos.
8 o Si E no está clasificado como variable, se crea una variable local temporal del tipo de E y se asigna el
9 valor de E a la variable. E se reclasifica entonces como referencia a esa variable local temporal. La
10 variable temporal está accesible como this dentro de M, pero no de otra forma. Por lo tanto, solamente
11 si E es una variable verdadera es posible que el proceso llamante observe los cambios que M efectúa en
12 this.
13 o La lista de argumentos se evalúa según lo descrito en §7.4.1.
14 o Se invoca M. La variable a que hace referencia E pasa a ser la variable a que hace referencia this.
15• Si M es un miembro de función de instancia declarado en un tipo de referencia (reference-type):
16 o Se evalúa E. Si esta evaluación da lugar a una excepción, no se ejecutan nuevos pasos.
17 o La lista de argumentos se evalúa según lo descrito en §7.4.1.
18 o Si el tipo de E es un tipo de valor (value-type), se realiza una conversión boxing (§4.3.1) para convertir
19 E al tipo object, y E se considera de tipo object en los pasos siguientes. En este caso, M sólo podría ser
20 un miembro de System.Object.
21 o Se comprueba la validez del valor de E. Si el valor de E es null, se inicia una excepción
22 System.NullReferenceException y no se ejecutan nuevos pasos.

305140 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


306 Capítulo 18 Código no seguro

1 o La implementación del miembro de función que se va a invocar se determina de la siguiente forma:


2 • Si el tipo de tiempo de compilación de E es una interfaz, el miembro de función que hay que invocar
3 es la implementación de M que proporciona el tipo de tiempo de ejecución de la instancia a la que
4 hace referencia E. Este miembro de función se determina aplicando las reglas de asignación de
5 interfaces (§13.4.2) para determinar la implementación de M proporcionada por el tipo en tiempo de
6 ejecución de la instancia a la que hace referencia E.
7 • De lo contrario, si M es un miembro de función virtual, el miembro de función que hay que invocar
8 es la implementación de M que proporciona el tipo de tiempo de ejecución de la instancia a la que
9 hace referencia E. Este miembro de función se determina aplicando las reglas para determinar la
10 implementación más derivada (§10.5.3) de M relativa al tipo en tiempo de ejecución de la instancia
11 a la que hace referencia E.
12 • De lo contrario, M será un miembro de función no virtual y el miembro de función que habrá que
13 invocar será el propio M.
14 o Se invoca la implementación del miembro de función determinada en el paso anterior. El objeto al que
15 hace referencia E se convierte en el objeto al que hace referencia this.

167.4.3.1 Invocaciones en instancias de conversión boxing


17Un miembro de función implementado en un tipo de valor (value-type) puede invocarse mediante una instancia
18de conversión boxing del tipo de valor en las situaciones siguientes:
19• Si el miembro de función es un override de un método heredado del tipo object y se invoca mediante una
20 expresión de instancia de tipo object.
21• Si el miembro de función es una implementación de un miembro de función de interfaz y se invoca
22 mediante una expresión de instancia de un tipo de interfaz (interface-type).
23• Si se invoca el miembro de función a través de un delegado.
24En estas situaciones, se considera que la instancia convertida mediante boxing contiene una variable de tipo de
25valor, y esta variable se convierte en la variable a la que se hace referencia con th i sdentro de la invocación del
26miembro de función. Esto, concretamente, significa que cuando se invoca un miembro de función en una
27instancia convertida mediante boxing, el miembro de función puede modificar el valor contenido en dicha
28instancia.

297.5 Expresiones primarias


30Las expresiones primarias incluyen las formas más simples de las expresiones.
31 primary-expression:
32 primary-no-array-creation-expression
33 array-creation-expression

307Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 141


308Especificación del lenguaje C#

1 primary-no-array-creation-expression:
2 literal
3 simple-name
4 parenthesized-expression
5 member-access
6 invocation-expression
7 element-access
8 this-access
9 base-access
10 post-increment-expression
11 post-decrement-expression
12 object-creation-expression
13 delegate-creation-expression
14 typeof-expression
15 checked-expression
16 unchecked-expression
17Las expresiones primarias se dividen en expresiones de creación de matrices (array-creation-expressions) y
18expresiones primarias no de creación de matrices (primary-no-array-creation-expressions). Si las expresiones de
19creación de matrices se tratan de esta manera, en lugar de enumerarlas junto con todas las formas de expresiones
20simples, se permite que la gramática deshabilite potencialmente códigos confusos como:

21 object o = new int[3][1];


22que, de otra manera, podría interpretarse como

23 object o = (new int[3])[1];


247.5.1 Literales
25Una expresión primaria (primary-expression) compuesta por un literal (literal) (§2.4.4) se clasifica como un
26valor.

277.5.2 Nombres sencillos


28Un nombre simple (simple-name) está formado por un solo identificador.
29 simple-name:
30 identifier
31Un nombre simple (simple-name) se evalúa y clasifica como sigue:
32• Si el nombre simple (simple-name) aparece dentro de un bloque (block) y si el espacio de declaración de
33 variables locales del bloque o de un bloque contenedor (§3.3) contiene una variable local o un parámetro
34 con el nombre dado, el nombre simple hace referencia a dicho parámetro o variable local y se clasifica como
35 una variable.
36• O bien, para cada tipo T, empezando con la declaración de clase, estructura o enumeración envolvente
37 inmediata y continuando con cada declaración de clase o estructura exterior contenedora (si existe), si una
38 búsqueda de miembros de nombre simple (simple-name) en T obtiene una coincidencia:
39 o Si T es el tipo de la clase o estructura envolvente inmediata y la búsqueda identifica uno o más métodos,
40 el resultado es un grupo de métodos con una expresión de instancia asociada de th i s.
41 o Si T es el tipo de clase o estructura envolvente inmediata, si la búsqueda identifica un miembro de
42 instancia, y si la referencia ocurre dentro del bloque (block) de un constructores de instancia, un método

309142 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


310 Capítulo 18 Código no seguro

1 de instancia o un descriptor de acceso a instancia, el resultado es el mismo que un acceso a miembros


2 (§7.5.4) de la forma th i s .E
, donde E es el nombre simple (simple-name).
3 o O bien, el resultado es el mismo que un acceso a miembros (§7.5.4) de la forma T.E, donde E es el
4 nombre simple (simple-name). En este caso, se produce un error durante la compilación si el nombre
5 simple (simple-name) hace referencia a un miembro de instancia.
6• O bien, empezando con el espacio de nombres en el que se produce el nombre simple (simple-name),
7 continuando con cada uno de los espacios de nombres envolventes (si los hay) y terminando con el espacio
8 de nombres global, se evalúan los pasos siguientes hasta que se localiza una entidad:
9 o Si el espacio de nombres contiene un miembro de espacio de nombres con el nombre dado, entonces el
10 nombre simple (simple-name) hace referencia a dicho miembro y, dependiendo del miembro, se
11 clasifica como un espacio de nombres o un tipo.
12 o O bien, si el espacio de nombres tiene una declaración de espacio de nombres correspondiente que
13 abarca la ubicación donde se produce el nombre simple (simple-name), entonces:
14 • Si la declaración del espacio de nombres contiene una directiva alias using (using-alias-directive)
15 que asocia el nombre dado a un espacio de nombres o tipo importado, entonces el nombre simple
16 (simple-name) hace referencia a dicho espacio de nombres o tipo.
17 • O bien, si los espacios de nombres importados por las directivas using de espacio de nombres
18 (using-namespace-directives) de la declaración del espacio de nombres contienen exactamente un
19 único tipo con el nombre dado, entonces el nombre simple (simple-name) hace referencia a dicho
20 tipo.
21 • O bien, si los espacios de nombres importados por las directivas using de espacio de nombres
22 (using-namespace-directives) de la declaración del espacio de nombres contienen más de un tipo
23 con el nombre dado, entonces el nombre simple (simple-name) es ambiguo y se produce un error en
24 tiempo de compilación.
25• De lo contrario, el nombre dado por el nombre simple (simple-name) no está definido y se produce un error
26 en tiempo de compilación.

277.5.2.1 Significado invariable en bloques


28Por cada repetición de un identificador dado como un nombre simple (simple-name) en una expresión o
29declarador, todas las demás repeticiones del mismo identificador como nombre simple (simple-name) en una
30expresión o declarador dentro del bloque (block) (§8.2) o bloque de modificadores (switch-block) (§8.7.2)
31inmediatamente contenedor debe hacer referencia a la misma entidad. Esta regla garantiza que el significado de
32un nombre siempre es el mismo dentro de un bloque.
33En el ejemplo
34 c lass Tes t
35 {
36 doub le x ;
37 vo id F (boo l b) {
38 x = 1 .0 ;
39 i f (b ) {
40 int x;
41 x = 1;
42 }
43 }
44
311Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 143
312Especificación del lenguaje C#

1produce un error durante la compilación porque x hace referencia a varias entidades dentro del bloque externo
2(cuya extensión incluye el bloque anidado de la instrucción i f). Por el contrario, el ejemplo:

3 class Test

4 {

56 void F(bool b) {

7 if (b) {

8 x = 1.0;

9 }

10 else {

11 int x;
16
12 está permitido porque el nombre x nunca se utiliza en el bloque externo.
17Debe tenerse en cuenta que la regla del significado invariable sólo se aplica a los nombres simples. Es
13
18perfectamente válido que el mismo identificador tenga un significado como nombre simple y otro distinto como
19
14operando derecho de un acceso a miembros (§7.5.4). Por ejemplo:
15
20 struct Point

21 {

22
23 public Point(int x, int y) {

24 this.x = x;

25 this.y = y;
28
26El ejemplo anterior ilustra un modelo común de uso de los nombres de campos como nombres de parámetros en
29un constructores de instancia. En el ejemplo, los nombres simples x e y hacen referencia a los parámetros, pero
27ello no impide que expresiones de acceso a miembros como this.x y this.y obtengan acceso a los campos.
30

317.5.3 Expresiones entre paréntesis


32Una expresión entre paréntesis (parenthesized-expression) consiste en una expresión (expression) encerrada
33entre paréntesis.
34 parenthesized-expression:
35 ( expression )
36Una expresión entre paréntesis (parenthesized-expression) se evalúa mediante la evaluación de la expresión
37(expression) contenida entre los paréntesis. Si la expresión (expression) entre paréntesis denota un espacio de
38nombres, un tipo o un grupo de métodos, se produce un error durante la compilación. De lo contrario, el
39resultado de la expresión entre paréntesis (parenthesized-expression) es el resultado de la evaluación de la
40expresión (expression) contenida.

417.5.4 Acceso a miembros


42Un acceso a miembro consiste en una expresión primaria (primary-expression) o un tipo predefinido
43(predefined-type), seguido por un símbolo (token) “.” y, después, por un identificador (identifier).

313144 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


314 Capítulo 18 Código no seguro

1 member-access:
2 primary-expression . identifier
3 predefined-type . identifier
4 predefined-type: uno de
5 boo l byte char dec ima l doub lef l oa t int l ong
6 ob jec t sby te shor t s t r i ngu in t u long ushor t
7Un acceso a miembro (member-access) de la forma E . ,I donde E es una expresión primaria (primary-expression)
8o un tipo predefinido (predefined-type) e I es un identificador (identifier), se evalúa y clasifica como sigue:
9• Si E es un espacio de nombres e I es el nombre de un miembro accesible de dicho espacio de nombres,
10 entonces el resultado es dicho miembro y, dependiendo del miembro, se clasifica como un espacio de
11 nombres o un tipo.
12• Si E es un tipo predefinido (predefined-type) o una expresión primaria (primary-expression) clasificada
13 como un tipo, y una búsqueda de miembros (§7.3) de I en E produce una coincidencia, entonces E.I se
14 evalúa y clasifica como sigue:
15 o Si I identifica un tipo, el resultado es dicho tipo.
16 o Si I identifica uno o varios métodos, entonces el resultado es un grupo de métodos sin una expresión de
17 instancia asociada.
18 o Si I identifica una propiedad static, entonces el resultado es un acceso a propiedad sin una expresión de
19 instancia asociada.
20 o Si I identifica un campo static:
21 • Si el campo es readonly y la referencia ocurre fuera del constructor estático de la clase o estructura
22 donde está declarado el campo, entonces el resultado es un valor, concretamente, el valor del campo
23 estático I de E.
24 • O bien, el resultado es una variable, en concreto, el campo estático I en E.
25 o Si I identifica un evento static:
26 • Si la referencia ocurre dentro de la clase o estructura donde está declarado el evento, y éste se ha
27 declarado sin declaraciones de descriptor de acceso a evento (event-accessor-declarations) (§10.7),
28 entonces E.I se procesa exactamente como si I fuera un campo estático.
29 • O bien, el resultado es un acceso a evento sin una expresión de instancia asociada.
30 o Si I identifica una constante, entonces el resultado es un valor, en concreto el valor de la constante.
31 o Si I identifica un miembro de enumeración, entonces el resultado es un valor, a saber, el valor de dicho
32 miembro de enumeración.
33 o De lo contrario, E.I será una referencia de miembro no válida y se producirá un error en tiempo de
34 compilación.
35• Si E es un acceso a propiedad, un acceso a indizador, una variable o un valor, cuyo tipo es T, y una búsqueda
36 de miembros (§7.3) de I en T produce una coincidencia, entonces E.I se evalúa y clasifica como sigue:
37 o En primer lugar, si E es una propiedad o un acceso de indizador, entonces se obtiene el valor de la
38 propiedad o el acceso a indizador (§7.1.1) y E se reclasifica como un valor.
39 o Si I identifica uno o varios métodos, entonces el resultado es un grupo de métodos con una expresión de
40 instancia asociada de E.

315Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 145


316Especificación del lenguaje C#

1 o Si I identifica una propiedad de instancia, entonces el resultado es un acceso a propiedad con una
2 expresión de instancia asociada de E.
3 o Si T es un tipo de clase (class-type) e I identifica un campo de instancia de dicho tipo de clase:
4 • Si el valor de E es nu l ,l se inicia una excepción System.NullReferenceException.
5 • O bien, si el campo es readonly y la referencia ocurre fuera de un constructores de instancia de la
6 clase donde está declarado el campo, entonces el resultado es un valor, concretamente, el valor del
7 campo I en el objeto al que hace referencia E.
8 • O bien, el resultado es una variable, en concreto, el campo I en el objeto al que hace referencia E.
9 o Si T es un tipo struct (struct-type) e I identifica un campo de instancia de dicho tipo:
10 • Si E es un valor o si el campo es readonly y la referencia ocurre fuera de un constructores de
11 instancia de la estructura donde está declarado el campo, entonces el resultado es un valor,
12 concretamente, el valor del campo I en la instancia de estructura dada por E.
13 • O bien, el resultado es una variable, en concreto, el campo I en la instancia de estructura dada por E.
14 o Si I identifica un evento de instancia:
15 • Si la referencia ocurre dentro de la clase o estructura donde está declarado el evento, y éste se ha
16 declarado sin declaraciones de descriptor de acceso a evento (event-accessor-declarations) (§10.7),
17 entonces E . Ise procesa exactamente como si I fuera un campo de instancia.
18 • O bien, el resultado es un acceso a evento con una expresión de instancia asociada de E.
19• De lo contrario, E.I será una referencia de miembro no válida y se producirá un error en tiempo de
20 compilación.

217.5.4.1 Nombres simples y nombres de tipos idénticos


22En un acceso a miembros de la forma E.I, si E es un solo identificador y el significado de E como nombre simple
23(simple-name) (§7.5.2) es una constante, un campo, una propiedad, una variable local o un parámetro del mismo
24tipo que el significado de E como nombre de tipo (type-name) (§3.8), entonces se permiten los dos posibles
25significados de E. Los dos posibles significados de E.I nunca son ambiguos, puesto que I necesariamente tiene
26que ser un miembro del tipo E en los dos casos. Es decir, la regla sencillamente permite el acceso a los
27miembros estáticos y tipos anidados de E donde, de otra forma, se habría producido un error en tiempo de
28compilación. Por ejemplo:
29 s t ruc t Co lo r
30 {
31 pub l i c s ta t i c readon ly Co lo r Whi te = new Co lo r ( . . . ) ;
32 pub l i c s ta t i c readon ly Co lo r B lack = new Co lo r ( . . . ) ;
33 pub l i c Co lo r Complement ( ) { . . . }
34 }
35 c lass A
36 {

37
38 pub l i c Color Co lo r ;
vo id F ( ) {
/ / F ie ld Co lo r o f t ype Co lo r

39 Co lo r = Color. B l ack ; / / Re fe rences Co lo r .B lack s ta t i c member


40 Co lo r = Co lo r .Comp lement ( ) ; / / I nvokes Complement ( ) on Co lo r f i e l d
41

317146 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


318 Capítulo 18 Código no seguro

1 static void G() {

2 Color c = Color.White; // References Color.White static member

3 }
5Dentro de la clase A, estas repeticiones del identificador Co lo rque hacen referencia al tipo Color están
64subrayadas, y las que hacen referencia al campo Color no.

77.5.5 Expresiones de llamada


8Una expresión de llamada (invocation expression) se utiliza para llamar a un método.
9 invocation-expression:
10 primary-expression ( argument-listopt )
11La expresión primaria (primary-expression) de una expresión de llamada (invocation-expression) debe ser un
12grupo de métodos o un valor de un tipo delegado (delegate-type). Si la expresión primaria (primary-expression)
13es un grupo de métodos, la expresión de llamada (invocation-expression) es una invocación de método
14(§7.5.5.1). Si la expresión primaria (primary-expression) es un valor de un tipo delegado (delegate-type), la
15expresión de llamada (invocation-expression) es una invocación de delegado (§7.5.5.2). Si la expresión primaria
16(primary-expression) no es un grupo de métodos ni un valor de un tipo delegado (delegate-type), se produce un
17error en tiempo de compilación.
18La lista de argumentos (argument-list) opcional (§7.4.1) proporciona valores o referencias de variables para los
19parámetros del método.
20El resultado de evaluar una expresión de llamada (invocation-expression) se clasifica como sigue:
21• Si la expresión de llamada (invocation-expression) invoca un método o un delegado que devuelve vo id, el
22 resultado es nada. Una expresión que se clasifica como nada no puede ser un operando de un operador, y
23 sólo está permitida en el contexto de una expresión de instrucción (statement-expression) (§8.6).
24• En caso contrario, el resultado es un valor del tipo que devuelve el método o el delegado.

257.5.5.1 Invocaciones de método


26Para una invocación de método, la expresión primaria (primary-expression) de la expresión de llamada
27(invocation-expression) debe ser un grupo de métodos. El grupo de métodos identifica el método que se invoca
28o el conjunto de métodos sobrecargados entre los cuales se elige un método concreto para invocar. En el último
29caso, la determinación del método concreto que se invoca se basa en el contexto suministrado por los tipos de
30los argumentos de lista de argumentos (argument-list).
31El procesamiento en tiempo de compilación de una invocación de método de la forma M(A), donde M es un
32grupo de métodos y A es una lista de argumentos (argument-list) opcional, se compone de los siguientes pasos:
33• Se construye el conjunto de métodos candidatos para la invocación del método. Empezando con el conjunto
34 de métodos asociados con M, encontrados por una búsqueda de miembros anterior (§7.3), el conjunto se
35 reduce a aquellos métodos que son aplicables con respecto a la lista de argumentos A. La reducción del
36 conjunto consiste en aplicar las siguientes reglas a cada método T.N del conjunto, donde T es el tipo en el
37 cual se declara el método N:
38 o Si N no es aplicable con respecto a A (§7.4.2.1), entonces N se quita del conjunto.
39 o Si N es aplicable con respecto a A (§7.4.2.1), entonces todos los métodos declarados en un tipo base de
40 T se quitan del conjunto.
41• Si el conjunto resultante de métodos candidatos está vacío, entonces no existen métodos aplicables, y se
42 produce un error durante la compilación.

319Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 147


320Especificación del lenguaje C#

1• El mejor método del conjunto de métodos candidatos se identifica mediante las reglas de resolución de
2 sobrecargas de §7.4.2. Si no puede identificarse un método individual mejor, la invocación del método es
3 ambigua, y se produce un error durante la compilación.
4• Dado un método mejor, la invocación del método se valida en el contexto del grupo de métodos: si el
5 método mejor es un método estático, el grupo de métodos debe haberse obtenido de un nombre simple
6 (simple-name) o de un acceso a miembro (member-access) mediante un tipo. Si el método mejor es un
7 método de instancia, el grupo de métodos debe haberse obtenido de un nombre simple (simple-name), de un
8 acceso a miembro (member-access) a través de una variable o un valor, o de un acceso a base (base-access).
9 Si ninguna de estas dos condiciones es verdadera, se producirá un error de compilación.
10Una vez seleccionado un método y validado en tiempo de compilación por los pasos anteriores, se procesa la
11invocación real en tiempo de ejecución conforme a las reglas de invocación de un miembro de función
12explicadas en §7.4.3.
13El efecto intuitivo de las reglas de resolución explicadas es como sigue: para localizar el método concreto
14invocado por una invocación de método, se empieza con el tipo indicado por la invocación del método y se
15continúa en la cadena de la herencia hasta encontrar por lo menos una declaración de método aplicable,
16accesible y no de reemplazo. Después, se lleva a cabo la resolución de sobrecargas en el conjunto de métodos
17aplicables, accesibles no de reemplazo declarados en dicho tipo y se invoca el método seleccionado por este
18procedimiento.

197.5.5.2 Invocaciones de delegados


20Para una invocación de delegado, la expresión primaria (primary-expression) de la expresión de llamada
21(invocation-expression) debe ser un valor de un tipo delegado (delegate-type). Además, considerando que el tipo
22delegado (delegate-type) debe ser un miembro de función con la misma lista de parámetros que el tipo delegado
23(delegate-type), éste debe ser aplicable (§7.4.2.1) con respecto a la lista de argumentos (argument-list) de
24expresión de llamada (invocation-expression).
25El procesamiento en tiempo de ejecución de una invocación de delegado con la estructura D(A), donde D es una
26expresión primaria (primary-expression) de un tipo delegado y A es una lista de argumentos (argument-list)
27opcional, se compone de los siguientes pasos:
28• Se evalúa D. Si esta evaluación da lugar a una excepción, no se ejecutan nuevos pasos.
29• Se comprueba la validez del valor de D. Si el valor de D es null, se inicia una excepción
30 System.NullReferenceException y no se ejecutan nuevos pasos.
31• En otro caso, D es una referencia a una instancia de delegado. Las llamadas a miembros de función (§7.4.3)
32 se realizan en cada una de las entidades a las que se puede llamar de la lista de llamadas del delegado. En el
33 caso de las entidades a las que se puede llamar y que constan de una instancia y de un método de instancia,
34 la instancia para la invocación es la contenida en la entidad a la que se puede llamar.

357.5.6 Acceso a elementos:


36Un acceso a elementos (element-access) consiste en una expresión primaria sin creación de matrices (primary-
37no-array-creation-expression), seguida de un símbolo “[”, una lista de expresiones (expression-list) y un
38símbolo “]”. Una lista de expresiones (expression-list) consta de una o más expresiones (expressions), separadas
39por comas.
40 element-access:
41 primary-no-array-creation-expression [ expression-list ]

321148 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


322 Capítulo 18 Código no seguro

1 expression-list:
2 expression
3 expression-list , expression
4Si la expresión primaria sin creación de matrices (primary-no-array-creation-expression) de un acceso a
5elementos (element-access) es un valor de tipo matricial (array-type), el acceso a elementos (element-access) es
6un acceso a matriz (§7.5.6.1). De lo contrario, la expresión primaria sin creación de matrices (primary-no-array-
7creation-expression) debe ser una variable o un valor de un tipo de clase, estructura o interfaz que tiene uno o
8más miembros indizadores, en cuyo caso el acceso a elementos (element-access) es un acceso a indizador
9(§7.5.6.2).

107.5.6.1 Acceso a matrices


11Para un acceso a matrices, la expresión primaria sin creación de matrices (primary-no-array-creation-
12expression) del acceso a elementos (element-access) debe ser un valor de tipo matricial (array-type). El número
13de expresiones de la lista de expresiones (expression-list) debe ser el mismo que el rango del tipo matricial
14(array-type), y cada expresión debe ser de tipo i n t, u in t, long, ulong o de un tipo que pueda convertirse de
15forma implícita a uno o más de estos tipos.
16El resultado de la evaluación de un acceso a matriz es una variable del tipo de elementos de la matriz, es decir,
17el elemento de matriz seleccionado por los valores de las expresiones de la lista de expresiones (expression-list).
18El procesamiento en tiempo de ejecución de un acceso a matriz con la estructura P[A] donde P es una expresión
19primaria sin creación de matrices (primary-no-array-creation-expression) de un tipo matricial (array-type) y A
20es una lista de expresiones (expression-list) se compone de los siguientes pasos:
21• Se evalúa P. Si esta evaluación da lugar a una excepción, no se ejecutan nuevos pasos.
22• Las expresiones de índice de la lista de expresiones (expression-list) se evalúan por orden, de izquierda a
23 derecha. Después de la evaluación de cada expresión de índice, se realiza una conversión implícita (§6.1) a
24 uno de los tipos siguientes: int, uint, long, ulong. Se elige el primer tipo de esta lista para el cual existe una
25 conversión implícita. Por ejemplo, si la expresión de índice es de tipo short, se lleva a cabo una conversión
26 implícita a int, puesto que son posibles conversiones implícitas de short a int y de short a long. Si la
27 evaluación de una expresión de índice o de la conversión implícita posterior causa una excepción, no se
28 evalúan otras expresiones de índice y no se ejecutan nuevos pasos.
29• Se comprueba la validez del valor de P. Si el valor de P es null, se inicia una excepción
30 System.NullReferenceException y no se ejecutan nuevos pasos.
31• Se compara otra vez el valor de cada expresión de lista de expresiones (expression-list) con los límites reales
32 de cada dimensión de la instancia matricial a que hace referencia P. Si uno o más valores no están
33 comprendidos en el intervalo, se inicia una excepción System.IndexOutOfRangeException y no se
34 ejecutan nuevos pasos.
35• Se calcula la ubicación del elemento de matriz dada por la expresión o expresiones de índice, que pasa a ser
36 el resultado del acceso a matriz.

377.5.6.2 Acceso al indizador


38Para un acceso a indizador, la expresión primaria sin creación de matrices (primary-no-array-creation-
39expression) del acceso a elementos (element-access) debe ser una variable o un valor de un tipo de clase,
40estructura o interfaz, y este tipo debe implementar uno o más indizadores que sean aplicables con respecto a la
41lista de expresiones (expression-list) del acceso a elementos (element-access).

323Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 149


324Especificación del lenguaje C#

1El procesamiento en tiempo de compilación de un acceso a indizador de la forma P[A ], donde P es una
2expresión primaria sin creación de matrices (primary-no-array-creation-expression) de un tipo T de clase,
3estructura o interfaz, y A es una lista de expresiones (expression-list), se compone de los siguientes pasos:
4• Se construye el conjunto de indizadores suministrados por T. El conjunto está formado por todos los
5 indizadores declarados en T o un tipo base de T que no son declaraciones override y están accesibles en el
6 contexto actual (§3.5).
7• El conjunto se reduce a aquellos indizadores que sean aplicables y no estén ocultos por otros indizadores.
8 Las reglas siguientes se aplican a todos los indizadores S.I del conjunto, donde S es el tipo en el que se
9 declara el indizador I:
10 o Si I no es aplicable con respecto a A (§7.4.2.1), entonces I se quita del conjunto.
11 o Si I es aplicable con respecto a A (§7.4.2.1), entonces todos los indizadores declarados en un tipo base
12 de S se quitan del conjunto.
13• Si el conjunto resultante de indizadores candidatos está vacío, entonces no existen indizadores aplicables, y
14 se produce un error durante la compilación.
15• El mejor indizador del conjunto de indizadores candidatos se identifica mediante las reglas de resolución de
16 sobrecargas de §7.4.2. Si no puede identificarse un indizador individual mejor, el acceso a indizador es
17 ambiguo y se produce un error durante la compilación.
18• Las expresiones de índice de la lista de expresiones (expression-list) se evalúan por orden, de izquierda a
19 derecha. El resultado de procesar el acceso a indizador es una expresión clasificada como un acceso a
20 indizador. La expresión de acceso a indizador hace referencia al indizador determinado en el paso anterior, y
21 tiene una expresión de instancia asociada de P y una lista de argumentos asociada de A.
22Dependiendo del contexto en que se utilice, un acceso a indizador provoca la invocación del descriptor de
23acceso get (get-accessor) o del descriptor de acceso set (set-accessor) del indizador. Si el acceso a indizador es
24el destino de una asignación, se invoca el descriptor de acceso set (set-accessor) para asignar un nuevo valor
25(§7.13.1). En todos los demás casos, se invoca el descriptor de acceso a get (get-accessor) para obtener el valor
26actual (§7.1.1).

277.5.7 Acceso a this


28Un acceso a this (this-access) consta de la palabra reservada this.
29 this-access:
30 this
31El acceso a this (this-access) sólo se permite en el bloque (block) de un constructores de instancia, un método de
32instancia o un descriptor de acceso a una instancia. Tiene uno de los siguientes significados:
33• Cuando se utiliza this en una expresión primaria (primary-expression) dentro de un constructor de instancia
34 de una clase, se clasifica como un valor. El tipo del valor es la clase dentro de la cual ocurre la utilización y
35 el valor es una referencia al objeto que se construye.
36• Cuando se utiliza this en una expresión primaria (primary-expression) dentro de un método de instancia o
37 de un descriptor de acceso a instancia de una clase, se clasifica como un valor. El tipo del valor es la clase
38 dentro de la cual ocurre la utilización y el valor es una referencia al objeto para el cual se invoca el método o
39 el descriptor de acceso.
40• Cuando se utiliza this en una expresión primaria (primary-expression) dentro de un constructores de
41 instancia de una estructura, se clasifica como una variable. El tipo de la variable es la estructura dentro de la
42 cual ocurre la utilización y la variable representa la estructura que se construye. La variable this de un

325150 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


326 Capítulo 18 Código no seguro

1 constructores de instancia de una estructura se comporta exactamente igual que un parámetro out del tipo
2 struct; esto significa concretamente que la variable debe estar asignada definitivamente en todas las rutas de
3 ejecución del constructores de instancia.
4• Cuando se utiliza th i sen una expresión primaria (primary-expression) dentro de un método de instancia o
5 un descriptor de acceso a instancia de una estructura, se clasifica como una variable. El tipo de la variable es
6 la estructura dentro de la cual ocurre la utilización y la variable representa la estructura para la cual se
7 invoca el método o el descriptor de acceso. La variable th i sde un método de instancia de una estructura se
8 comporta exactamente igual que un parámetro ref del tipo struct.
9El uso de this en una expresión primaria (primary-expression) de un contexto distinto de los mencionados
10anteriormente, produce un error en tiempo de compilación. En particular, no es posible hacer referencia a this en
11un método estático, en un descriptor de acceso a una propiedad estático o en un inicializador de variable
12(variable-initializer) de una declaración de campo.

137.5.8 Acceso a bases


14Un acceso a base (base-access) está formado por la palabra reservada base seguida de un símbolo (token) “.” y
15un identificador, o de una lista de expresiones (expression-list) encerrada entre corchetes:
16 base-access:
17 base . identifier
18 base [ expression-list ]
19Un acceso a base (base-access) permite obtener acceso a miembros de clase base que están ocultos por
20miembros de nombres similares en la clase o estructura actuales. El acceso a base (base-access) sólo se permite
21en el bloque (block) de un constructores de instancia, un método de instancia o un descriptor de acceso a una
22instancia. Si base.I ocurre en una clase o una estructura, I debe denotar un miembro de la clase base de dicha
23clase o estructura. De igual manera, si base[E] ocurre en una clase, debe existir un indizador aplicable en la
24clase base.
25En tiempo de compilación, las expresiones de acceso a base (base-access) con la estructura base.I y base[E] se
26evalúan exactamente igual que si se escribieran ((B)this).I y ((B)this)[E], donde B es la clase base de la clase o
27estructura en que tiene lugar la construcción. Por lo tanto, base.I y base[E] corresponden a this.I y this[E],
28con la excepción de que this se considera como una instancia de la clase base.
29Cuando un acceso a base (base-access) hace referencia a un miembro de función virtual (un método, una
30propiedad o un indizador), cambia la función que se debe invocar en tiempo de ejecución (§7.4.3). El miembro
31de función que se invoca se determina buscando la implementación más derivada (§10.5.3) del miembro de
32función con respecto a B (en lugar de la relativa al tipo de tiempo de ejecución de this, como sería normal en un
33acceso distinto del acceso a base). Por lo tanto, dentro de un override de un miembro de función virtual, un
34acceso a base (base-access) puede utilizarse para llamar a la implementación heredada del miembro de función.
35Si el miembro de función al que hace referencia un acceso a base (base-access) es abstracto, se producirá un
36error en tiempo de compilación.

377.5.9 Operadores postfijos de incremento y decremento


38 post-increment-expression:
39 primary-expression ++
40 post-decrement-expression:
41 primary-expression --
42El operando de una operación de sufijo de incremento o decremento debe ser una expresión clasificada como
43una variable, un acceso a propiedad o un acceso a indizador. El resultado de la operación es un valor del mismo
44tipo que el operando.

327Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 151


328Especificación del lenguaje C#

1Si el operando de una operación de postfijo de incremento o decremento es una propiedad o un acceso a
2indizador, la propiedad o el indizador debe tener tanto un descriptor de acceso get como se t. Si no es éste el
3caso, se produce un error en tiempo de compilación.
4La resolución de sobrecargas de operadores unarios (§7.2.3) se aplica para seleccionar una implementación de
5operador concreta. Existen operadores predefinidos ++ y - -para los tipos siguientes: sbyte, byte, short,
6ushort, int, uint, long, ulong, char, float, double, decimal y cualquier tipo enum. Los operadores
7predefinidos ++ devuelven el valor generado al sumar 1 al operando y los operadores predefinidos -- devuelven
8el valor generado al restarle 1. En un contexto checked, si el resultado de esta suma o resta se encuentra fuera
9del intervalo del tipo del resultado y el tipo del resultado es un tipo integral o enum, se inicia una excepción
10System.OverflowException.
11El procesamiento en tiempo de ejecución de una operación de postfijo de incremento o decremento de la forma
12x++ o x - -consta de los pasos siguientes:
13• Si x está clasificada como una variable:
14 o Se evalúa x para producir la variable.
15 o Se guarda el valor de x.
16 o Se invoca el operador seleccionado con el valor guardado de x como argumento.
17 o El valor devuelto por el operador se almacena en la ubicación dada por la evaluación de x.
18 o El valor guardado de x es el resultado de la operación.
19• Si x se clasifica como una propiedad o un acceso a indizador:
20 o Se evalúa la expresión de instancia (si x no es s ta t i)cy la lista de argumentos (si x es un acceso a
21 indizador) asociada con x, y el resultado se utiliza en las posteriores invocaciones de descriptor de
22 acceso get y set.
23 o Se invoca el descriptor de acceso get de x y se guarda el valor devuelto.
24 o Se invoca el operador seleccionado con el valor guardado de x como argumento.
25 o Se invoca el descriptor de acceso set de x con el valor devuelto por el operador como su argumento
26 value.
27 o El valor guardado de x es el resultado de la operación.
28Los operadores ++ y -- admiten la notación de prefijos (§7.6.5). El resultado de x++ o x-- es el valor de x
29antes de la operación, mientras que el resultado de ++x o --x es el valor de x después de la operación. En uno u
30otro caso, x tiene el mismo valor después de la operación.
31Una implementación de operator ++ u operator -- puede invocarse mediante la notación de postfijo o prefijo.
32No es posible contar con implementaciones de operador independientes para las dos notaciones.

337.5.10 El operador new


34El operador new se utiliza para crear nuevas instancias de tipos.
35Existen tres formas de expresiones new:
36• Las expresiones de creación de objetos se utilizan para crear nuevas instancias de tipos de clase y tipos de
37 valor.
38• Expresiones de creación de matrices, que se utilizan para crear nuevas instancias de tipos de matriz.
39• Expresiones de creación de delegados, que se utilizan para crear nuevas instancias de tipos delegados.

329152 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


330 Capítulo 18 Código no seguro

1El operador new implica la creación de una instancia de un tipo, pero no necesariamente la asignación dinámica
2de memoria. En concreto, las instancias de tipos de valor no requieren más memoria que las variables en las que
3residen, por lo que no se llevan a cabo asignaciones dinámicas cuando se usa new para crear instancias de tipos
4de valor.

57.5.10.1 Expresiones de creación de objetos


6Una expresión de creación de objetos (object-creation-expression) se utiliza para crear una nueva instancia de
7un tipo de clase (class-type) o un tipo de valor (value-type).
8 object-creation-expression:
9 new type ( argument-listopt )
10El tipo (type) de una expresión de creación de objetos (object-creation-expression) debe ser un tipo de clase
11(class-type) o un tipo de valor (value-type). El tipo (type) no puede ser un tipo de clase (class-type) abstract.
12La lista de argumentos (argument-list) opcional (§7.4.1) sólo está permitida si el tipo (type) es un tipo de clase
13(class-type) o un tipo struct (struct-type).
14El procesamiento en tiempo de compilación de una expresión de creación de objetos (object-creation-
15expression) de la forma new T(A), donde T es un tipo de clase (class-type) o un tipo de valor (value-type) y A es
16una lista de argumentos (argument-list) opcional, se compone de los siguientes pasos:
17• Si T es un tipo de valor (value-type) y A no está presente:
18 o La expresión de creación de objetos (object-creation-expression) es una invocación de constructor
19 predeterminado. El resultado de la expresión de creación de objetos (object-creation-expression) es un
20 valor de tipo T, en concreto, el valor predeterminado de T según se define en §4.1.1.
21• O bien, si T es un tipo de clase (class-type) o un tipo struct (struct-type):
22 o Si T es un tipo de clase (class-type) abstract, se produce un error en tiempo de compilación.
23 o El constructores de instancia que se invoca se determina con las reglas de resolución de sobrecargas de
24 §7.4.2. El conjunto de constructores de instancia candidatos está formado por todos los constructores de
25 instancia accesibles declarados en T, que son aplicables con respecto a A (§7.4.2.1). Si el conjunto de
26 constructores de instancia candidatos está vacío o no es posible identificar un solo constructores de
27 instancia idóneo, se produce un error en tiempo de compilación.
28 o El resultado de la expresión de creación de objetos (object-creation-expression) es un valor de tipo T, en
29 concreto, el valor generado al invocar el constructores de instancia determinado en el paso anterior.
30• De lo contrario, la expresión de creación de objetos (object-creation-expression) no es válida y se produce
31 un error durante la compilación.
32El procesamiento en tiempo de compilación de una expresión de creación de objetos (object-creation-
33expression) con la estructura new T(A), donde T es un tipo de clase (class-type) o un tipo struct (struct-type) y A
34es una lista de argumentos (argument-list) opcional, se compone de los siguientes pasos:
35• Si T es un tipo de clase (class-type):
36 o Se asigna una nueva instancia de la clase T. Si no hay memoria disponible suficiente para asignar la
37 nueva instancia, se inicia una excepción Sys tem.OutO fMemoryExcept i on y no se ejecutan más
38 pasos.
39 o Todos los campos de la nueva instancia se inicializan con sus valores predeterminados (§5.2).

331Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 153


332Especificación del lenguaje C#

1 o El constructores de instancia se invoca conforme a las reglas de invocación de un miembro de función


2 (§7.4.3). Se pasa automáticamente al constructores de instancia una referencia a la instancia recién
3 asignada y se posibilita el acceso a dicha instancia como th i sdesde el constructor.
4• Si T es un tipo struct (struct-type):
5 o Se crea una instancia de tipo T mediante la asignación de una variable local temporal. Dado que se
6 requiere un constructores de instancia de un tipo struct (struct-type) para asignar definitivamente un
7 valor a cada campo de la instancia que se crea, no es necesaria una inicialización de la variable
8 temporal.
9 o El constructores de instancia se invoca conforme a las reglas de invocación de un miembro de función
10 (§7.4.3). Se pasa automáticamente al constructores de instancia una referencia a la instancia recién
11 asignada y se posibilita el acceso a dicha instancia como th i sdesde el constructor.

333154 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


334 Capítulo 18 Código no seguro

17.5.10.2 Expresiones de creación de matrices


2Para crear una nueva instancia de un tipo matricial (array-type), se utiliza una expresión de creación de matrices
3(array-creation-expression).
4 array-creation-expression:
5 new non-array-type [ expression-list ] rank-specifiersopt array-initializeropt
6 new array-type array-initializer
7Una expresión de creación de matrices de la primera forma asigna una instancia de matriz del tipo que se
8obtiene al eliminar cada una de las expresiones individuales de la lista de expresiones. Por ejemplo, la expresión
9de creación de matrices new int[10, 20] produce una instancia de matriz de tipo int[,], y la expresión new
10int[10][,] produce una matriz de tipo int[][,]. Cada expresión de la lista de expresiones debe ser de tipo int,
11uint, long o ulong, o bien de un tipo que pueda convertirse implícitamente a uno o más de estos tipos. El valor
12de cada expresión determina la longitud de la dimensión correspondiente en la instancia de matriz recién
13asignada. Dado que la longitud de la dimensión de matriz no puede ser negativa, si existe una expresión
14constante (constant-expression) con un valor negativo en la lista de expresiones, se producirá un error en tiempo
15de compilación.
16Excepto en un contexto no seguro (§18.1), la forma de las matrices no se especifica.
17Si una expresión de creación de matriz de la primera forma incluye un inicializador de matriz, cada expresión de
18la lista de expresiones debe ser una constante, y las longitudes de rango y dimensión especificadas por la lista de
19expresiones deben coincidir con las del inicializador.
20En una expresión de creación de matrices de la segunda forma, el rango del tipo de matriz especificado debe
21coincidir con el del inicializador. Las longitudes de las dimensiones individuales se infieren del número de
22elementos en cada uno de los niveles de anidamiento correspondientes del inicializador de matriz. Por lo tanto,
23la expresión
24 new i n t [ , ] {{0 , 1} , {2 , 3} , {4 , 5}}
25se corresponde exactamente con
26 new i n t [3 , 2] {{0 , 1} , {2 , 3} , {4 , 5}}
27Los inicializadores de matrices se describen más detalladamente en §12.6.
28El resultado de evaluar una expresión de creación de matrices se clasifica como un valor, concretamente como
29una referencia a la instancia de matriz recién asignada. El procesamiento en tiempo de ejecución de una
30expresión de creación de matrices consta de los siguientes pasos:
31• Las expresiones de longitud de dimensión de la lista de expresiones (expression-list) se evalúan por orden,
32 de izquierda a derecha. Después de la evaluación de cada expresión, se realiza una conversión implícita
33 (§6.1) a uno de los tipos siguientes: int, uint, long, ulong. Se elige el primer tipo de esta lista para el cual
34 existe una conversión implícita. Si la evaluación de una expresión o de la conversión implícita posterior
35 causa una excepción, no se evalúan otras expresiones y no se ejecutan nuevos pasos.
36• Los valores calculados para las longitudes de dimensión se validan como sigue. Si uno o más valores son
37 menores que cero, se inicia una excepción Sys tem.Over f l owExcept i ony no se ejecutan nuevos pasos.
38• Se asigna una instancia de matriz con las longitudes de dimensión dadas. Si no hay memoria disponible
39 suficiente para asignar la nueva instancia, se inicia una excepción Sys tem.OutO fMemoryExcept i on y no
40 se ejecutan más pasos.
41• Todos los elementos de la nueva instancia de matriz se inicializan con sus valores predeterminados (§5.2).

335Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 155


336Especificación del lenguaje C#

1• Si la expresión de creación de matriz contiene un inicializador de matriz, entonces se evalúan las


2 expresiones del inicializador y se asignan a sus elemento de matriz correspondientes. Las evaluaciones y
3 asignaciones se llevan a cabo en el orden en que están escritas las expresiones en el inicializador de matriz,
4 es decir, los elementos se inicializan en orden de índice creciente, empezando por la dimensión situada más
5 a la derecha. Si la evaluación de una expresión dada o la posterior asignación al elemento de matriz
6 correspondiente causa una excepción, no se inicializan más elementos (y los elementos restantes conservan
7 sus valores predeterminados).
8Una expresión de creación de matriz permite crear una instancia de una matriz con elementos de un tipo de
9matriz, pero los elementos de una matriz como ésta deben inicializarse de forma manual. Por ejemplo, la
10instrucción

11 int[][] a = new int[100][];


12crea una matriz de una dimensión con 100 elementos de tipo i n t [. ]El valor inicial de cada elemento es nu l .l Una
13misma expresión de creación de matrices no puede crear también instancias de las submatrices, y la instrucción

14 int[][] a = new int[100][5]; // Error


15da como resultado un error en tiempo de compilación. La creación de instancias de las submatrices debe
16realizarse de forma manual, como en

17 int[][] a = new int[100][];

18Cuando for
19 (int de
una matriz i matrices
= 0; i tiene
< 100;
formai++) a[i] = es
“rectangular”, new int[5];
decir, cuando todas las submatrices tienen la
20misma longitud, es más eficaz utilizar una matriz de varias dimensiones. En el ejemplo anterior, la creación de
21instancias de la matriz de matrices crea 101 objetos, una matriz externa y 100 submatrices. Por contra,

22 int[,] = new int[100, 5];


23sólo crea un objeto, una matriz de dos dimensiones, y realiza la asignación mediante una sola instrucción.

247.5.10.3 Expresiones de creación de delegados


25Una expresión de creación de delegados (delegate-creation-expression) se usa para crear una nueva instancia de
26un tipo delegado (delegate-type).
27 delegate-creation-expression:
28 new delegate-type ( expression )
29El argumento de una expresión de creación de delegados debe ser un grupo de métodos (§7.1) o un valor de un
30tipo delegado (delegate-type). Si el argumento es un conjunto de métodos, identifica el método y, en el caso de
31un método de instancia, el objeto para el que se crea un delegado. Si el argumento es un valor de un tipo
32delegado (delegate-type), identifica una instancia de delegado que se va a copiar.
33El procesamiento en tiempo de compilación de una expresión de creación de delegados (delegate-creation-
34expression) con la estructura new D(E), donde D es un tipo delegado (delegate-type) y E es una expresión
35(expression), se compone de los siguientes pasos:
36• Si E es un grupo de métodos:
37 o El conjunto de métodos identificado por E debe incluir exactamente un método que sea compatible
38 (§15.1) con D, y este método es aquél al que hace referencia el delegado recién creado. Si no existe un
39 método coincidente, o existe más de uno, se producirá un error en tiempo de compilación. Si el método
40 seleccionado es un método de instancia, la expresión de instancia asociada con E determina el objeto de
41 destino del delegado.

337156 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


338 Capítulo 18 Código no seguro

1 o Como en una invocación de método, el método seleccionado debe ser compatible con el contexto del
2 grupo de métodos: si el método es estático, el grupo de métodos debe haberse obtenido de un nombre
3 simple (simple-name) o de un acceso a miembro (member-access) mediante un tipo. Si el método es de
4 instancia, el grupo de métodos debe haberse obtenido de un nombre simple (simple-name) o de un
5 acceso a miembro (member-access) a través de una variable o un valor. Si el método seleccionado no es
6 válido para el contexto del grupo de métodos, se produce un error durante la compilación.
7 o El resultado es un valor de tipo D, es decir, un delegado recién creado que hace referencia al método
8 seleccionado y al objeto de destino.
9• O bien, si E es un valor de tipo delegado (delegate-type):
10 o D y E deben ser compatibles (§15.1) o, de lo contrario, se produce un error durante la compilación.
11 o El resultado es un valor de tipo D, concretamente, un delegado recién creado que hace referencia a la
12 misma lista de invocaciones que E.
13• De lo contrario, la expresión de creación de delegados no es válida y se produce un error durante la
14 compilación.
15El procesamiento en tiempo de ejecución de una expresión de creación de delegados (delegate-creation-
16expression) con la estructura new D(E), donde D es un tipo delegado (delegate-type) y E es una expresión
17(expression), se compone de los siguientes pasos:
18• Si E es un grupo de métodos:
19 o Si el método seleccionado en tiempo de compilación es un método estático, el objeto de destino del
20 delegado es nu l .l O bien, el método seleccionado es un método de instancia, y el objeto de destino del
21 delegado se determina a partir de la expresión de instancia asociada con E:
22 • Se evalúa la expresión de instancia. Si esta evaluación da lugar a una excepción, no se ejecutan
23 nuevos pasos.
24 • Si la expresión de instancia es de un tipo de referencia (reference-type), el valor calculado por la
25 expresión de instancia es el objeto de destino. Si el objeto de destino es null, se inicia una excepción
26 System.NullReferenceException y no se ejecutan más pasos.
27 • Si la expresión de instancia es de un tipo de valor (value-type), se realiza una operación boxing
28 (§4.3.1) para convertir el valor en un objeto, que pasa a ser el objeto de destino.
29 o Se asigna una nueva instancia del tipo delegado D. Si no hay memoria disponible suficiente para asignar
30 la nueva instancia, se inicia una excepción Sys tem.OutO fMemoryExcept i on y no se ejecutan más
31 pasos.
32 o Se inicializa la nueva instancia de delegado con una referencia al método que se determinó en tiempo de
33 compilación y una referencia al objeto de destino antes calculado.
34• Si E es un valor de un tipo delegado (delegate-type):
35 o Se evalúa E. Si esta evaluación da lugar a una excepción, no se ejecutan nuevos pasos.
36 o Si el valor de E es null, se inicia una excepción System.NullReferenceException y no se ejecutan
37 nuevos pasos.
38 o Se asigna una nueva instancia del tipo delegado D. Si no hay memoria disponible suficiente para asignar
39 la nueva instancia, se inicia una excepción Sys tem.OutO fMemoryExcept i on y no se ejecutan más
40 pasos.

339Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 157


340Especificación del lenguaje C#

1 o Se inicializa la nueva instancia de delegado con la misma lista de invocaciones que la instancia de
2 delegado proporcionada por E.
3La lista de invocaciones de un delegado se determina cuando se inicializa el delegado y permanece constante
4durante toda la existencia del delegado. Dicho de otra forma: no se podrán cambiar las entidades de destino de
5un delegado capaces de recibir llamadas una vez haya sido creado el delegado. Si se combinan dos delegados o
6se quita uno de otro (§15.1), se produce un nuevo delegado; no se cambia el contenido de ningún delegado
7existente.
8No es posible crear un delegado que haga referencia a una propiedad, un indizador, un operador definido por el
9usuario, un constructores de instancia, un destructor o un constructor estático.
10Como se ha explicado antes, cuando se crea un delegado a partir de un grupo de métodos, la lista de parámetros
11formales y el tipo de valor devuelto del delegado determinan cuál de los métodos sobrecargados se debe
12seleccionar. En el siguiente ejemplo:

13 delegate double DoubleFunc(double x);


14 class A

15 {

16
17 static float Square(float x) {

18 return x * x;

19
20 static double Square(double x) {

21 return x * x;

22 }
24el campo A. fse inicializa con un delegado que hace referencia al segundo método Square debido a que ese
25
23método coincide exactamente con la lista de parámetros formales y el tipo de valor devuelto de DoubleFunc. Si
26no hubiera existido el segundo método Square, se habría producido un error de compilación.

277.5.11 Operador typeof


28El operador typeof se utiliza con el fin de obtener el objeto System.Type para un tipo.
29 typeof-expression:
30 t ypeo f ( type )
31 typeof ( void )
32La primera forma de expresión typeof (typeof-expression) consta de una palabra clave typeof seguida de un tipo
33(type) entre paréntesis. El resultado de una expresión de esta forma es el objeto System.Type del tipo indicado.
34Sólo existe un objeto System.Type para cualquier tipo dado. Lo que implica que, para el tipo T, typeof(T) ==
35typeof(T) siempre será true.
36La segunda forma de la expresión typeof (typeof-expression) consta de una palabra clave typeof seguida de una
37palabra clave void entre paréntesis. El resultado de una expresión con esta estructura es el objeto System.Type
38que representa la ausencia de un tipo. El objeto del tipo devuelto por typeof(void) es exclusivo del objeto del
39tipo devuelto por cualquier tipo. Este objeto de tipo especial es útil en las bibliotecas de clases que permiten la
40reflexión en métodos del lenguaje, donde dichos métodos desean tener un medio para representar el tipo de valor
41devuelto de cualquier método, incluidos los métodos void, con una instancia de System.Type.

341158 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


342 Capítulo 18 Código no seguro

1En el ejemplo

2 using System;
3 class Test

4 {

5 static void Main() {

6 Type[] t = {

7 typeof(int),

8 typeof(System.Int32),

9 typeof(string),

10 typeof(double[]),

11 typeof(void)
18produce el resultado:
12
19
13 System.Int32

20
14 System.Int32

21
15 System.String
24
22
16Debe tenerse en cuenta que i n ty Sys tem. In t32son el mismo tipo.
23
177.5.12 Los operadores checked y unchecked
25
26Los operadores checked y unchecked se utilizan con el fin de controlar el contexto de comprobación de
27desbordamiento para conversiones y operaciones aritméticas de tipo integral.
28 checked-expression:
29 checked ( expression )
30 unchecked-expression:
31 unchecked ( expression )
32El operador checked evalúa la expresión contenida en un contexto comprobado (checked), y el operador
33unchecked evalúa la expresión contenida en un contexto no comprobado (unchecked). Una expresión checked
34(checked-expression) o una expresión unchecked (unchecked-expression) se corresponde exactamente con una
35expresión entre paréntesis (parenthesized-expression) (§7.5.3), excepto porque la expresión contenida se evalúa
36en el contexto de comprobación de desbordamiento dado.
37El contexto de comprobación de desbordamiento también puede controlarse mediante instrucciones checked y
38unchecked (§8.11).
39Las siguientes operaciones resultan afectadas por el contexto de comprobación de desbordamiento establecido
40por los operadores e instrucciones checked y unchecked:
41• Los operadores unarios predefinidos ++ y - - (§7.5.9 y §7.6.5), cuando el operando es de un tipo integral.
42• El operador unario predefinido - (§7.6.2), cuando el operando es de un tipo integral.
43• Los operadores binarios predefinidos +, -, * y / (§7.7), cuando los dos operandos son de tipo integral.

343Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 159


344Especificación del lenguaje C#

1• Las conversiones explícitas numéricas (§6.2.1) de un tipo integral a otro, o de f l oa to doub le a un tipo
2 integral.
3Cuando una de las operaciones anteriores produce un resultado demasiado grande para representarlo en el tipo
4de destino, el contexto en que se ejecuta la operación controla el comportamiento resultante:
5• En un contexto checked, si la operación es una expresión constante (§7.15), se produce un error en tiempo
6 de compilación. O bien, si la operación se realiza en tiempo de ejecución, se inicia una excepción
7 Sys tem.Over f l owExcept i on .
8• En un contexto unchecked, el resultado se trunca, descartándose cualquier bit de orden superior que no
9 quepa en el tipo de destino.
10Para expresiones no constantes (expresiones que se evalúan en tiempo de ejecución) no incluidas por operadores
11o instrucciones checked o unchecked, el contexto de comprobación de desbordamiento predeterminado es
12unchecked salvo que factores externos (como la configuración de opciones del compilador y del entorno de
13ejecución) llamen a la evaluación checked.
14Para expresiones constantes (expresiones que pueden evaluarse completamente en tiempo de compilación), el
15contexto de comprobación de desbordamiento predeterminado siempre es checked. Salvo que una expresión
16constante se coloque explícitamente en un contexto unchecked, los desbordamientos que ocurren durante la
17evaluación en tiempo de compilación de la expresión siempre causan errores de tiempo de compilación.
18En el siguiente ejemplo:

19 class Test

20 {

21 static readonly int x = 1000000;


23 static int F() {
22
24 return checked(x * y); // Throws OverflowException

25
26 static int G() {

27 return unchecked(x * y); // Returns -727379968

28
29 static int H() {

30 return x * y; // Depends on default

31 }
33no se notifican errores de tiempo de compilación puesto que ninguna de las expresiones puede evaluarse en
34tiempo de compilación. En tiempo de ejecución, el método F inicia una excepción
32
35System.OverflowException y el método G devuelve –727379968 (los 32 bits menores del resultado fuera de
36intervalo). El comportamiento del método H depende del contexto de comprobación de desbordamiento
37predeterminado de la compilación, pero es el mismo que F o el mismo que G.
38En el siguiente ejemplo:

39 class Test

40 {

41 const int x = 1000000;


43 static int F() {
42
44 return checked(x * y); // Compile error, overflow

45
345160 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
346 Capítulo 18 Código no seguro

1 static int G() {

2 return unchecked(x * y); // Returns -727379968

34 static int H() {

5 return x * y; // Compile error, overflow

6 }
8los desbordamientos que se producen durante la evaluación de las expresiones constantes en F y H causan la
97notificación de errores en tiempo de compilación, puesto que las expresiones se evalúan en un contexto
10checked. También se produce un desbordamiento cuando se evalúa la expresión constante en G, si bien, al tener
11lugar la evaluación en un contexto unchecked, dicho desbordamiento no se notifica.
12Los operadores checked y unchecked solamente afectan al contexto de comprobación de desbordamiento para
13aquellas operaciones que están contenidas textualmente en los tokens “(” y “)”. Los operadores no tienen ningún
14efecto sobre los miembros de función que se invocan como consecuencia de la evaluación de la expresión
15contenida. En el siguiente ejemplo:

16 class Test

17 {

18 static int Multiply(int x, int y) {

19
21 static int F() {
20
22 return checked(Multiply(1000000, 1000000));

23 }
25el uso de checked en F no afecta a la evaluación de x * y en Multiply y, por lo tanto, x * y se evalúa en el
26
24contexto de comprobación de desbordamiento predeterminado.
27El operador unchecked es práctico cuando se escriben constantes de los tipos integrales con signo en notación
28hexadecimal. Por ejemplo:

29 class Test

30 {

31
32 public const int HighBit = unchecked((int)0x80000000);

33
34Las dos }constantes hexadecimales anteriores son de tipo uint. Dado que las constantes están fuera del intervalo
35de int, sin el operador unchecked, las conversiones al tipo int producirían errores de tiempo de compilación.
36Las instrucciones y operadores checked y unchecked permiten a los programadores controlar algunos
37aspectos de determinados cálculos numéricos. No obstante, el comportamiento de algunos operadores numéricos
38depende de los tipos de datos de sus operandos. Por ejemplo, la multiplicación de dos decimales siempre genera
39una excepción por desbordamiento, incluso en una construcción unchecked explícita. De igual manera, la
40multiplicación de dos tipos de punto flotante nunca genera una excepción por desbordamiento, incluso en una
41construcción checked explícita. Asimismo, otros operadores nunca resultan afectados por el modo de
42comprobación, ya sea predeterminado o explícito.

437.6 Operadores unarios


44Los operadores +, -, !, ~, ++, --, y los operadores de conversión de tipos se denominan operadores unarios.

347Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 161


348Especificación del lenguaje C#

1 unary-expression:
2 primary-expression
3 + unary-expression
4 - unary-expression
5 ! unary-expression
6 ~ unary-expression
7 pre-increment-expression
8 pre-decrement-expression
9 cast-expression

107.6.1 Operador unario de signo más


11Para una operación de la forma +x, se aplica la resolución de sobrecargas de operadores unarios (§7.2.3) para
12seleccionar una implementación de operador concreta. El operando se convierte al tipo de parámetro del
13operador seleccionado, y el tipo del resultado es el tipo de valor devuelto del operador. Los operadores de signo
14más unarios predefinidos son:
15 i n t opera to r +( in t x ) ;
16 u in t opera to r +(u in t x ) ;
17 l ong opera to r +( long x ) ;
18 u long opera to r +(u long x ) ;
19 f l oa t opera to r +( f l oa t x ) ;
20 doub le opera to r +(doub le x ) ;
22Para cada uno de estos operadores, el resultado es sencillamente el valor del operando.
21
237.6.2 Operador unario de signo menos
24Para una operación de la forma –x, se aplica la resolución de sobrecargas de operadores unarios (§7.2.3) para
25seleccionar una implementación de operador concreta. El operando se convierte al tipo de parámetro del
26operador seleccionado, y el tipo del resultado es el tipo de valor devuelto del operador. Los operadores de
27negación predefinidos son:
28• Negación entera:
29 i n t opera to r – ( i n t x ) ;
30 l ong opera to r – ( l ong x ) ;
31 El resultado se calcula restando x de cero. Si el valor de x es el valor más pequeño representable del tipo de
32 operando (−231 para int o −263 para long), la negación matemática de x no se puede representar en este tipo
33 de operando. Si esto ocurre dentro de un contexto checked, se inicia una excepción
34 System.OverflowException; si ocurre dentro de un contexto unchecked, el resultado es el valor del
35 operando y no se informa del desbordamiento.
36 Si el operando del operador de negación es de tipo uint, se convierte al tipo long y el tipo del resultado es
37 long. Una excepción es la regla que permite escribir el valor int −2147483648 (−231) como un literal entero
38 decimal (§2.4.4.2).
39 Si el operando del operador de negación es de tipo ulong, se produce un error durante la compilación. Una
40 excepción es la regla que permite escribir el valor long −9223372036854775808 (−263) como un literal
41 entero decimal (§2.4.4.2).
42• Negación de punto flotante:
43 f l oa t opera to r – ( f l oa t x ) ;
44 doub le opera to r – (doub le x ) ;

349162 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


350 Capítulo 18 Código no seguro

1 El resultado es el valor de x con su signo invertido. Si x es NaN, el resultado es también NaN.


2• Negación decimal:

3 decimal operator –(decimal x);


4 El resultado se calcula restando x de cero. La negación decimal equivale a usar el operador unario de signo
5 menos del tipo Sys tem.Dec ima l.

351Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 163


352Especificación del lenguaje C#

17.6.3 Operador de negación lógica


2Para una operación de la forma ! x, se aplica la resolución de sobrecargas de operadores unarios (§7.2.3) para
3seleccionar una implementación de operador concreta. El operando se convierte al tipo de parámetro del
4operador seleccionado, y el tipo del resultado es el tipo de valor devuelto del operador. Solamente existe un
5operador de negación lógica predefinido:
6 boo l opera to r ! (boo l x ) ;
7Este operador calcula la negación lógica del operando: si éste es t rue, el resultado es f a l se. Si el operando es
8false, el resultado es true.

97.6.4 Operador de complemento de bit a bit


10Para una operación de la forma ~x , se aplica la resolución de sobrecargas de operadores unarios (§7.2.3) para
11seleccionar una implementación de operador concreta. El operando se convierte al tipo de parámetro del
12operador seleccionado, y el tipo del resultado es el tipo de valor devuelto del operador. Los operadores de
13complemento de bit a bit predefinidos son:
14 i n t opera to r ~( in t x ) ;
15 u in t opera to r ~(u in t x ) ;
16 l ong opera to r ~( long x ) ;
17Para cada
18 u long
uno deopera to r ~(u long
estos operadores, x) ;
el resultado de la operación es el complemento de bit a bit de x.
19Todos los tipos de enumeración E proporcionan implícitamente el siguiente operador de complemento de bit a
20bit:
21 E opera to r ~(E x);
22El resultado de evaluar ~x, donde x es una expresión de un tipo de enumeración E con un tipo subyacente U, es
23exactamente el mismo que el de evaluar (E)(~(U)x).

247.6.5 Operadores prefijos de incremento y decremento


25 pre-increment-expression:
26 ++ unary-expression
27 pre-decrement-expression:
28 -- unary-expression
29El operando de una operación de prefijo de incremento o decremento debe ser una expresión clasificada como
30un variable, un acceso a propiedad o un acceso a indizador. El resultado de la operación es un valor del mismo
31tipo que el operando.
32Si el operando de una operación de prefijo de incremento o decremento es una propiedad o un acceso a
33indizador, la propiedad o el indizador debe tener tanto un descriptor de acceso get como set. Si no es éste el
34caso, se produce un error en tiempo de compilación.
35La resolución de sobrecargas de operadores unarios (§7.2.3) se aplica para seleccionar una implementación de
36operador concreta. Existen operadores predefinidos ++ y -- para los tipos siguientes: sbyte, byte, short,
37ushort, int, uint, long, ulong, char, float, double, decimal y cualquier tipo enum. Los operadores
38predefinidos ++ devuelven el valor generado al sumar 1 al operando y los operadores predefinidos -- devuelven
39el valor generado al restarle 1. En un contexto checked, si el resultado de esta suma o resta se encuentra fuera
40del intervalo del tipo del resultado y el tipo del resultado es un tipo integral o enum, se inicia una excepción
41System.OverflowException.

353164 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


354 Capítulo 18 Código no seguro

1El procesamiento en tiempo de ejecución de una operación de prefijo de incremento o decremento de la forma
2++x o - - xconsta de los pasos siguientes:
3• Si x está clasificada como una variable:
4 o Se evalúa x para producir la variable.
5 o Se invoca el operador seleccionado con el valor de x como argumento.
6 o El valor devuelto por el operador se almacena en la ubicación dada por la evaluación de x.
7 o El valor devuelto por el operador es el resultado de la operación.
8• Si x se clasifica como una propiedad o un acceso a indizador:
9 o Se evalúa la expresión de instancia (si x no es s ta t i)cy la lista de argumentos (si x es un acceso a
10 indizador) asociada con x, y el resultado se utiliza en las posteriores invocaciones de descriptor de
11 acceso get y set.
12 o Se invoca el descriptor de acceso get de x.
13 o Se invoca el operador seleccionado con el valor devuelto por el descriptor de acceso get como
14 argumento.
15 o Se invoca el descriptor de acceso set de x con el valor devuelto por el operador como su argumento
16 value.
17 o El valor devuelto por el operador es el resultado de la operación.
18Los operadores ++ y -- admiten la notación de postfijos (§7.5.9). El resultado de x++ o x-- es el valor de x
19antes de la operación, mientras que el resultado de ++x o --x es el valor de x después de la operación. En uno u
20otro caso, x tiene el mismo valor después de la operación.
21Una implementación de operator ++ u operator -- puede invocarse mediante la notación de postfijo o prefijo.
22No es posible contar con implementaciones de operador independientes para las dos notaciones.

237.6.6 Expresiones de conversión


24Una expresión de conversión de tipos (cast-expression) se usa para convertir explícitamente una expresión a un
25tipo dado.
26 cast-expression:
27 ( type ) unary-expression
28Una expresión de conversión cast (cast-expression) de la forma (T )E, donde T es un tipo (type) y E es una
29expresión unaria (unary-expression), realiza una conversión explícita (§6.2) del valor de E al tipo T. Si no existe
30una conversión explícita del tipo E al tipo T, se produce un error durante la compilación. En caso contrario, el
31resultado es el valor producido por la conversión explícita. El resultado siempre se clasifica como un valor,
32aunque E denote una variable.
33La gramática de una expresión de conversión de tipos (cast-expression) produce algunas ambigüedades
34sintácticas. Por ejemplo, la expresión (x)–y podría interpretarse como una expresión de conversión de tipos
35(cast-expression) (una conversión del tipo –y al tipo x) o como una expresión aditiva (additive-expression)
36combinada con una expresión entre paréntesis (parenthesized-expression) (que calcula el valor de x – y).
37Para resolver las ambigüedades de las expresiones de conversión de tipos (cast-expression), existe la siguiente
38regla: una secuencia de uno o más símbolos (tokens) (§2.3.3) encerrados entre paréntesis se considera el inicio
39de una expresión de conversión de tipos (cast-expression) sólo si al menos uno de los siguientes supuestos es
40cierto:

355Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 165


356Especificación del lenguaje C#

1• La secuencia de símbolos es correcta gramaticalmente para un tipo (type), pero no para una expresión
2 (expression).
3• La secuencia de símbolos es correcta gramaticalmente para un tipo (type), y el símbolo que sigue
4 inmediatamente al paréntesis de cierre es el token “~”, “!” o “(”, un identificador (identifier) (§2.4.1), un
5 literal (literal) (§2.4.4) o cualquier palabra clave (keyword) (§2.4.3) excepto as e is.
6El término “gramaticalmente correcta” significa que la secuencia de símbolos (tokens) debe ajustarse a la
7producción gramatical particular de esta clase de expresiones. En concreto, no se considera al significado real de
8sus identificadores constituyentes. Por ejemplo, si x e y son identificadores, entonces x.y es correcta
9gramaticalmente para un tipo, aunque x.y no denote realmente un tipo.
10De la regla de eliminación de ambigüedades se deduce que, si x e y son identificadores, (x)y, (x)(y) y (x)(-y)
11son expresiones de conversión de tipos (cast-expressions), pero no así (x)-y, aunque x identifique un tipo. No
12obstante, si x es una palabra clave que identifica un tipo predefinido (como int), las cuatro formas son
13expresiones de conversión de tipos (cast-expressions) (porque esta palabra clave nunca podría ser una expresión
14por sí misma).

157.7 Operadores aritméticos


16Los operadores *, /, %, + y – se denominan operadores aritméticos.
17 multiplicative-expression:
18 unary-expression
19 multiplicative-expression * unary-expression
20 multiplicative-expression / unary-expression
21 multiplicative-expression % unary-expression
22 additive-expression:
23 multiplicative-expression
24 additive-expression + multiplicative-expression
25 additive-expression – multiplicative-expression

267.7.1 Operador de multiplicación


27Para una operación de la forma x * y, se aplica la resolución de sobrecargas de operadores binarios (§7.2.4) para
28seleccionar una implementación de operador concreta. Los operandos se convierten a los tipos de parámetro del
29operador seleccionado, y el tipo del resultado es el tipo de valor devuelto por el operador.
30A continuación se enumeran los operadores de multiplicación predefinidos. Todos los operadores calculan el
31producto de x e y.
32• Multiplicación de enteros:
33 i n t opera to r * ( i n t x , i n t y ) ;
34 u in t opera to r * (u in t x , u in t y ) ;
35 l ong opera to r * ( l ong x , l ong y ) ;
36
37 u long
En un opera
contexto to r *, (u
checked long
si el x , uestá
producto long y ) ;del intervalo del tipo del resultado, se inicia una
fuera
38 excepción System.OverflowException. En un contexto unchecked, no se notifican los desbordamientos
39 y se descarta cualquier bit significativo de nivel superior del resultado que esté fuera del intervalo del tipo
40 de resultado.
41• Multiplicación de números de punto flotante:
42 f l oa t opera to r * ( f l oa t x , f l oa t y ) ;
43 doub le opera to r * (doub le x , doub le y ) ;

357166 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


358 Capítulo 18 Código no seguro

1 El producto se calcula según las reglas de aritmética IEEE 754. La siguiente tabla enumera los resultados de
2 todas las posibles combinaciones de valores finitos distintos de cero, ceros, infinitos y valores NaN. En la
3 tabla, x e y son valores finitos positivos. z es el resultado de x * y. Si el resultado es demasiado grande para
4 el tipo de destino, z es infinito. Si el resultado es demasiado pequeño para el tipo de destino, z es cero.
5

+y –y +0 –0 +∞ –∞ NaN
+x +z –z +0 –0 +∞ –∞ NaN
–x –z +z –0 +0 –∞ +∞ NaN
+0 +0 –0 +0 –0 NaN NaN NaN
–0 –0 +0 –0 +0 NaN NaN NaN
+∞ +∞ –∞ NaN NaN +∞ –∞ NaN
–∞ –∞ +∞ NaN NaN –∞ +∞ NaN
NaN NaN NaN NaN NaN NaN NaN NaN
6
7• Multiplicación de números decimales:
8 dec ima l opera to r * (dec ima l x , dec ima l y ) ;
9 Si el valor resultante es demasiado grande para representarlo en formato dec ima l, se inicia una excepción
10 Sys tem.Over f l owExcept i on . Si el valor resultante es demasiado pequeño para representarlo en formato
11 decimal, el resultado es cero. La escala del resultado, antes de cualquier redondeo, es la suma de las escalas
12 de los dos operandos.
13 La multiplicación de decimales equivale al uso del operador de multiplicación de tipo System.Decimal.

147.7.2 Operador de división


15Para una operación de la forma x / y, se aplica la resolución de sobrecargas de operadores binarios (§7.2.4) para
16seleccionar una implementación de operador concreta. Los operandos se convierten a los tipos de parámetro del
17operador seleccionado, y el tipo del resultado es el tipo de valor devuelto por el operador.
18A continuación se enumeran los operadores de división predefinidos. Todos los operadores calculan el cociente
19de x e y.
20• División de números enteros:
21 i n t opera to r / ( i n t x , i n t y ) ;
22 u in t opera to r / (u in t x , u in t y ) ;
23 l ong opera to r / ( l ong x , l ong y ) ;
24
25 Si eluvalor
longdel
opera to r derecho
operando / (u l onges xcero,
, u se
long y )una
inicia ; excepción System.DivideByZeroException.

26 La división redondea el resultado hacia cero, y el valor absoluto del resultado es el entero mayor posible que
27 sea menor que el valor absoluto del cociente de los dos operandos. El resultado es cero o positivo cuando los
28 dos operandos tienen el mismo signo, y cero o negativo si los dos operandos tienen signos opuestos.
29 Si el operando izquierdo es el int o long representable más pequeño y el derecho es –1, se produce un
30 desbordamiento. En un contexto checked, esto hace que se produzca una excepción
31 System.ArithmeticException (o una subclase de la misma). En un contexto unchecked, la
32 implementación define si se inicia una excepción System.ArithmeticException (o una subclase de la
33 misma) o no se informa del desbordamiento, siendo el valor resultante el del operando izquierdo.

359Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 167


360Especificación del lenguaje C#

1• División de números de punto flotante:

2 float operator /(float x, float y);

34 doubleseoperator
El cociente /(double
calcula según las reglas x, double y);
de aritmética IEEE 754. La siguiente tabla enumera los resultados de
5 todas las posibles combinaciones de valores finitos distintos de cero, ceros, infinitos y valores NaN. En la
6 tabla, x e y son valores finitos positivos. z es el resultado de x / y. Si el resultado es demasiado grande para
7 el tipo de destino, z es infinito. Si el resultado es demasiado pequeño para el tipo de destino, z es cero.
8

+y –y +0 –0 +∞ –∞ NaN
+x +z –z +∞ –∞ +0 –0 NaN
–x –z +z –∞ +∞ –0 +0 NaN
+0 +0 –0 NaN NaN +0 –0 NaN
–0 –0 +0 NaN NaN –0 +0 NaN
+∞ +∞ –∞ +∞ –∞ NaN NaN NaN
–∞ –∞ +∞ –∞ +∞ NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN
9
10• División de números decimales:
11 dec ima l opera to r / (dec ima l x , dec ima l y ) ;
12 Si el valor del operando derecho es cero, se inicia una excepción Sys tem.D iv ideByZeroExcept i on . Si el
13 valor resultante es demasiado grande para representarlo en formato dec ima l, se inicia una excepción
14 System.OverflowException. Si el valor resultante es demasiado pequeño para representarlo en formato
15 decimal, el resultado es cero. La escala del resultado será la menor escala que conserve un resultado igual
16 al valor decimal representable más cercano al auténtico valor matemático.
17 La división de decimales equivale al uso del operador de división de tipo System.Decimal.

187.7.3 Operador de resto


19Para una operación de la forma x % y, se aplica la resolución de sobrecargas de operadores binarios (§7.2.4)
20para seleccionar una implementación de operador concreta. Los operandos se convierten a los tipos de
21parámetro del operador seleccionado, y el tipo del resultado es el tipo de valor devuelto por el operador.
22A continuación se enumeran los operadores de resto predefinidos. Todos los operadores calculan el resto de la
23división entre x e y.
24• Resto de números enteros:
25 i n t opera to r %( in t x , i n t y ) ;
26 u in t opera to r %(u in t x , u in t y ) ;
27 l ong opera to r %( long x , l ong y ) ;
28
29 u long opera
El resultado de x %toyres%(u longproducido
el valor x , u long
por yx)–; (x / y) * y. Si y es cero, se produce una excepción
30 System.DivideByZeroException.
31 Si el operando izquierdo es el valor int o long más pequeño y el operando derecho es -1, se inicia una
32 excepción System.OverflowException. En ningún caso x % y inicia una excepción donde x / y no
33 inicien una excepción.

361168 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


362 Capítulo 18 Código no seguro

1• Resto de números de punto flotante:

2 float operator %(float x, float y);

34 double
La tabla operator
siguiente enumera%(double x, de
los resultados double y);
todas las posibles combinaciones de valores finitos distintos de
5 cero, ceros, valores infinitos y valores NaN. En la tabla, x e y son valores finitos positivos. z es el resultado
6 de x % y, y se calcula como x – n * y, donde n es el mayor entero posible que sea menor o igual a x / y.
7 Este método para calcular el resto es análogo al utilizado para los operandos enteros, pero difiere de la
8 definición de IEEE 754 (en la que n es el entero más próximo a x / y).
9

+y –y +0 –0 +∞ –∞ NaN
+x +z +z NaN NaN x x NaN
–x –z –z NaN NaN –x –x NaN
+0 +0 +0 NaN NaN +0 +0 NaN
–0 –0 –0 NaN NaN –0 –0 NaN
+∞ NaN NaN NaN NaN NaN NaN NaN
–∞ NaN NaN NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN NaN NaN
10
11• Resto de números decimales:
12 dec ima l opera to r %(dec ima l x , dec ima l y ) ;
13 Si el valor del operando derecho es cero, se inicia una excepción Sys tem.D iv ideByZeroExcept i on . La
14 escala del resultado, antes de cualquier redondeo, es la mayor de las escalas de los dos operandos; el signo
15 del resultado, si es distinto de cero, es el mismo que el de x.
16 El resto decimal equivale al uso del operador de resto de tipo Sys tem.Dec ima l.

177.7.4 Operador de suma


18Para una operación de la forma x + y, se aplica la resolución de sobrecargas de operadores binarios (§7.2.4)
19para seleccionar una implementación de operador concreta. Los operandos se convierten a los tipos de
20parámetro del operador seleccionado, y el tipo del resultado es el tipo de valor devuelto por el operador.
21A continuación se enumeran los operadores de suma predefinidos. Para tipos numéricos y de enumeración, los
22operadores de suma predefinidos calculan la suma de los dos operandos. Cuando al menos uno de los operandos
23es de tipo string, los operadores de suma predefinidos concatenan las representaciones de la cadena de los
24operandos.
25• Suma de números enteros:
26 i n t opera to r +( in t x , i n t y ) ;
27 u in t opera to r +(u in t x , u in t y ) ;
28 l ong opera to r +( long x , l ong y ) ;
29
30 u long
En un opera
contexto to r +(u
checked long
, si la sumax está
, u long y ) ;intervalo del tipo del resultado, se inicia una excepción
fuera del
31 Sys tem.Over f l owExcept i on . En un contexto unchecked, no se notifican los desbordamientos y se
32 descarta cualquier bit significativo de nivel superior del resultado que esté fuera del intervalo del tipo de
33 resultado.

363Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 169


364Especificación del lenguaje C#

1• Suma de números de punto flotante:

2 float operator +(float x, float y);

34 double
La suma operator
se calcula según +(double
las reglas dex, doubleIEEE
aritmética y);754. La tabla siguiente enumera los resultados de
5 todas las posibles combinaciones de valores finitos distintos de cero, ceros, valores infinitos y valores NaN.
6 En la tabla, x e y son valores finitos distintos de cero. z es el resultado de x + y. Si x e y tienen la misma
7 magnitud pero signos opuestos, z es cero positivo. Si x + y es demasiado grande para representarlo en el
8 tipo de destino, z es un infinito con el signo de x + y.
9

y +0 –0 +∞ –∞ NaN
x z x x +∞ –∞ NaN
+0 y +0 +0 +∞ –∞ NaN
–0 y +0 –0 +∞ –∞ NaN
+∞ +∞ +∞ +∞ +∞ NaN NaN
–∞ –∞ –∞ –∞ NaN –∞ NaN
NaN NaN NaN NaN NaN NaN NaN
10
11• Suma de números decimales:
12 dec ima l operato r +(dec ima l x , dec ima l y ) ;
13 Si el valor resultante es demasiado grande para representarlo en formato dec ima l, se inicia una excepción
14 Sys tem.Over f l owExcept i on . La escala del resultado, antes de cualquier redondeo, es la mayor de las
15 escalas de los dos operandos.
16 La suma de decimales equivale al uso del operador de suma de tipo Sys tem.Dec ima l.
17• Suma de enumeraciones. Todos los tipos de enumeración proporcionan de forma implícita los siguientes
18 operadores predefinidos, donde E es el tipo enum y U es el tipo subyacente de E:
19 E opera to r +(E x , U y ) ;
20 E opera to r +(U x , E y ) ;
21 Los operadores se evalúan exactamente como (E)((U)x + (U)y).
22• Concatenación de cadenas:
23 s t r i ng opera to r +(s t r i ng x , s t r i ng y ) ;
24 s t r i ng opera to r +(s t r i ng x , ob jec t y ) ;
25 s t r i ng opera to r +(ob jec t x , s t r i ng y ) ;
26 El operador binario + concatena cadenas cuando uno o los dos operandos son de tipo string. Si un
27 operando de la concatenación de cadenas es null, se sustituye una cadena vacía. O bien, cualquier
28 argumento no de cadena se convierte a su representación en formato de cadena mediante la invocación del
29 método virtual ToString heredado del tipo object. Si ToString devuelve null, se sustituye una cadena vacía.
30 us ing Sys tem;

365170 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


366 Capítulo 18 Código no seguro

1 class Test

2 {

3 static void Main() {

4 string s = null;

5 Console.WriteLine("s = >" + s + "<"); // displays s = ><

6 int i = 1;

7 Console.WriteLine("i = " + i); // displays i = 1

8 float f = 1.2300E+15F;
14 El resultado del operador de concatenación de cadenas es una cadena formada por los caracteres del
159 operando izquierdo seguidos de los caracteres del operando derecho. El operador de concatenación de
16
10 cadenas nunca devuelve un valor nu l .l Puede iniciarse una excepción Sys tem.OutO fMemoryExcept i on si
17 no hay suficiente memoria libre para asignar la cadena resultante.
11
18• Combinación de delegados. Todos los tipos delegados proporcionan implícitamente el siguiente operador
12
19 predefinido, donde D es el tipo delegado:
13
20 D operator +(D x, D y);
21 El operador binario + realiza la combinación de delegados cuando los dos operandos son de un tipo
22 delegado D. Si los operandos tienen tipos de delegado distintos, se produce un error durante la compilación.
23 Si el primer operando es null, el resultado de la operación es el valor del segundo operando (aunque este
24 operando también sea null). En caso contrario, si el segundo operando es null, el resultado de la operación
25 es el valor del primer operando. O bien, el resultado de la operación es una nueva instancia de delegado que,
26 cuando se invoca, llama al primer operando y después al segundo. Para obtener ejemplos de combinación de
27 delegados, vea §7.7.5 y §15.3. Puesto que System.Delegate no es un tipo delegado, no tiene operator +.

287.7.5 Operador de resta


29Para una operación de la forma x – y, se aplica la resolución de sobrecargas de operadores binarios (§7.2.4) para
30seleccionar una implementación de operador concreta. Los operandos se convierten a los tipos de parámetro del
31operador seleccionado, y el tipo del resultado es el tipo de valor devuelto por el operador.
32A continuación se enumeran los operadores de resta predefinidos. Todos los operadores restan y de x.
33• Resta de enteros:

34 int operator –(int x, int y);


35 uint operator –(uint x, uint y);
36 long operator –(long x, long y);
37 ulong operator –(ulong x, ulong y);
38 En un contexto checked, si la diferencia está fuera del intervalo del tipo del resultado, se inicia una
39 excepción System.OverflowException. En un contexto unchecked, no se notifican los desbordamientos
40 y se descarta cualquier bit significativo de nivel superior del resultado que esté fuera del intervalo del tipo
41 de resultado.
42• Resta de números de punto flotante:

43 float operator –(float x, float y);


44 double operator –(double x, double y);

367Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 171


368Especificación del lenguaje C#

1 La diferencia se calcula según las reglas de aritmética IEEE 754. La tabla siguiente muestra los resultados
2 de todas las posibles combinaciones de valores finitos distintos de cero, ceros, infinitos y NaN. En la tabla,
3 x e y son valores finitos distintos de cero y z es el resultado de x – y. Si x e y son iguales, z es cero positivo.
4 Si x – y es demasiado grande para representarlo en el tipo de destino, z es un infinito con el signo de x – y.
5

y +0 –0 +∞ –∞ NaN
x z x x –∞ +∞ NaN
+0 –y +0 +0 –∞ +∞ NaN
–0 –y –0 +0 –∞ +∞ NaN
+∞ +∞ +∞ +∞ NaN +∞ NaN
–∞ –∞ –∞ –∞ –∞ NaN NaN
NaN NaN NaN NaN NaN NaN NaN
6
7• Resta de números decimales:
8 dec ima l opera to r –(decimal x, decimal y);
9 Si el valor resultante es demasiado grande para representarlo en formato dec ima l, se inicia una excepción
10 Sys tem.Over f l owExcept i on . La escala del resultado, antes de cualquier redondeo, es la mayor de las
11 escalas de los dos operandos.
12 La resta de decimales equivale al uso del operador de resta de tipo Sys tem.Dec ima l.
13• Resta de enumeraciones. Todos los tipos de enumeración proporcionan de forma implícita el siguiente
14 operador predefinido, donde E es el tipo enum y U es el tipo subyacente de E:
15 U opera to r –(E x, E y);
16 Este operador se evalúa exactamente como (U)((U)x – (U)y). Es decir, el operador calcula la diferencia
17 entre los valores ordinales de x e y, y el tipo del resultado es el tipo subyacente de la enumeración.
18 E opera to r –(E x, U y);
19 Este operador se evalúa exactamente como (E)((U)x – y). Es decir, el operador resta un valor del tipo
20 subyacente de la enumeración, que produce un valor de la enumeración.
21• Eliminación de delegados. Todos los tipos delegados proporcionan implícitamente el siguiente operador
22 predefinido, donde D es el tipo delegado:
23 D opera to r –(D x, D y);
24 El operador – binario realiza la eliminación de delegados cuando los dos operandos son de un tipo delegado
25 D. Si los operandos tienen tipos de delegado distintos, se produce un error en tiempo de compilación. Si el
26 primer operando es null, el resultado de la operación también es null. En caso contrario, si el segundo
27 operando es null, el resultado de la operación es el valor del primer operando. De lo contrario, ambos
28 operandos representan listas de invocaciones (§15.1) que tienen una o más entradas, y el resultado es una
29 nueva lista de invocaciones que se compone de la lista del primer operando con las entradas del segundo
30 operando quitadas de ella, siempre que la lista del segundo operando sea una sublista apropiada contigua al
31 primero. (Para determinar la igualdad de la sublista, las entradas correspondientes se comparan en
32 cuanto al operador de igualdad del delegado (§7.9.8).) En caso contrario, el resultado es el valor del
33 operando izquierdo. En el proceso no se modifica la lista de ninguno de los operandos. Si la lista del
34 segundo operando coincide con varias sublistas de entradas contiguas de la lista del primero, se elimina la
35 sublista coincidente situada más a la derecha de las entradas contiguas. Si la eliminación produce una lista
36 vacía, el resultado es null. Por ejemplo:

369172 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


370 Capítulo 18 Código no seguro

1 delegate void D(int x);


2 class C

3 {

4 public static void M1(int i) { /* … */ }

57 class Test
68 {

9 static void Main() {

10 D cd1 = new D(C.M1);

11 D cd2 = new D(C.M2);


14 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1
12
15 cd3 -= cd1 + cd2; // => M2 + M1
16
13 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1

17 cd3 -= cd2 + cd2; // => M1 + M1


18 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1

19 cd3 -= cd2 + cd1; // => M1 + M2


20 cd3 = cd1 + cd2 + cd2 + cd1; // M1 + M2 + M2 + M1

21 cd3 -= cd1 + cd1; // => M1 + M2 + M2 + M1

22 }
24
237.8 Operadores de desplazamiento
25Los operadores << y >> permiten realizar operaciones de desplazamiento de bits.
26 shift-expression:
27 additive-expression
28 shift-expression << additive-expression
29 shift-expression >> additive-expression
30Para una operación de la forma x << count o x >> count, se aplica la resolución de sobrecargas de
31operadores binarios (§7.2.4) para seleccionar una implementación de operador concreta. Los operandos se
32convierten a los tipos de parámetro del operador seleccionado, y el tipo del resultado es el tipo de valor devuelto
33por el operador.
34Cuando se declara un operador de desplazamiento sobrecargado, el tipo del primer operando siempre debe ser la
35clase o estructura que contiene su declaración, mientras que el tipo del segundo operando siempre debe ser int.
36A continuación se enumeran los operadores de desplazamiento predefinidos.
37• Desplazamiento a la izquierda:
38 i n t opera to r <<( in t x , i n t count ) ;
39 u in t opera to r <<(u in t x , i n t count ) ;
40 l ong opera to r <<( long x , i n t count ) ;
41
42 u long opera
El operador to r <<(u
<< desplaza long
x a la x, in
izquierda eltnúmero
countde
) ; bits calculados como se explica a continuación.

43 Los bits de orden superior no comprendidos en el intervalo del tipo de resultado de x no se tienen en cuenta,
44 los bits restantes se desplazan a la izquierda y las posiciones vacías de los bits de orden inferior se
45 establecen en cero.

371Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 173


372Especificación del lenguaje C#

1• Desplazamiento a la derecha:

2 int operator >>(int x, int count);

3 uint operator >>(uint x, int count);

4 long operator >>(long x, int count);


6 El operador >> desplaza x a la derecha el número de bits calculados como se explica a continuación.
57 Si x es de tipo i n to long, los bits de orden inferior de x no se tienen en cuenta, los bits restantes se
8 desplazan a la derecha y las posiciones vacías de los bits de orden superior se establecen en cero si x no es
9 negativo, y en uno si x es negativo.
10 Si x es de tipo uint o ulong, los bits de orden inferior de x no se tienen en cuenta, los bits restantes se
11 desplazan a la derecha y las posiciones vacías de los bits de orden superior se establecen en cero.
12Para los operadores predefinidos, el número de bits del desplazamiento se calcula como se explica a
13continuación:
14• Si el tipo de x es i n to uint, el valor del desplazamiento viene dado por los cinco bits de orden inferior de
15 count. Es decir, el recuento del desplazamiento se calcula a partir de count & 0x1F.
16• Si el tipo de x es long o ulong, el valor del desplazamiento viene dado por los seis bits de orden inferior de
17 count. Es decir, el recuento del desplazamiento se calcula a partir de count & 0x3F.
18Si el valor del desplazamiento resultante es cero, los operadores de desplazamiento sencillamente devuelven el
19valor de x.
20Las operaciones de desplazamiento nunca causan desbordamientos y producen el mismo resultado en los
21contextos checked y unchecked.
22Si el operando izquierdo del operador >> es de un tipo integral con signo, el operador realiza un
23desplazamiento aritmético a la derecha, en el cual el valor del bit más significativo (el bit de signo) del operando
24se propaga a las posiciones vacías de los bits de orden superior. Si el operando izquierdo del operador >> es de
25un tipo integral sin signo, el operador realiza un desplazamiento lógico a la derecha, en el cual las posiciones
26vacías de los bits de orden superior siempre se establecen en cero. Para realizar la operación opuesta de la
27inferida a partir del tipo del operando, pueden utilizarse conversiones explícitas. Por ejemplo, si x es una
28variable de tipo int, la operación unchecked((int)((uint)x >> y)) realiza un desplazamiento lógico a la
29derecha de x.

307.9 Operadores de comprobación de tipos y relacionales


31Los operadores ==, !=, <, >, <=, >=, is y as se denominan operadores relacionales y de comprobación de
32tipos.
33 relational-expression:
34 shift-expression
35 relational-expression < shift-expression
36 relational-expression > shift-expression
37 relational-expression <= shift-expression
38 relational-expression >= shift-expression
39 relational-expression is type
40 relational-expression as type
41 equality-expression:
42 relational-expression
43 equality-expression == relational-expression
44 equality-expression != relational-expression

373174 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


374 Capítulo 18 Código no seguro

1El operador i s se describe en §7.9.9, y el operador as en §7.9.10.


2Los operadores == , !=, <, >, <= y >= son operadores de comparación. Para una operación de la forma x op
3y, donde op es un operador de comparación, se aplica la resolución de sobrecargas de operadores binarios
4(§7.2.4) para seleccionar una implementación de operador concreta. Los operandos se convierten a los tipos de
5parámetro del operador seleccionado, y el tipo del resultado es el tipo de valor devuelto por el operador.
6Los operadores de comparación predefinidos se describen en las siguientes secciones. Todos los operadores de
7comparación predefinidos devuelven un resultado de tipo boo l, como se muestra en la tabla siguiente.
8

Operación Resultado
x == y t ruesi x es igual a y, false en los demás casos
x != y true si x no es igual a y, false en los demás casos
x<y true si x es menor que y, false en los demás casos
x>y true si x es mayor que y, false en los demás casos
x <= y true si x es menor o igual que y, false en los demás casos
x >= y true si x es mayor o igual que y, false en los demás casos
9
107.9.1 Operadores de comparación de enteros
11Los operadores de comparación de enteros predefinidos son:
12 boo l opera to r ==(int x, int y);
13 bool operator ==(uint x, uint y);
14 bool operator ==(long x, long y);
15 bool operator ==(ulong x, ulong y);
16 boo l opera to r !=(int x, int y);
17 bool operator !=(uint x, uint y);
18 bool operator !=(long x, long y);
19 bool operator !=(ulong x, ulong y);
20 boo l opera to r <(int x, int y);
21 bool operator <(uint x, uint y);
22 bool operator <(long x, long y);
23 bool operator <(ulong x, ulong y);
24 boo l opera to r >(int x, int y);
25 bool operator >(uint x, uint y);
26 bool operator >(long x, long y);
27 bool operator >(ulong x, ulong y);
28 boo l opera to r <=(int x, int y);
29 bool operator <=(uint x, uint y);
30 bool operator <=(long x, long y);
31 bool operator <=(ulong x, ulong y);
32 boo l opera to r >=(int x, int y);
33 bool operator >=(uint x, uint y);
34 bool operator >=(long x, long y);
35 bool operator >=(ulong x, ulong y);
36Todos estos operadores comparan los valores numéricos de los dos operandos enteros y devuelven un valor bool
37que indica si la relación concreta es true o false.

375Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 175


376Especificación del lenguaje C#

17.9.2 Operadores de comparación de punto flotante


2Los operadores de comparación de punto flotante predefinidos son:

3 bool operator ==(float x, float y);


4 bool operator ==(double x, double y);
5 bool operator !=(float x, float y);
6 bool operator !=(double x, double y);
7 bool operator <(float x, float y);
8 bool operator <(double x, double y);
9 bool operator >(float x, float y);
10 bool operator >(double x, double y);
11 bool operator <=(float x, float y);
12 bool operator <=(double x, double y);
13 bool operator >=(float x, float y);
14 bool operator >=(double x, double y);
15Los operadores comparan los operandos según las reglas del estándar IEEE 754:
16• Si uno de los operandos es NaN, el resultado es f a l separa todos los operadores excepto !=, cuyo resultado
17 es true. Para dos operandos cualesquiera, x != y siempre produce el mismo resultado que !(x == y). No
18 obstante, si uno o los dos operandos son NaN, los operadores <, >, <= y >= no producen el mismo
19 resultado que la negación lógica del operador opuesto. Por ejemplo, si x o y es NaN, entonces x < y es
20 false, pero !(x >= y) es true.
21• Si ninguno de los operandos es NaN, los operadores comparan los valores de los dos operandos de punto
22 flotante con respecto al orden
23 –∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞
24 donde min y max son los valores finitos positivos máximo y mínimo que pueden representarse en el
25 formato de punto flotante. Son efectos notables de este orden:
26 o El cero negativo y el positivo se consideran iguales.
27 o Un infinito negativo se considera menor que todos los demás valores, pero igual que otro infinito
28 negativo.
29 o Un infinito positivo se considera mayor que todos los demás valores, pero igual que otro infinito
30 positivo.

317.9.3 Operadores de comparación decimales


32Los operadores de comparación de decimales predefinidos son:

33 bool operator ==(decimal x, decimal y);


34 bool operator !=(decimal x, decimal y);
35 bool operator <(decimal x, decimal y);
36 bool operator >(decimal x, decimal y);
37 bool operator <=(decimal x, decimal y);
38 bool operator >=(decimal x, decimal y);

377176 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


378 Capítulo 18 Código no seguro

1Todos estos operadores comparan los valores numéricos de los dos operandos decimales y devuelven un valor
2boo l que indica si la relación concreta es t rueo false. Cada comparación de decimales equivale al uso del
3operador relacional o de igualdad correspondiente de tipo System.Decimal.

47.9.4 Operadores de igualdad booleanos


5Los operadores de igualdad booleanos predefinidos son:

6 bool operator ==(bool x, bool y);


7 bool operator !=(bool x, bool y);
8El resultado de == es true si tanto x como y son true, o si tanto x como y son false. De lo contrario, el
9resultado es false.
10El resultado de != es false si tanto x como y son true, o si tanto x como y son false. De lo contrario, el
11resultado es true. Si los operandos son de tipo bool, el operador != produce el mismo resultado que ^.

127.9.5 Operadores de comparación de tipo de enumeración


13Todos los tipos de enumeración proporcionan implícitamente los siguientes operadores de comparación
14predefinidos:

15 bool operator ==(E x, E y);


16 bool operator !=(E x, E y);
17 bool operator <(E x, E y);
18 bool operator >(E x, E y);
19 bool operator <=(E x, E y);
20 bool operator >=(E x, E y);
21El resultado de evaluar x op y, donde x e y son expresiones de un tipo de enumeración E con un tipo subyacente
22U, y op es uno de los operadores de comparación, es exactamente el mismo que el de evaluar ((U)x) op ((U)y).
23Esto es lo mismo que decir que los operadores de comparación de tipo de enumeración sencillamente comparan
24los valores subyacentes integrales de los dos operandos.

257.9.6 Operadores de igualdad de tipos de referencia


26Los operadores de igualdad de tipos de referencia predefinidos son:

27 bool operator ==(object x, object y);


28 bool operator !=(object x, object y);
29Los operadores devuelven el resultado de comparar la igualdad o desigualdad de las dos referencias.
30Dado que los operadores de igualdad de tipos de referencia predefinidos aceptan operandos de tipo object, se
31aplican a todos los tipos que no declaran miembros aplicables operator == y operator !=. A la inversa,
32cualquier operador de igualdad aplicable definido por el usuario oculta los operadores de igualdad predefinidos
33de tipos de referencia.
34Los operadores de igualdad de tipo de referencia predefinidos requieren que los operandos sean valores de tipo
35de referencia (reference-type) o el valor null; además, requieren que exista una conversión implícita estándar
36(§6.3.1) del tipo de uno de los operandos al tipo del otro operando. A menos que estas dos condiciones sean
37verdaderas, se producirá un error de compilación. Son implicaciones notables de estas reglas:
38• Produce un error durante la compilación utilizar los operadores de igualdad de tipos de referencia
39 predefinidos para comparar dos referencias de las que se sabe que son diferentes en tiempo de compilación.

379Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 177


380Especificación del lenguaje C#

1 Por ejemplo, si los tipos de los operandos en tiempo de compilación fueran dos tipos de clases A y B, y si ni
2 A ni B se derivaran del otro, no sería posible que los operandos hicieran referencia al mismo objeto. Por lo
3 tanto, la operación se considera un error de compilación.
4• Los operadores de igualdad de tipos de referencia predefinidos no permiten la comparación de operandos de
5 tipo de valor. Por lo tanto, salvo que un tipo struct declare sus propios operadores de igualdad, no es posible
6 comparar valores de ese tipo struct.
7• Los operadores de igualdad de tipos de referencia predefinidos nunca causan operaciones boxing para sus
8 operandos. No tendría sentido realizar este tipo de operaciones boxing, puesto que las referencias a las
9 instancias convertidas mediante boxing recién asignadas diferirían necesariamente de todas las demás
10 referencias.
11Para una operación de la forma x == y o x != y, si existe un operator == u operator != aplicable, las reglas
12de resolución de sobrecargas de operador (§7.2.4) seleccionan este operador en lugar del operador de igualdad
13de tipos de referencia predefinido. No obstante, siempre es posible seleccionar el operador de igualdad de tipos
14de referencia predefinido mediante la conversión explícita de uno o los dos operandos al tipo object. En el
15ejemplo

16 using System;
17 class Test

18 {

19 static void Main() {

20 string s = "Test";

21 string t = string.Copy(s);

22 Console.WriteLine(s == t);

23 Console.WriteLine((object)s == t);
28produce el resultado
24
29 True
25
30 False
26
31 False
33
27Las variables s y t hacen referencia a las dos instancias únicas de string que contienen los mismos caracteres.
34La primera comparación produce True a causa de la selección del operador de igualdad de cadenas predefinido
32
35(§7.9.7) cuando los dos operandos son de tipo string. Todas las comparaciones restantes producen False a causa
36de la selección del operador de igualdad de tipos de referencia predefinido cuando uno o los dos operandos son
37de tipo object.
38Téngase en cuenta que la técnica anterior no tiene sentido para los tipos de valor. En el ejemplo

39 class Test

40 {

41 static void Main() {

42 int i = 123;

43 int j = 123;

44
45
381
46178 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
382 Capítulo 18 Código no seguro

1produce Fa l seporque las conversiones de tipos crean referencias a dos instancias distintas de valores i n t
2convertidos mediante boxing.

37.9.7 Operadores de igualdad de cadenas


4Los operadores de igualdad de cadenas predefinidos son:

5 bool operator ==(string x, string y);


6 bool operator !=(string x, string y);
7Dos valores s t r i ngse consideran iguales cuando una de las siguientes condiciones es verdadera:
8• Los dos valores son nu l .l
9• Los dos valores son referencias no nulas a instancias de cadenas que tienen una longitud idéntica y
10 caracteres idénticos en cada posición de carácter.
11Los operadores de igualdad de cadenas comparan valores de cadenas y no referencias a cadenas. Cuando dos
12instancias de cadena distintas contienen exactamente la misma secuencia de caracteres, los valores de las
13cadenas son iguales, pero las referencias son diferentes. Como se explica en la §7.9.6, los operadores de
14igualdad de tipos de referencia sirve para comparar referencias de cadenas en lugar de valores de cadenas.

383Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 179


384Especificación del lenguaje C#

17.9.8 Operadores de igualdad de delegados


2Todos los tipos delegados proporcionan implícitamente los siguientes operadores de comparación predefinidos:

3 bool operator ==(System.Delegate x, System.Delegate y);


4 bool operator !=(System.Delegate x, System.Delegate y);
5Dos instancias de delegados se consideran iguales en los siguientes casos:
6• Si una de las instancias de delegado es nu l ,l son iguales si y solamente si las dos son nu l .l
7• Si una de las instancias de delegado contiene una lista de invocaciones (§15.1) que contiene una sola
8 entrada, son iguales si y solamente si la otra también tiene una lista de invocaciones que sólo contiene una
9 entrada, y:
10 • las dos hacen referencia al mismo método estático, o bien
11 • ambas hacen referencia al mismo método no estático del mismo objeto de destino.
12• Si una de las instancias de delegado tiene una lista de invocaciones que contiene dos o más entradas, dichas
13 instancias son iguales si y solamente si sus listas de invocaciones tienen la misma longitud, y cada entrada
14 de la lista de invocaciones de una es igual a la entrada correspondiente, por orden, de la lista de
15 invocaciones de la otra.
16Debe tenerse en cuenta que los delegados de tipos diferentes pueden considerarse iguales por la definición
17anterior, siempre y cuando el tipo del valor devuelto y los tipos de parámetros sean los mismos.

187.9.9 Operador Is
19El operador i s se utiliza para comprobar dinámicamente si el tipo en tiempo de ejecución de un objeto es
20compatible con un tipo dado. El resultado de la operación e is T, donde e es una expresión y T es un tipo, es un
21valor booleano que indica si e puede convertirse de forma satisfactoria al tipo T mediante una conversión de
22referencias, una conversión boxing o una conversión unboxing. La operación se evalúa como sigue:
23• Si el tipo de e en tiempo de compilación es el mismo que el de T, o si existe una conversión de referencia
24 implícita (§6.1.4) o una conversión boxing (§6.1.5) desde el tipo de tiempo de compilación de e a T:
25 o Si e es un tipo de referencia, el resultado de la operación equivale a evaluar e != null.
26 o Si e es un tipo de valor, el resultado de la operación es true.
27• O bien, si existe una conversión explícita de referencia (§6.2.3) o una conversión unboxing (§6.2.4) desde el
28 tipo de tiempo de compilación de e a T, se ejecuta una comprobación de tipo dinámico:
29 o Si el valor de e es null, el resultado es false.
30 o O bien, supongamos que R es el tipo en tiempo de ejecución de la instancia a la que hace referencia e.
31 Si R y T son del mismo tipo, si R es un tipo de referencia y existe una conversión implícita de referencia
32 de R a T o si R es un tipo de valor y T es un tipo de interfaz que implementa R, el resultado es true.
33 o De lo contrario, el resultado es false.
34• O bien, no es posible una conversión de referencia o boxing de e al tipo T, y el resultado de la operación es
35 false.
36Tenga en cuenta que el operador is solamente tiene en cuenta las conversiones de referencia, las conversiones
37boxing y las conversiones unboxing. El operador is no tiene en cuenta otras conversiones, como las
38conversiones definidas por el usuario.

385180 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


386 Capítulo 18 Código no seguro

17.9.10 Operador As
2El operador as permite convertir explícitamente un valor a un tipo de referencia dado mediante una conversión
3de referencia o una conversión boxing. A diferencia de una expresión de conversión de tipos (§7.6.6), el
4operador as nunca inicia una excepción. En lugar de ello, si la conversión indicada no es posible, el valor
5resultante es null.
6En una operación de la forma e as T, e debe ser una expresión y T debe ser un tipo de referencia. El tipo del
7resultado es T, y el resultado siempre se clasifica como un valor. La operación se evalúa como sigue:
8• Si el tipo de e en tiempo de compilación es el mismo que el de T, el resultado es sencillamente el valor de e.
9• O bien, si existe una conversión implícita de referencia (§6.1.4) o una conversión boxing (§6.1.5) desde el
10 tipo de tiempo de compilación de e a T, se ejecuta esta conversión, que pasa a ser el resultado de la
11 operación.
12• O bien, si existe una conversión explícita de referencia (§6.2.3) desde el tipo de tiempo de compilación de e
13 a T, se realiza una comprobación de tipo dinámico:
14 o Si el valor de e es null, el resultado es el valor null con el tipo de tiempo de compilación T.
15 o O bien, supongamos que R es el tipo en tiempo de ejecución de la instancia a la que hace referencia e.
16 Si R y T son del mismo tipo, si R es un tipo de referencia y existe una conversión implícita de referencia
17 de R a T o si R es un tipo de valor y T es un tipo de interfaz que implementa R, el resultado es la
18 referencia dada por e con el tipo de tiempo de compilación T.
19 o En caso contrario, el resultado es el valor null con el tipo de tiempo de compilación T.
20• O bien, la conversión indicada no es posible y se produce un error en tiempo de compilación.
21Debe tenerse en cuenta que el operador as solamente ejecuta conversiones de referencia y conversiones boxing.
22Otras conversiones, como las definidas por el usuario, no son posibles con el operador as y deben realizarse
23mediante expresiones de conversión de tipos (cast).

247.10 Operadores lógicos


25Los operadores &, ^ y | se denominan operadores lógicos.
26 and-expression:
27 equality-expression
28 and-expression & equality-expression
29 exclusive-or-expression:
30 and-expression
31 exclusive-or-expression ^ and-expression
32 inclusive-or-expression:
33 exclusive-or-expression
34 inclusive-or-expression | exclusive-or-expression
35Para una operación de la forma x op y, donde op es uno de los operadores lógicos, se aplica la resolución de
36sobrecargas de operadores binarios (§7.2.4) para seleccionar una implementación de operador concreta. Los
37operandos se convierten a los tipos de parámetro del operador seleccionado, y el tipo del resultado es el tipo de
38valor devuelto por el operador.
39Los operadores lógicos predefinidos se describen en las siguientes secciones.

387Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 181


388Especificación del lenguaje C#

17.10.1 Operadores lógicos enteros


2Los operadores lógicos enteros predefinidos son:

3 int operator &(int x, int y);


4 uint operator &(uint x, uint y);
5 long operator &(long x, long y);
6 ulong operator &(ulong x, ulong y);
7 int operator |(int x, int y);
8 uint operator |(uint x, uint y);
9 long operator |(long x, long y);
10 ulong operator |(ulong x, ulong y);
11 int operator ^(int x, int y);
12 uint operator ^(uint x, uint y);
13 long operator ^(long x, long y);
14 ulong operator ^(ulong x, ulong y);
15El operador & calcula la operación lógica AND bit a bit de los dos operandos, el operador | calcula la operación
16lógica OR bit a bit de los dos operandos y el operador ^ calcula la operación lógica OR exclusivo bit a bit de los
17dos operandos. Los desbordamientos no son posibles en estas operaciones.

187.10.2 Operadores lógicos de enumeración


19Todo tipo de enumeración E proporciona implícitamente los siguientes operadores lógicos predefinidos:

20 E operator &(E x, E y);


21 E operator |(E x, E y);
22 E operator ^(E x, E y);
23El resultado de evaluar x op y, donde x e y son expresiones de un tipo de enumeración E con un tipo subyacente
24U, y op es uno de los operadores lógicos, es exactamente el mismo que el de evaluar (E)((U)x op (U)y). Esto es
25lo mismo que decir que los operadores lógicos de tipo de enumeración sencillamente ejecutan la operación
26lógica en el tipo subyacente de los dos operandos.

277.10.3 Operadores lógicos booleanos


28Los operadores lógicos booleanos predefinidos son:

29 bool operator &(bool x, bool y);


30 bool operator |(bool x, bool y);
31 bool operator ^(bool x, bool y);
32El resultado de x & y es true si tanto x como y son true. De lo contrario, el resultado es false.
33El resultado de x | y es true si x o y es true. De lo contrario, el resultado es false.
34El resultado de x ^ y es true si x es true e y es false, o si x es false e y es true. De lo contrario, el resultado
35es false. Si los operandos son de tipo bool, el operador ^ calcula el mismo resultado que el operador !=.

367.11 Operadores lógicos condicionales


37Los operadores && y || se denominan operadores lógicos condicionales. También se conocen como operadores
38lógicos de evaluación “cortocircuitada”.
39 conditional-and-expression:
40 inclusive-or-expression
41 conditional-and-expression && inclusive-or-expression

389182 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


390 Capítulo 18 Código no seguro

1 conditional-or-expression:
2 conditional-and-expression
3 conditional-or-expression | | conditional-and-expression
4Los operadores && y | |son versiones condicionales de los operadores & y |:
5• La operación x && y corresponde a la operación x & y, excepto que y se evalúa solamente si x es true.
6• La operación x || y corresponde a la operación x | y, excepto que y se evalúa solamente si x es false.
7Una operación de la forma x && y o x || y se procesa mediante la aplicación de la resolución de sobrecargas
8(§7.2.4) como si la operación estuviera escrita como x & y o x | y. Entonces:
9• Si la resolución de sobrecargas no encuentra un solo operador mejor o selecciona uno de los operadores
10 lógicos enteros predefinidos, se produce un error durante la compilación.
11• O bien, si el operador seleccionado es uno de los operadores lógicos booleanos predefinidos (§7.10.3), la
12 operación se procesa como se explica en §7.11.1.
13• O bien, el operador seleccionado es un operador definido por el usuario y la operación se procesa como se
14 explica en §7.11.2.
15No es posible sobrecargar directamente los operadores lógicos condicionales. No obstante, dado que los
16operadores lógicos condicionales se evalúan en términos de los operadores lógicos regulares, las sobrecargas de
17éstos, con algunas restricciones, también se consideran sobrecargas de los operadores lógicos condicionales.
18Esta categoría se explica con más detalle en la sección §7.11.2.

197.11.1 Operadores lógicos condicionales booleanos


20Si los operandos de && o || son de tipo bool, o si los operandos son de tipos que no definen un operator & o
21un operator | aplicable, pero sí definen conversiones implícitas a bool, la operación se procesa como sigue:
22• La operación x && y se evalúa como x ? y : false. En otras palabras, primero se evalúa x y se convierte al
23 tipo bool. Después, si x es true, y se evalúa y se convierte al tipo bool, y se convierte en el resultado de la
24 operación. De lo contrario, el resultado de la operación es false.
25• La operación x || y se evalúa como x ? true : y. En otras palabras, primero se evalúa x y se convierte al tipo
26 bool. Después, si el valor de x es true, el resultado de la operación es true. O bien, y se evalúa y se
27 convierte al tipo bool, y pasa a ser el resultado de la operación.

287.11.2 Operadores lógicos condicionales definidos por el usuario


29Si los operandos de && o de || son de tipos que declaran un operador operator & o un operador operator |
30aplicable definidos por el usuario, las dos siguientes declaraciones deben ser verdaderas, donde T es el tipo en
31que se declara el operador seleccionado:
32• El tipo del valor devuelto y el tipo de todos los parámetros del operador seleccionado debe ser T. Es decir, el
33 operador debe calcular el AND lógico o el OR lógico de los dos operandos de tipo T, y debe devolver un
34 resultado de tipo T.
35• T debe contener declaraciones de operator true y operator false.
36Si alguno de estos requisitos no se satisface, se produce un error de compilación. O bien, la operación && o || se
37evalúa mediante la combinación de operadores operator true u operator false definidos por el usuario con el
38operador seleccionado definido por el usuario:
39• La operación x && y se evalúa como T.false(x) ? x : T.&(x, y), donde T.false(x) es una invocación del
40 elemento operator false declarado en T, y T.&(x, y) es una invocación del elemento operator &

391Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 183


392Especificación del lenguaje C#

1 seleccionado. En otras palabras, x es el primero en evaluarse y se invoca opera to r f a l seen el resultado


2 para averiguar si x es definitivamente false. Después, si x es definitivamente false, el resultado de la
3 operación es el valor previamente calculado para x. De lo contrario, se evalúa y y se invoca el operador
4 operator & seleccionado en el valor previamente calculado para x y el valor calculado para y, a fin de
5 producir el resultado de la operación.
6• La operación x || y se evalúa como T.true(x) ? x : T.|(x, y), donde T.true(x) es una invocación del
7 elemento operator true declarado en T, y T.|(x, y) es una invocación del elemento operator |
8 seleccionado. En otras palabras, x es el primero en evaluarse y se invoca operator true en el resultado para
9 averiguar si x es definitivamente true. Después, si x es definitivamente true, el resultado de la operación es
10 el valor previamente calculado para x. De lo contrario, se evalúa y y se invoca el operador operator |
11 seleccionado en el valor previamente calculado para x y el valor calculado para y, a fin de producir el
12 resultado de la operación.
13En cualquiera de estas operaciones, la expresión dada por x se evalúa una sola vez, y la expresión dada por y no
14se evalúa o bien se evalúa exactamente una vez.
15Para obtener un ejemplo de un tipo que implementa operator true y operator false, vea §11.4.2.

167.12 Operador condicional


17El operador ?: se denomina operador condicional. A veces también se le denomina operador ternario.
18 conditional-expression:
19 conditional-or-expression
20 conditional-or-expression ? expression : expression
21Una expresión condicional con la estructura b ? x : y evalúa en primer lugar la condición b. Después, si el valor
22de b es true, se evalúa x y pasa a ser el resultado de la operación. De lo contrario, se evalúa y, que se convierte
23en el resultado de la operación. Una expresión condicional nunca evalúa x e y.
24El operador condicional es asociativo por la derecha, lo que significa que las operaciones se agrupan de derecha
25a izquierda. Por ejemplo, una expresión con la estructura a ? b : c ? d : e se evalúa como a ? b : (c ? d : e).
26El primer operando del operador ?: debe ser una expresión de un tipo que pueda convertirse implícitamente a
27bool o una expresión de un tipo que implemente operator true. Si no se cumple ninguna de estas dos
28condiciones, se producirá un error de compilación.
29El segundo y tercer operandos del operador ?: controlan el tipo de la expresión condicional. Supongamos que X
30e Y sean los tipos del segundo y tercer operandos. Entonces:
31• Si X e Y son del mismo tipo, entonces éste es el tipo de la expresión condicional.
32• O bien, si existe una conversión implícita (§6.1) de X a Y, pero no de Y a X, entonces Y es el tipo de la
33 expresión condicional.
34• O bien, si existe una conversión implícita (§6.1) de Y a X, pero no de X a Y, entonces X es el tipo de la
35 expresión condicional.
36• En caso contrario, no puede determinarse una expresión y se produce un error de compilación.
37El procesamiento en tiempo de ejecución de una expresión condicional con la estructura b ? x : y consta de los
38siguientes pasos:
39• En primer lugar, se evalúa b y se determina el valor bool de b:
40 o Si existe una conversión implícita del tipo de b a bool, se ejecuta dicha conversión para generar un
41 valor bool.

393184 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


394 Capítulo 18 Código no seguro

1 o De lo contrario, se invoca el opera to r t rue


definido por el tipo de b para generar un valor bool.
2• Si el valor bool producido por el paso anterior es true, entonces se evalúa x y se convierte al tipo de la
3 expresión condicional, que pasa a ser el resultado de la expresión condicional.
4• O bien, y se evalúa y se convierte al tipo de la expresión condicional, y pasa a ser el resultado de dicha de
5 expresión.

67.13 Operadores de asignación


7Los operadores de asignación se utilizan para asignar un valor nuevo a una variable, propiedad, evento o
8elemento de indizador.
9 assignment:
10 unary-expression assignment-operator expression
11 assignment-operator: uno de
12 = += -= *= /= %= &= |= ^= <<= >>=
13El operando izquierdo de una asignación debe ser una expresión clasificada como una variable, un acceso a
14propiedad, un acceso a indizador o un acceso a evento.
15El operador = se denomina operador de asignación simple. Asigna el valor del operando derecho a la variable,
16propiedad o elemento de indizador dado por el operando izquierdo. El operando de la izquierda del operador de
17asignación simple puede no ser un acceso a evento (excepto en las condiciones descritas en §10.7.1). El
18operador de asignación simple se explica en §7.13.1.
19Los operadores de asignación distintos del operador = se denominan operadores de asignación compuestos.
20Dichos operadores ejecutan la operación indicada en los dos operandos y después asignan el valor resultante a la
21variable, propiedad o elemento de indizador dado por el operando izquierdo. Los operadores de asignación
22compuesta se explican en §7.13.2.
23Los operadores += y -= con una expresión de acceso a eventos como operando izquierdo se denominan
24operadores de asignación de eventos. Ningún otro operador de asignación es válido si incluye un acceso a
25eventos como operando izquierdo. Los operadores de asignación de eventos se explican en §7.13.3.
26Los operadores de asignación son asociativos por la derecha, lo que significa que las operaciones se agrupan de
27derecha a izquierda. Por ejemplo, una expresión de la forma a = b = c se evalúa como a = (b = c).

287.13.1 Asignación simple


29El operador = se denomina operador de asignación simple. En una asignación simple, el operando derecho
30debe ser una expresión de un tipo que pueda convertirse implícitamente al tipo del operando izquierdo. La
31operación asigna el valor del operando derecho a la variable, propiedad o elemento de indizador dado por el
32operando izquierdo.
33El resultado de una expresión de asignación simple es el valor asignado al operando izquierdo. El resultado tiene
34el mismo tipo que el operando izquierdo y siempre se clasifica como un valor.
35Si el operando izquierdo es una propiedad o un acceso a indizador, debe tener un descriptor de acceso set. Si no
36es éste el caso, se produce un error en tiempo de compilación.
37El procesamiento en tiempo de ejecución de una asignación simple de la forma x = y consta de los siguientes
38pasos:
39• Si x está clasificada como una variable:
40 o Se evalúa x para producir la variable.

395Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 185


396Especificación del lenguaje C#

1 o Se evalúa y y, si es necesario, se convierte al tipo de x mediante una conversión implícita (§6.1).


2 o Si la variable dada por x es un elemento de matriz de un tipo de referencia (reference-type), se lleva a
3 cabo una comprobación en tiempo de ejecución para garantizar que el valor calculado de y es
4 compatible con la instancia de matriz a la cual pertenece x. La comprobación es satisfactoria si y es null
5 o si existe una conversión de referencia implícita (§6.1.4) del tipo real de la instancia a que hace
6 referencia y al tipo del elemento real de la instancia de matriz que contiene a x. De lo contrario, se inicia
7 una excepción System.ArrayTypeMismatchException.
8 o El valor resultante de la evaluación y conversión de y se almacena en la ubicación dada por la
9 evaluación de x.
10• Si x se clasifica como una propiedad o un acceso a indizador:
11 o Se evalúan la expresión de instancia (si x no es s ta t i)cy la lista de argumentos (si x es un acceso a
12 indizador) asociadas con x, y el resultado se utiliza en la posterior invocación de descriptor de acceso
13 set.
14 o Se evalúa y y, si es necesario, se convierte al tipo de x mediante una conversión implícita (§6.1).
15 o Se invoca el descriptor de acceso set de x con el valor calculado para y como su argumento value.
16Las reglas de covarianza matricial (§12.5) permiten que un valor de un tipo de matriz A[] se trate como una
17referencia a una instancia de un tipo matricial B[], siempre que exista una conversión implícita de referencias de
18B a A. Debido a estas reglas, la asignación de un tipo de referencia (reference-type) a un elemento de matriz
19requiere una comprobación en tiempo de ejecución para garantizar que el valor asignado es compatible con la
20instancia de matriz. En el siguiente ejemplo:
21 s t r i ng [ ] sa = new s t r i ng [10 ] ;
22 ob jec t [ ] oa = sa ;
23 oa[0 ] = nu l l ; / / Ok
24 oa[1 ] = "He l l o " ; / / Ok
25 oa[2 ] = new ArrayL i s t ( ) ; / / ArrayTypeMismatchExcept i on
26la última asignación produce el inicio de la excepción System.ArrayTypeMismatchException, debido a que
27la instancia de ArrayList no se puede almacenar en un elemento de una cadena string[].
28Cuando una propiedad o un indizador declarado en un tipo struct (struct-type) es el destino de una asignación, la
29expresión de instancia asociada al acceso a propiedad o a indizador debe estar clasificada como una variable. Si
30la expresión de instancia está clasificada como un valor, se produce un error de compilación. Conforme a la
31§7.5.4, la misma regla también es aplicable a los campos.
32Dadas las declaraciones:
33 s t ruc t Po in t
34 {
35 int x, y;
36 pub l i c Po in t ( i n t x , i n t y ) {
37 th i s . x = x ;
38 th i s . y = y ;
39 }
40 pub l i c i n t X {
41 get { re tu rn x ; }
42 se t { x = va lue ; }
43 }

397186 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


398 Capítulo 18 Código no seguro

1 public int Y {

2 get { return y; }

3 set { y = value; }

46 struct Rectangle
57 {

89 public Rectangle(Point a, Point b) {

10 this.a = a;

11 this.b = b;
13 public Point A {
12
14 get { return a; }

15 set { a = value; }
17 public Point B {
16
18 get { return b; }

19 set { b = value; }
22
20del ejemplo:
21
23 Point p = new Point();

24 p.X = 100;

25 p.Y = 100;

26 Rectangle r = new Rectangle();


29las asignaciones a p .X, p .Y, r.A y r.B están permitidas porque p y r son variables. No obstante, en el ejemplo:
27
30 Rectangle r = new Rectangle();
28
31 r.A.X = 10;

32 r.A.Y = 10;
35
33las asignaciones no son válidas, puesto que r.A y r.B no son variables.
347.13.2 Asignación compuesta
36
37Una operación de la forma x op= y se procesa mediante la aplicación de la resolución de sobrecargas de
38operadores binarios (§7.2.4) como si la operación se hubiera escrito x op y. Entonces:
39• Si el tipo de valor devuelto del operador seleccionado es convertible implícitamente al tipo de x, la
40 operación se evalúa como x = x op y, excepto en que x se evalúa una sola vez.
41• O bien, si el operador seleccionado es un operador predefinido, si el tipo del valor devuelto del operador
42 seleccionado es convertible explícitamente al tipo de x, y si y es convertible implícitamente al tipo de x,
43 entonces la operación se evalúa como x = (T)(x op y), donde T es el tipo x, excepto en que x se evalúa una
44 sola vez.
45• O bien, la asignación compuesta no es válida y se produce un error en tiempo de compilación.

399Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 187


400Especificación del lenguaje C#

1La expresión “se evalúa una sola vez” implica que, en la evaluación de x op y, el resultado de cualquier
2expresión que forme parte de x se guarda temporalmente y se reutiliza cuando se realiza la asignación a x. Por
3ejemplo, en la asignación A()[B()] += C(), donde A es un método que devuelve int[], y B y C son métodos que
4devuelven int, los métodos se invocan solamente una vez, en el orden A, B, C.
5Si el operando izquierdo de una asignación compuesta es un acceso a propiedad o un acceso a indizador, la
6propiedad o el indizador debe tener un descriptor de acceso get y un descriptor de acceso se t. Si no es éste el
7caso, se produce un error en tiempo de compilación.
8La segunda regla mencionada permite que x op= y se evalúe como x = (T)(x op y) en ciertos contextos. La
9regla existe para permitir utilizar los operadores predefinidos como operadores compuestos cuando el operando
10izquierdo es de tipo sbyte, byte, short, ushort o char. Aunque los dos argumentos sean de uno de estos tipos,
11los operadores predefinidos producen un resultado de tipo int, como se explica en §7.2.6.2. Por lo tanto, sin una
12conversión, no sería posible asignar el resultado al operando izquierdo.
13El efecto intuitivo de la regla de los operadores predefinidos es, sencillamente, que x op= y está permitido si
14están permitidos x op y, y x = y. En el siguiente ejemplo:
15 byte b = 0 ;
16 char ch = ' \ 0 ' ;
17 i n t i = 0;
18 b += 1 ; / / Ok
19 b += 1000 ; / / Er ro r , b = 1000 not permi t ted
20 b += i ; / / Er ro r , b = i not permi t ted
21 b += (by te ) i ; / / Ok
22 ch += 1 ; / / Er ro r , ch = 1 not permi t ted
23 ch += ( cha r )1 ; / / Ok
24el motivo intuitivo de cada error es que una asignación simple correspondiente también habría sido un error.

257.13.3 Asignación de eventos


26Si el operando izquierdo de un operador += o - = se clasifica como acceso a evento, la expresión se evalúa de la
27siguiente forma:
28• Si existe una expresión de instancia del acceso a evento, se evalúa.
29• Se evalúa el operando de la derecha del operador += o - =y, si fuera necesario, se convierte al tipo del
30 operando de la izquierda mediante una conversión implícita (§6.1).
31• Después de la evaluación y, si es necesario, también después de la conversión, se invoca un descriptor de
32 acceso del evento, con la lista de argumentos formada por el operando derecho. Si el operador fuera += , se
33 invoca el descriptor de acceso add ; si el operador fuera -=, se invoca el descriptor de acceso remove.
34Una expresión de asignación de evento no produce ningún valor. Por tanto, una expresión de asignación de
35eventos sólo será válida en el contexto de una expresión de instrucción (statement-expression) (§8.6).

367.14 Expresión
37Una expresión (expression) es una expresión condicional (conditional-expression) o una asignación
38(assignment).
39 expression:
40 conditional-expression
41 assignment

401188 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


402 Capítulo 18 Código no seguro

17.15 Expresiones constantes


2Una expresión constante (constant-expression) es una expresión que se puede evaluar totalmente en tiempo de
3compilación.
4 constant-expression:
5 expression
6El tipo de una expresión constante puede ser uno de los siguientes: sby te, byte, short, ushort, int, uint, long,
7ulong, char, float, double, decimal, bool, string, cualquier tipo de enumeración o el tipo null. Se permiten
8las siguientes construcciones en expresiones de constantes:
9• Literales (incluido null).
10• Referencias a miembros const y tipos de clase y estructura.
11• Referencias a miembros de tipos de enumeración.
12• Subexpresiones entre paréntesis, que son en sí mismas expresiones constantes.
13• Expresiones de conversión, siempre que el tipo de destino sea uno de los antes indicados.
14• Los operadores unarios predefinidos +, –, ! y ~.
15• Los operadores binarios predefinidos +, –, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <= y >=,
16 siempre que todos los operandos sean de uno de los tipos indicados anteriormente.
17• El operador condicional ?:.
18Se permiten las siguientes conversiones en expresiones de constantes:
19• Conversiones de identidad
20• Conversiones numéricas
21• Conversiones de enumeración
22• Conversiones de expresión constante
23• Conversiones de referencia implícita y explícita, siempre y cuando el origen de las conversiones sea una
24 expresión constante que se evalúe como null.
25Otras conversiones, incluidas las conversiones boxing y unboxing, y las conversiones de referencia implícita de
26valores distintos de null no se permiten en expresiones constantes. Por ejemplo:

27 class C {

28 const object i = 5; // error: boxing conversion not permitted

29 const object str = “hello”; // error: implicit reference conversion


31la inicialización de ies un error porque es necesaria una conversión boxing. La inicialización de s t res un error
32
30porque es necesaria una conversión de referencia implícita desde un valor distinto de null.
33Siempre que una expresión es de uno de los tipos mencionados y sólo contiene las construcciones indicadas, la
34expresión se evalúa en tiempo de compilación. Esto ocurre así aunque la expresión sea una sub-expresión de una
35expresión mayor que contiene construcciones no constantes.
36La evaluación en tiempo de compilación de expresiones constantes está regida por las mismas reglas que la
37evaluación en tiempo de ejecución de expresiones no constantes, excepto porque donde la evaluación en tiempo
38de ejecución iniciaría una excepción, la evaluación en tiempo de compilación causa un error de tiempo de
39compilación.

403Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 189


404Especificación del lenguaje C#

1Salvo que una expresión constante se coloque explícitamente en un contexto unchecked, los desbordamientos
2que ocurran en las conversiones y operaciones aritméticas de tipo integral durante la evaluación en tiempo de
3compilación de la expresión siempre causarán errores de tiempo de compilación (§7.5.12).
4Las expresiones constantes ocurren en los contextos que se enumeran a continuación. En estos contextos, se
5produce un error durante la compilación si una expresión no puede evaluarse totalmente en tiempo de
6compilación.
7• Declaraciones de constantes (§10.3).
8• Declaraciones de miembros de enumeración (§14.3).
9• Etiquetas case de una instrucción sw i t ch(§8.7.2).
10• Instrucciones goto case (§8.9.3).
11• Longitudes de dimensión en una expresión de creación de matriz (§7.5.10.2) que incluye un inicializador.
12• Atributos (§17).
13Una conversión implícita de expresión constante (§6.1.6) permite la conversión de una expresión constante de
14tipo i n tal tipo sby te, byte, short, ushort, uint o ulong, siempre que el valor de la expresión constante quede
15dentro del intervalo del tipo de destino.

167.16 Expresiones booleanas


17Una expresión booleana (boolean-expression) es una expresión que da como resultado un valor boo l.
18 boolean-expression:
19 expression
20La expresión condicional que controla una instrucción if (if-statement) (§8.7.1), instrucción while (while-
21statement) (§8.8.1), instrucción do (do-statement) (§8.8.2) o instrucción for (for-statement) (§8.8.3) es una
22expresión booleana (boolean-expression). La expresión condicional de control del operador ? : (§7.12) sigue las
23mismas reglas que una expresión booleana (boolean-expression) pero, por motivos de prioridad de operadores,
24se clasifica como una expresión or condicional (conditional-or-expression).
25Una expresión booleana (boolean-expression) debe ser de un tipo que pueda convertirse implícitamente a boo l o
26de un tipo que implemente opera to rtrue. Si no se satisface ninguno de los requisitos, se produce un error de
27compilación.
28Si una expresión booleana es de un tipo que no puede convertirse implícitamente a bool pero que implementa
29operator true, después de la evaluación de la expresión, se llama a la implementación de operator true
30suministrada por este tipo para generar un valor bool.
31El tipo struct DBBool de la §11.4.2 proporciona un ejemplo de un tipo que implementa operator true y
32operator false.

405190 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


406 Capítulo 18 Código no seguro

1 8.Instrucciones

2C# proporciona una gran variedad de instrucciones. La mayoría de ellas son conocidas por los programadores de
3C y C++.
4 statement:
5 labeled-statement
6 declaration-statement
7 embedded-statement
8 embedded-statement:
9 block
10 empty-statement
11 expression-statement
12 selection-statement
13 iteration-statement
14 jump-statement
15 try-statement
16 checked-statement
17 unchecked-statement
18 lock-statement
19 using-statement
20La instrucción incrustada (embedded-statement) sin terminación se utiliza en instrucciones que aparecen dentro
21de otras instrucciones. El uso de una instrucción incrustada en lugar de una instrucción (statement) permite que
22no sea necesario utilizar instrucciones de declaración y con etiqueta en dichos contextos. En el ejemplo

23 void F(bool b) {

24 if (b)

25 int i = 44;
27da como resultado un error en tiempo de compilación, ya que una instrucción i frequiere una instrucción
28
26incrustada (embedded-statement) en lugar de una instrucción (instruction) para su rama if. Si se admitiera este
29código, la variable i se declararía pero no se utilizaría nunca. Observe, sin embargo, que si coloca la declaración
30de i en un bloque, el ejemplo es válido.

318.1 Puntos finales y alcance


32Toda instrucción tiene un punto final. De manera intuitiva, el punto final de una instrucción es la ubicación que
33sigue a la instrucción. Las reglas para la ejecución de instrucciones compuestas (instrucciones que contienen
34instrucciones incrustadas) especifican las acciones a tomar cuando el control llega al punto final de una
35instrucción incrustada. Por ejemplo, cuando el control llega al punto final de una instrucción dentro de un
36bloque, pasa a la siguiente instrucción del bloque.
37Si una instrucción tiene posibilidades de ejecutarse, se dice que la instrucción es alcanzable. De manera inversa,
38si una instrucción no tiene ninguna posibilidad de ejecutarse, se dice que es una instrucción inalcanzable.

407Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 191


408Especificación del lenguaje C#

1En el siguiente ejemplo:

2 void F() {

3 Console.WriteLine("reachable");

4 goto Label;

5 Console.WriteLine("unreachable");

96la segunda llamada


Label:a Conso le .Wr i teL inees inalcanzable porque no hay posibilidad de que se ejecute la
10instrucción.
7
11Si el compilador determina que existe alguna instrucción inalcanzable, emite una advertencia. Que una
128instrucción sea inalcanzable no es un error propiamente dicho.
13Para determinar si una determinada instrucción o punto final es o no alcanzable, el compilador realiza un
14análisis del flujo del programa de acuerdo a las reglas de alcance definidas para cada instrucción. El análisis de
15flujo tiene en cuenta los valores de expresiones de constantes (§7.15) que controlan el comportamiento de las
16instrucciones, pero no considera los posibles valores de expresiones de variables. En otras palabras, en el
17análisis de flujo se considera que una expresión de variable de un determinado tipo puede tener cualquier valor
18posible de dicho tipo.
19En el siguiente ejemplo:

20 void F() {

21 const int i = 1;

22 if (i == 2) Console.WriteLine("unreachable");
24la expresión booleana de la instrucción i fes una expresión constante porque los dos operandos del operador ==
25
23son constantes. La expresión constante se evalúa en tiempo de compilación y, como devuelve el valor false, la
26llamada a Console.WriteLine se considera inalcanzable. Sin embargo, si i se convierte en una variable local

27 void F() {

28 int i = 1;

29 if (i == 2) Console.WriteLine("reachable");
31la llamada a Console.WriteLine se considera alcanzable, aunque en realidad no se ejecutará nunca.
30
32El bloque (block) de un miembro de función siempre se considera alcanzable. Evaluando sucesivamente las
33reglas de alcance de cada instrucción de un bloque, puede determinarse el alcance de una determinada
34instrucción.
35En el siguiente ejemplo:

36 void F(int x) {

37 Console.WriteLine("start");

38 if (x < 0) Console.WriteLine("negative");
40el alcance de la segunda instrucción Console.WriteLine se determina de la forma siguiente:
39
41• La primera instrucción de la expresión Console.WriteLine es alcanzable, ya que el bloque del método F
42 también lo es.
43• El punto final de la primera instrucción Console.WriteLine es alcanzable porque esta instrucción también
44 lo es.

409192 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


410 Capítulo 18 Código no seguro

1• La instrucción i fes alcanzable porque el punto final de la primera instrucción Conso le .Wr i teL ine
también
2 lo es.
3• Por último, como la expresión booleana de la instrucción i fno tiene el valor constante f a l se
, la segunda
4 instrucción Console.WriteLine es alcanzable.
5Existen dos situaciones en las que supone un error en tiempo de compilación que el punto final de una
6instrucción sea alcanzable:
7• Como la instrucción switch no admite que de una sección “se pase” a la siguiente sección, supone un error
8 en tiempo de compilación que el punto final de la lista de instrucciones de una sección de switch sea
9 alcanzable. Este error suele producirse cuando falta una instrucción break.
10• Es un error que sea alcanzable el punto final del bloque de un miembro de función que calcula un valor. Este
11 error suele producirse cuando falta una instrucción return.

128.2 Bloques
13Un bloque (block) permite escribir varias instrucciones en contextos donde se admite una única instrucción.
14 block:
15 { statement-listopt }
16Un bloque (block) está formado por una lista de instrucciones (statement-list) opcional (§8.2.1), encerrada entre
17llaves. Si se omite la lista de instrucciones, se dice que el bloque es un bloque vacío.
18Un bloque puede contener instrucciones de declaración (§8.5). El ámbito de una variable o constante local
19declarada en un bloque es el propio bloque.
20Dentro de un bloque, el significado de un nombre utilizado en un contexto de expresión siempre debe ser el
21mismo (§7.5.2.1).
22Un bloque se ejecuta de la siguiente forma:
23• Si el bloque está vacío, el control se transfiere al punto final del bloque.
24• Si no está vacío, el control se transfiere a la lista de instrucciones. Cuando el control alcanza el punto final
25 de la lista de instrucciones, se transfiere al punto final del bloque.
26La lista de instrucciones de un bloque es alcanzable si el propio bloque es alcanzable.
27El punto final de un bloque es alcanzable si el bloque está vacío o si el punto final de la lista de instrucciones es
28alcanzable.

298.2.1 Listas de instrucciones


30Una lista de instrucciones está formada por una o varias instrucciones escritas secuencialmente. Las listas de
31instrucciones aparecen en bloques (blocks) (§8.2) y en bloques de modificadores (switch-blocks) (§8.7.2).
32 statement-list:
33 statement
34 statement-list statement
35Cuando se ejecuta una lista de instrucciones, se transfiere el control a la primera instrucción. Cuando el control
36alcanza el punto final de una instrucción, se transfiere a la siguiente instrucción. Cuando el control alcanza el
37punto final de la última instrucción, se transfiere al punto final de la lista de instrucciones.
38Una instrucción que forma parte de una lista de instrucciones es alcanzable si se cumple al menos una de las
39siguientes condiciones:

411Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 193


412Especificación del lenguaje C#

1• Es la primera instrucción y la propia lista de instrucciones es alcanzable.


2• El punto final de la instrucción anterior es alcanzable.
3• La instrucción es una instrucción con etiqueta y la etiqueta es referenciada por una instrucción goto
4 alcanzable.
5El punto final de una lista de instrucciones es alcanzable si el punto final de la última instrucción de la lista es
6alcanzable.

78.3 Instrucción vacía


8Una instrucción vacía (empty-statement) no hace nada.
9 empty-statement:
10 ;
11Una instrucción vacía se utiliza cuando no hay operaciones que realizar en un contexto donde se requiere una
12instrucción.
13Cuando se ejecuta una instrucción vacía, simplemente se transfiere el control al punto final de la instrucción. Por
14lo tanto, el punto final de una instrucción vacía es alcanzable si la instrucción vacía es alcanzable.
15Puede utilizar una instrucción vacía cuando escriba una instrucción whi l esin cuerpo:
16 boo l ProcessMessage ( ) {...}
17 vo id ProcessMessages ( ) {
18 whi l e ( P rocessMessage ( ) )
19 ;
20 }
21También puede utilizarla para declarar una etiqueta justo antes de la llave de cierre “}” de un bloque:
22 vo id F ( ) {
23 ...
24 i f (done ) goto ex i t ;
25 ...
26 ex i t : ;
27 }

288.4 Instrucciones con etiqueta


29Una instrucción con etiqueta (labeled-statement) permite agregar una etiqueta por delante de una instrucción.
30Las instrucciones con etiqueta se pueden incluir en bloques, pero no están permitidas como instrucciones
31incrustadas.
32 labeled-statement:
33 identifier : statement
34Una instrucción con etiqueta declara una etiqueta con el nombre especificado en el identificador (identifier). El
35ámbito de una etiqueta es el bloque entero en el cual se declara, incluyendo los bloques anidados. Se produce un
36error en tiempo de compilación cuando se definen dos etiquetas con el mismo nombre para que sus ámbitos se
37solapen.
38Se puede hacer referencia a una etiqueta desde instrucciones goto (§8.9.3) dentro del ámbito de la etiqueta. Esto
39significa que las instrucciones goto pueden transferir el control dentro y fuera de los bloques, pero nunca en los
40bloques.

413194 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


414 Capítulo 18 Código no seguro

1Las etiquetas tienen su propio espacio de declaración y no interfieren con otros identificadores. En el ejemplo

2 int F(int x) {

3 if (x >= 0) goto x;

4 x = -x;
75es un ejemplo válido que utiliza el nombre x como parámetro y como etiqueta.
86Una instrucción con etiqueta se ejecuta tal y como se ejecute la instrucción que sigue a la etiqueta.
9Además de la capacidad de alcance que proporciona el flujo normal de control, una instrucción con etiqueta es
10alcanzable si una instrucción goto alcanzable hace referencia a la etiqueta. (Excepción: si una instrucción goto
11se encuentra dentro de otra try que incluye un bloque finally, la instrucción con etiqueta está fuera de try y el
12extremo del bloque finally no es alcanzable, entonces la instrucción con etiqueta tampoco es alcanzable desde
13esta instrucción goto).

148.5 Instrucciones de declaración


15Una instrucción de declaración (declaration-statement) declara una variable o una constante local. Las
16instrucciones de declaración se pueden incluir en bloques, pero no están permitidas como instrucciones
17incrustadas.
18 declaration-statement:
19 local-variable-declaration ;
20 local-constant-declaration ;

218.5.1 Declaraciones de variables locales


22Una declaración de variable local (local-variable-declaration) declara una o varias variables locales.
23 local-variable-declaration:
24 type local-variable-declarators
25 local-variable-declarators:
26 local-variable-declarator
27 local-variable-declarators , local-variable-declarator
28 local-variable-declarator:
29 identifier
30 identifier = local-variable-initializer
31 local-variable-initializer:
32 expression
33 array-initializer
34El tipo (type) de una declaración de variable local (local-variable-declaration) especifica el tipo de las variables
35que se incluyen en la declaración. El tipo aparece seguido de una lista de declaradores de variable local (local-
36variable-declarators), cada una de las cuales incluye una nueva variable. Un declarador de variable local (local-
37variable-declarator) está formado por un identificador (identifier) que da nombre a la variable, que
38opcionalmente puede ir seguido del símbolo (token) “=” y de un inicializador de variable local (local-variable-
39initializer) que establece el valor inicial de la variable.
40Para obtener el valor de una variable local en una expresión se utiliza un nombre simple (simple-name) (§7.5.2)
41y, para modificarlo, se realiza una asignación (assignment) (§7.13). Una variable local debe estar asignada
42definitivamente (§5.3) en cada ubicación donde se obtenga su valor.

415Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 195


416Especificación del lenguaje C#

1El ámbito de una variable local declarada en una declaración de variable local (local-variable-declaration) es el
2bloque donde se produce la declaración. Supone un error hacer referencia a una variable local en una posición
3textual que precede al declarador de la variable local (local-variable-declarator). Dentro del ámbito de una
4variable local, supone un error en tiempo de compilación declarar otra variable o constante local con el mismo
5nombre.
6Una declaración de variable local que declara varias variables equivale a varias declaraciones de una sola
7variable con el mismo tipo. Un inicializador de variable (variable-initializer) en una declaración de variable
8local es en realidad una instrucción de asignación que se inserta inmediatamente después de la declaración.
9En el ejemplo

10 void F() {

11 int x = 1, y, z = x * 2;
13es idéntico a
12
14 void F() {

15 int x; x = 1;

16 int y;

178.5.2 Declaraciones de constantes locales


19
18Una declaración de constante local (local-constant-declaration) declara una o varias constantes locales.
20
21 local-constant-declaration:
22 cons t type constant-declarators
23 constant-declarators:
24 constant-declarator
25 constant-declarators , constant-declarator
26 constant-declarator:
27 identifier = constant-expression
28El tipo (type) de una declaración de constante local (local-constant-declaration) especifica el tipo de las
29constantes que se incluyen en la declaración. El tipo (type) viene seguido de una lista de declaradores de
30constante (constant-declarators), cada uno de los cuales incluye una nueva constante. Un declarador de
31constante (constant-declarator) consta de un identificador (identifier) que da nombre a la constante, seguido del
32símbolo (token) “=” y de una expresión constante (constant-expression) (§7.15) que establece el valor de la
33constante.
34El tipo (type) y la expresión constante (constant-expression) de una declaración de constante local deben seguir
35las reglas de declaración de miembros de constantes (§10.3).
36Para obtener el valor de una constante local en una expresión, se utiliza el nombre simple (simple-name)
37(§7.5.2).
38El ámbito de una constante local es el bloque donde se produce la declaración. Es un error hacer referencia a una
39constante local en una posición textual anterior a su declarador de constante (constant-declarator). Dentro del
40ámbito de una constante local, supone un error en tiempo de compilación declarar otra variable o constante local
41con el mismo nombre.
42Una declaración de constante local que declara varias constantes equivale a varias declaraciones de una sola
43constante con el mismo tipo.

417196 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


418 Capítulo 18 Código no seguro

18.6 Instrucciones de expresiones


2Una instrucción de expresión (expression-statement) evalúa una expresión determinada. El valor calculado por
3la expresión, en el caso de haber alguno, se descarta.
4 expression-statement:
5 statement-expression ;
6 statement-expression:
7 invocation-expression
8 object-creation-expression
9 assignment
10 post-increment-expression
11 post-decrement-expression
12 pre-increment-expression
13 pre-decrement-expression
14No todas las expresiones pueden ser instrucciones. En concreto, expresiones como x + y y x == 1, que
15simplemente calculan un valor (el cual será descartado), no se admiten como instrucciones.
16La ejecución de una instrucción de expresión (expression-statement) evalúa la expresión que contiene y después
17transfiere el control al punto final de la instrucción de expresión. Por lo tanto, el punto final de una instrucción
18de expresión es alcanzable si dicha instrucción también lo es.

198.7 Instrucciones de selección


20Las instrucciones de selección (selection-statement) seleccionan una de las instrucciones que se van a ejecutar
21en función del valor de alguna expresión.
22 selection-statement:
23 if-statement
24 switch-statement

258.7.1 Instrucción If
26La instrucción if selecciona una instrucción que se va a ejecutar basándose en el valor de una expresión
27booleana.
28 if-statement:
29 i f ( boolean-expression ) embedded-statement
30 if ( boolean-expression ) embedded-statement else embedded-statement
31 boolean-expression:
32 expression
33La sección else se asocia a la instrucción if anterior más cercana permitida por la sintaxis. Por lo tanto, una
34instrucción if de la forma
35 i f ( x ) i f ( y ) F ( ) ; e l se G( ) ;
36equivale a

419Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 197


420Especificación del lenguaje C#

1 if (x) {

2 if (y) {

3 F();

4 }

5 else {
9Una instrucción i fse ejecuta de la siguiente forma:
6
10• Se evalúa la expresión booleana (boolean-expression) (§7.16).
7
11• Si la expresión booleana devuelve t rue, el control se transfiere a la primera instrucción incrustada. Cuando
128 el control alcanza el punto final de dicha instrucción, se transfiere al punto final de la instrucción i f.
13• Si la expresión booleana devuelve f a l sey existe una sección e l se, el control se transfiere a la segunda
14 instrucción incrustada. Cuando el control alcanza el punto final de dicha instrucción, se transfiere al punto
15 final de la instrucción if.
16• Si la expresión booleana devuelve false y no existe una sección else, el control se transfiere al punto final
17 de la instrucción if.
18La primera instrucción incrustada de una instrucción if es alcanzable si la instrucción if es alcanzable y la
19expresión booleana no tiene el valor constante false.
20La segunda instrucción incrustada de una instrucción if, si existe, es alcanzable si la instrucción if es alcanzable
21y la expresión booleana no tiene el valor constante true.
22El punto final de una instrucción if es alcanzable si el punto final de al menos una de sus instrucciones
23incrustadas es alcanzable. Además, el punto final de una instrucción if que no tiene sección else es alcanzable si
24la instrucción if es alcanzable y la expresión booleana no tiene el valor constante true.

258.7.2 Instrucción Switch


26La instrucción switch (switch-statement) selecciona una lista de instrucciones que se van a ejecutar que tengan
27asociada una etiqueta switch (switch-label) que se corresponda con el valor de la expresión switch.
28 switch-statement:
29 sw i t ch ( expression ) switch-block
30 switch-block:
31 { switch-sectionsopt }
32 switch-sections:
33 switch-section
34 switch-sections switch-section
35 switch-section:
36 switch-labels statement-list
37 switch-labels:
38 switch-label
39 switch-labels switch-label
40 switch-label:
41 case constant-expression :
42 default :

421198 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


422 Capítulo 18 Código no seguro

1Una instrucción switch (switch-statement) está formada por la palabra clave sw i t ch, seguida de una expresión
2entre paréntesis (denominada expresión switch) y de un bloque switch (switch-block). El bloque switch consiste
3en varias o ninguna secciones de switch (switch-section), encerradas entre llaves. Cada sección de switch
4(switch-section) está formada por una o varias etiquetas de switch (switch-label) seguidas de una lista de
5instrucciones (statement-list) (§8.2.1).
6El tipo aplicable en una instrucción sw i t chestá establecido por la expresión switch. Si el tipo de la expresión
7switch es sby te, byte, short, ushort, int, uint, long, ulong, char, string o un tipo enum (enum-type), ése
8será el tipo aplicable en la instrucción switch. En caso contrario, debe existir una conversión implícita definida
9por el usuario (§6.4) del tipo de la expresión switch a uno de los posibles tipos aplicables: sbyte, byte, short,
10ushort, int, uint, long, ulong, char y string. Si no existe una conversión implícita o existe más de una, se
11producirá un error en tiempo de compilación.
12La expresión constante de cada etiqueta case debe denotar un valor de tipo convertible implícitamente (§6.1) al
13tipo aplicable en la instrucción sw i t ch. Si dos o más etiquetas case de la misma instrucción switch especifican
14el mismo valor constante, se producirá un error en tiempo de compilación.
15Puede existir como máximo una etiqueta default en una instrucción switch.
16Una instrucción switch se ejecuta de la siguiente forma:
17• Se evalúa la expresión switch y se convierte en el tipo aplicable.
18• Si una de las constantes especificadas en una etiqueta case de una instrucción switch es igual al valor de la
19 expresión switch, el control se transfiere a la lista de instrucciones que están a continuación de dicha
20 etiqueta case.
21• Si ninguna de las constantes especificadas en las etiquetas case de una instrucción switch es igual al valor
22 de la expresión switch y existe una etiqueta default, el control se transfiere a la lista de instrucciones que
23 aparece a continuación de la etiqueta default.
24• Si ninguna de las constantes especificadas en las etiquetas case de una instrucción switch es igual al valor
25 de la expresión switch y no existe una etiqueta default, el control se transfiere al punto final de la
26 instrucción switch.
27Si el punto final de la lista de instrucciones de una sección de switch es alcanzable, se producirá un error en
28tiempo de compilación. Esto se conoce como regla “sin paso explícito”. En el ejemplo
29 sw i t ch ( i ) {
30 case 0 :
31 CaseZero ( ) ;
32 break ;
33 case 1 :
34 CaseOne( ) ;
35 break ;
36 de fau l t :
37 CaseOthe rs ( ) ;
40es válido porque ninguna sección de switch tiene un punto final alcanzable. A diferencia de C y C++, la
38
41ejecución de una sección switch no permite el “paso explícito” a la siguiente sección de switch, y el ejemplo
39

423Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 199


424Especificación del lenguaje C#

1 switch (i) {

2 case 0:

3 CaseZero();

4 case 1:

5 CaseZeroOrOne();
9da como resultado un error en tiempo de compilación. Para ejecutar una sección de switch después de la
106ejecución de otra sección de switch, debe utilizar una instrucción goto caseo goto de fau lexplícita:
t
7
11 switch (i) {
8
12 case 0:

13 CaseZero();

14 goto case 1;

15 case 1:

16 CaseZeroOrOne();

17 goto default;
22Una sección switch (switch-section) admite varias etiquetas. En el ejemplo
18
23 switch (i) {
19
24 case 0:
20
25 CaseZero();
21
26 break;

27 case 1:

28 CaseOne();

29 break;
35
30es válido. El ejemplo no viola la regla “sin paso explícito” porque las etiquetas case 2 : y default: forman
36parte de la misma sección de switch (switch-section).
31
37La regla “sin paso explícito” evita una clase de errores comunes que se producen en C y C++ cuando se omiten
32
38involuntariamente instrucciones break. Además, gracias a esta regla, las secciones de switch de una instrucción
39
33sw i t chse pueden reorganizar arbitrariamente sin afectar al comportamiento de la instrucción. Por ejemplo, las
40secciones de la instrucción switch anterior se pueden colocar en orden inverso sin modificar el comportamiento
34
41de la instrucción:

425200 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


426 Capítulo 18 Código no seguro

1 switch (i) {

2 default:

3 CaseAny();

4 break;

5 case 1:

6 CaseZeroOrOne();

7 goto default;
12La lista de instrucciones de una sección de switch termina normalmente con una instrucción break, goto case
138o goto default, pero también se admite cualquier sintaxis que convierta el punto final de la lista de
14instrucciones en inalcanzable. Por ejemplo, una instrucción while controlada mediante la expresión booleana
159true nunca alcanzará su punto final. Igualmente, una instrucción throw o return siempre transfiere el control a
16otra parte y nunca alcanza su punto final. Por tanto, el siguiente ejemplo es correcto:
10
11
17 switch (i) {

18 case 0:

19 while (true) F();

20 case 1:

21 throw new ArgumentException();


25El tipo aplicable en una instrucción switch puede ser el tipo string. Por ejemplo:
22
26
23 void DoCommand(string command) {

27
24 switch (command.ToLower()) {

28 case "run":

29 DoRun();

30 break;

31 case "save":

32 DoSave();

33 break;

34 case "quit":

35
42De la misma formaDoQuit();
que los operadores de igualdad (§7.9.7), la instrucción switch distingue mayúsculas de
43
36 minúsculas y ejecutará una determinada sección sólo si la cadena de expresión switch coincide exactamente con
44una constante de etiqueta case.
37
45Cuando el tipo aplicable en una instrucción switch es string, se admite el valor null como constante de etiqueta
38case.
46
47Las listas de instrucciones (statement-lists) de bloque switch (switch-block) puede contener instrucciones de
39
48declaración (§8.5). El ámbito de una variable o constante local declarada en un bloque switch es el propio
40
49bloque.
41

427Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 201


428Especificación del lenguaje C#

1Dentro de un bloque switch, el significado de un nombre utilizado en un contexto de expresión siempre debe ser
2el mismo (§7.5.2.1).
3La lista de instrucciones de una sección de switch determinada es alcanzable si la instrucción sw i t ches
4alcanzable y se cumple al menos una de las condiciones siguientes:
5• La expresión switch no es un valor constante.
6• La expresión switch es un valor de constante que coincide con una etiqueta case de la sección de switch.
7• La expresión switch es un valor de constante que no coincide con ninguna etiqueta case y la sección de
8 switch contiene la etiqueta de fau l .t
9• Una instrucción goto case o goto default alcanzable hace referencia a una etiqueta switch de la sección.
10El punto final de una instrucción sw i t ches alcanzable si se cumple al menos una de las siguientes condiciones:
11• La instrucción sw i t chcontiene una instrucción break alcanzable que provoca la salida de la instrucción
12 switch.
13• La instrucción switch es alcanzable, la expresión switch no es un valor constante y no existe etiqueta
14 default.
15• La instrucción switch es alcanzable, la expresión switch es una constante que no coincide con ninguna
16 etiqueta case y no existe etiqueta default.

429202 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


430 Capítulo 18 Código no seguro

18.8 Instrucciones de iteración


2Las instrucciones de iteración (iteration-statement) ejecutan repetidas veces una instrucción incrustada.
3 iteration-statement:
4 while-statement
5 do-statement
6 for-statement
7 foreach-statement

88.8.1 Instrucción While


9La instrucción whi l eejecuta una instrucción incrustada cero o varias veces dependiendo de una condición.
10 while-statement:
11 whi l e ( boolean-expression ) embedded-statement
12Una instrucción while se ejecuta de la siguiente forma:
13• Se evalúa la expresión booleana (boolean-expression) (§7.16).
14• Si la expresión booleana devuelve t rue, el control se transfiere a la instrucción incrustada. Cuando el
15 control alcanza el punto final de la instrucción incrustada (posiblemente desde la ejecución de una
16 instrucción cont inue), se transfiere al inicio de la instrucción while.
17• Si la expresión booleana devuelve false, el control se transfiere al punto final de la instrucción while.
18Dentro de la instrucción incrustada de la instrucción while, puede utilizar una instrucción break (§8.9.1) para
19transferir el control al punto final de la instrucción while (terminando así la iteración de la instrucción
20incrustada), y una instrucción continue (§8.9.2) para transferir el control al punto final de la instrucción
21incrustada (de esta forma se realizará otra iteración de la instrucción while).
22La instrucción incrustada de una instrucción while es alcanzable si la instrucción while es alcanzable y la
23expresión booleana no tiene el valor constante false.
24El punto final de una instrucción while es alcanzable si se cumple al menos una de las siguientes condiciones:
25• La instrucción while contiene una instrucción break alcanzable que provoca la salida de la instrucción
26 while.
27• La instrucción while es alcanzable y la expresión booleana no tiene el valor constante true.

288.8.2 Instrucción Do
29La instrucción do ejecuta una instrucción incrustada una o varias veces dependiendo de una condición.
30 do-statement:
31 do embedded-statement while ( boolean-expression ) ;
32Una instrucción do se ejecuta de la siguiente forma:
33• El control se transfiere a la instrucción incrustada.
34• Cuando el control alcanza el punto final de la instrucción incrustada (posiblemente desde la ejecución de
35 una instrucción continue), se evalúa la expresión booleana (boolean-expression) (§7.16). Si la expresión
36 booleana devuelve true, el control se transfiere al inicio de la instrucción do. En caso contrario, el control
37 se transfiere al punto final de la instrucción do.

431Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 203


432Especificación del lenguaje C#

1Dentro de la instrucción incrustada de la instrucción do, puede utilizar una instrucción break (§8.9.1) para
2transferir el control al punto final de la instrucción do (terminando así la iteración de la instrucción incrustada),
3y una instrucción continue (§8.9.2) para transferir el control al punto final de la instrucción incrustada.
4La instrucción incrustada de una instrucción do es alcanzable si la instrucción do es alcanzable.
5El punto final de una instrucción es alcanzable si se cumple al menos una de las siguientes condiciones:
6• La instrucción do contiene una instrucción break alcanzable que provoca la salida de la instrucción do.
7• El punto final de la instrucción incrustada es alcanzable y la expresión booleana no tiene el valor constante
8 true.

98.8.3 Instrucción For


10La instrucción for evalúa primero una secuencia de expresiones de inicialización y, mientras se cumpla una
11determinada condición, ejecuta repetidas veces una instrucción incrustada y evalúa una secuencia de expresiones
12de iteración.
13 for-statement:
14 for ( for-initializeropt ; for-conditionopt ; for-iteratoropt ) embedded-statement
15 for-initializer:
16 local-variable-declaration
17 statement-expression-list
18 for-condition:
19 boolean-expression
20 for-iterator:
21 statement-expression-list
22 statement-expression-list:
23 statement-expression
24 statement-expression-list , statement-expression
25El inicializador de for (for-initializer), si existe, es una declaración de variable local (local-variable-declaration)
26(§8.5.1) o una lista de expresiones de instrucción (statement-expressions) (§8.6) separadas por comas. El ámbito
27de una variable local declarada por un inicializador for (for-initializer) se inicia en el declarador de variable
28local (local-variable-declarator) y se extiende hasta el final de la instrucción incrustada. El ámbito incluye la
29condición de for (for-condition) y el iterador de for (for-iterator).
30La condición de for (for-condition), si existe, debe ser una expresión booleana (boolean-expression) (§7.16).
31El iterador de for (for-iterator), si existe, consiste en una lista de expresiones de instrucción (statement-
32expressions) (§8.6) separadas por comas.
33Una instrucción for se ejecuta de la siguiente forma:
34• Si existe un inicializador de for, se ejecutan los inicializadores de variable o las expresiones de instrucción
35 en el orden en el que se hayan codificado. Este paso sólo se realiza una vez.
36• Si existe una condición de for, se evalúa.
37• Si no existe una condición de for (for-condition), o existe y la evaluación devuelve true, el control se
38 transfiere a la instrucción incrustada. Cuando el control alcanza el punto final de la instrucción incrustada
39 (posiblemente desde la ejecución de una instrucción continue), las expresiones del iterador de for (for-
40 iterator), si existen, se evalúan en secuencia y, a continuación, se realiza una nueva iteración empezando por
41 la evaluación de la condición for (for-condition), como se describe en el paso anterior.

433204 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


434 Capítulo 18 Código no seguro

1• Si existe una condición de for (for-condition) y devuelve f a l se


, el control se transfiere al punto final de la
2 instrucción f o r.
3Dentro de la instrucción incrustada de una instrucción f o r, se puede utilizar una instrucción break (§8.9.1) para
4transferir el control al punto final de la instrucción for (terminando así la iteración de la instrucción incrustada),
5y una instrucción continue (§8.9.2) para transferir el control al punto final de la instrucción incrustada (de esta
6manera, se ejecuta el iterador de for (for-iterator) y se realiza otra iteración de la instrucción for, empezando por
7la condición de for (for-condition).
8La instrucción incrustada de una instrucción f o res alcanzable si se cumple alguna de las condiciones siguientes:
9• La instrucción for es alcanzable y no hay ninguna condición de for (for-condition).
10• La instrucción for es alcanzable y hay una condición de for (for-condition), pero ésta última no tiene el
11 valor constante false.
12El punto final de una instrucción for es alcanzable si se cumple al menos una de las siguientes condiciones:
13• La instrucción for contiene una instrucción break alcanzable que provoca la salida de la instrucción for.
14• La instrucción for es alcanzable y hay una condición de for (for-condition), pero ésta última no tiene el
15 valor constante true.

168.8.4 Instrucción Foreach


17La instrucción f o reachenumera los elementos de una colección, ejecutando una instrucción incrustada para
18cada elemento de la colección.
19 foreach-statement:
20 foreach ( type identifier in expression ) embedded-statement
21El tipo (type) y el identificador (identifier) de una instrucción foreach declaran la variable de iteración de la
22instrucción. La variable de iteración es una variable local de sólo lectura con un ámbito que se extiende a lo
23largo de toda la instrucción incrustada. Durante la ejecución de una instrucción foreach, la variable de iteración
24representa el elemento de colección para el que se está realizando la iteración en ese momento. Se producirá un
25error en tiempo de compilación si la instrucción incrustada intenta modificar la variable de iteración (por medio
26de asignación o utilizando los operadores ++ y --) o bien si la pasa como un parámetro ref o out.
27El tipo de la expresión (expression) de una instrucción foreach debe ser un tipo de colección (como se define
28más adelante), y debe existir una conversión explícita (§6.2) del tipo del elemento de la colección al tipo de la
29variable de iteración. Si la expresión (expression) tiene el valor null, se inicia una excepción
30System.NullReferenceException.
31Un tipo C es un tipo de colección si implementa la interfaz Sys tem.Co l l ec t i ons . I Enumerabole
implementa
32el diseño de colección cumpliendo todos los criterios siguientes:
33• C contiene un método de instancia public con la firma GetEnumerator() que devuelve un tipo struct
34 (struct-type), tipo clase (class-type) o tipo interfaz (interface-type), que se denomina E en el texto siguiente.
35• E contiene un método de instancia pub l i ccon la firma MoveNext() y el tipo de valor devuelto bool.

36• E contiene una propiedad de instancia public denominada Current que permite la lectura del valor actual.
37 Se dice que el tipo de esta propiedad es el tipo de elemento del tipo de colección.
38Un tipo que implementa IEnumerable también es un tipo de colección, aun cuando no satisface las condiciones
39anteriores. Esto es posible si implementa alguno de los miembros IEnumerable por medio de una
40implementación de miembro de interfaz explícita, como se describe en §13.4.1.)

435Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 205


436Especificación del lenguaje C#

1El tipo Sys tem.Ar ray(§12.1.1) es un tipo de colección y, puesto que todos los tipos de matriz se derivan de
2Sys tem.Ar ray, en una instrucción foreach se admite cualquier expresión de tipo de matriz. El orden en que
3foreach recorre los elementos de una matriz es el siguiente: en matrices unidimensionales, los elementos se
4recorren en orden creciente, empezando por el índice 0 y acabando por el índice Length – 1. Los elementos de
5matrices multidimensionales se recorren de manera que los índices de la dimensión del extremo derecho se
6incrementan en primer lugar, a continuación la dimensión situada inmediatamente a su izquierda y así
7sucesivamente hacia la izquierda.
8Una instrucción f o reachde la forma:
9 f o reach (E lement Type e lement i n co l l ec t i on ) s ta tement
10corresponde a una de dos posibles expansiones:
11• Si la expresión co l l ec t i on
es de un tipo que implementa el diseño de colección (como se ha definido
12 anteriormente), la expansión de la instrucción f o reaches:
13 E enumera to r = ( co l l ec t i on ) .Ge tEnumera to r ( ) ;
14 t ry {
15 E lement Type e lement ;
16 whi l e (enumera to r .MoveNext ( ) ) {
17 e lement = (E lement Type )enumera to r .Cu r ren t ;
18 s ta tement ;
19 }
20 }
21 f i na l l y {
22
25 ID i sposab
Optimizaciones le d i sposab
significativas de lo le = enu
anterior mera to
pueden r as Syscon
conseguirse tem. ID i sposab
bastante le ; Si el tipo E
facilidad.
23
26 implementa Sys tem. ID i sposab le , la expresión (enumerator as System.IDisposable) siempre será
27
24 distinta de null y la implementación puede sustituir de forma segura una sencilla conversión por una
28 comprobación de tipo posiblemente más costosa. A la inversa, si el tipo E es sealed y no implementa
29 System.IDisposable, la expresión (enumerator as System.IDisposable) siempre se evaluará como
30 null. En este caso, la implementación puede optimizar de forma segura toda la cláusula finally.
31• En caso contrario, la expresión collection es de un tipo que implementa System.IEnumerable y la
32 expansión de la instrucción foreach es:
33 I Enumera to r enumera to r =
34 ( (Sys tem.Co l l ec t i ons . I Enumerab le ) ( co l l ec t i on ) ) .Ge tEnumera to r ( ) ;
35 t ry {
36 E lement Type e lement ;
37 whi l e (enumera to r .MoveNext ( ) ) {
38 e lement = (E lement Type )enumera to r .Cu r ren t ;
39 s ta tement ;
40 }
41 }
42 f i na l l y {
43En cualquiera de las expansiones, enumerator es una variable temporal a la que no puede obtenerse acceso (ni
46
47es visible) en la instrucción statement incrustada; además, la variable element es de sólo lectura en esta
44
48
45instrucción (statement) incrustada.

437206 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


438 Capítulo 18 Código no seguro

1El código del siguiente ejemplo muestra cada valor de una matriz bidimensional según el orden de los
2elementos:

3 using System;
4 class Test

5 {

6 static void Main() {

7 double[,] values = {

8 {1.2, 2.3, 3.4, 4.5},


11 foreach (double elementValue in values)
9
12 Console.Write("{0} ", elementValue);
13
10 Console.WriteLine();

14 }
16
15 El resultado producido es el siguiente:

17 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9

188.9 Instrucciones Jump


19Las instrucciones de salto (jump-statement) transfieren el control de forma incondicional.
20 jump-statement:
21 break-statement
22 continue-statement
23 goto-statement
24 return-statement
25 throw-statement
26La posición a la que transfiere el control una instrucción de salto se denomina destino de la instrucción de salto.
27Cuando se ejecuta una instrucción de salto dentro de un bloque y el destino se encuentra fuera del bloque, se
28dice que la instrucción de salto sale del bloque. Aunque una instrucción de salto puede transferir el control fuera
29de un bloque, no puede transferirlo dentro del bloque.
30La ejecución de las instrucciones de salto se complica cuando intervienen instrucciones t r y. Si no existen
31instrucciones t r y, la instrucción de salto transfiere incondicionalmente el control a su destino. Pero si existen
32instrucciones try, la ejecución es más compleja. Si la instrucción de salto sale de uno o varios bloques try que
33llevan asociados bloques finally, el control se transfiere inicialmente al bloque finally de la instrucción try más
34interna. Cuando el control alcanza el punto final de un bloque finally, el control se transfiere al bloque finally
35de la siguiente instrucción envolvente try. Este proceso se repite hasta que se ejecuten los bloques finally de
36todas las instrucciones try que intervienen.

439Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 207


440Especificación del lenguaje C#

1En el siguiente ejemplo:

2 using System;
3 class Test

4 {

5 static void Main() {

6 while (true) {

7 try {

8 try {

9 Console.WriteLine("Before break");

10 break;

11 }

12 finally {

13 Console.WriteLine("Innermost finally block");

14 }
23los bloques f i na l lasociados
y a las dos instrucciones t ryse ejecutan antes de transferir el control al destino de la
15instrucción de salto.
24
16El resultado producido es el siguiente:
25
17
26 Before break
18
27 Innermost finally block
19
28 Outermost finally block
208.9.1 Instrucción Break
30
29
31La instrucción break provoca la salida de la instrucción envolvente sw i t ch, while, do, for o foreach más
21
32próxima.
22
33 break-statement:
34 break ;
35El destino de una instrucción break es el punto final de la instrucción envolvente switch, while, do, for o
36foreach más próxima. Si la instrucción break no se encuentra dentro de una instrucción switch, while, do, for
37o foreach, se produce un error en tiempo de compilación.
38Cuando existen varias instrucciones switch, while, do, for o foreach anidadas, la instrucción break se aplica
39sólo a la instrucción más interna. Para transferir el control a través de varios niveles de anidamiento, debe
40utilizar una instrucción goto (§8.9.3).
41Una instrucción break no puede salir de un bloque f i na l l(§8.10).
y Cuando se ejecuta una instrucción break
42dentro de un bloque finally, el destino de la instrucción break debe encontrarse dentro del propio bloque
43finally; en caso contrario, se producirá un error en tiempo de compilación.
44Una instrucción break se ejecuta de la siguiente forma:
45• Si la instrucción break sale de uno o más bloques try que llevan asociados bloques finally, el control se
46 transfiere inicialmente al bloque finally de la instrucción try más interna. Cuando el control alcanza el

441208 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


442 Capítulo 18 Código no seguro

1 punto final de un bloque f i na l ,l yel control se transfiere al bloque f i na l lde


y la siguiente instrucción
2 envolvente try. Este proceso se repite hasta que se ejecuten los bloques finally de todas las instrucciones try
3 que intervienen.
4• El control se transfiere al destino de la instrucción break.
5Como una instrucción break transfiere incondicionalmente el control a otra parte del código, el punto final de
6una instrucción break nunca es alcanzable.

78.9.2 Instrucción Statement


8La instrucción continue inicia una nueva iteración de la instrucción envolvente while, do, for o foreach más
9próxima.
10 continue-statement:
11 cont inue ;
12El destino de una instrucción continue es el punto final de la instrucción incrustada de la instrucción
13envolvente while, do, for o foreach más próxima. Si la instrucción continue no se encuentra dentro de una
14instrucción while, do, for o foreach, se produce un error en tiempo de compilación.
15Cuando existen varias instrucciones while, do, for o foreach anidadas, la instrucción continue se aplica sólo a
16la instrucción más interna. Para transferir el control a través de varios niveles de anidamiento, debe utilizar una
17instrucción goto (§8.9.3).
18Una instrucción cont inueno puede salir de un bloque f i na l l(§8.10).
y Cuando se ejecuta una instrucción
19continue dentro de un bloque finally, el destino de la instrucción continue debe encontrarse dentro del propio
20bloque finally; en caso contrario, se producirá un error en tiempo de compilación.
21Una instrucción continue se ejecuta de la siguiente forma:
22• Si la instrucción continue sale de uno o más bloques try que llevan asociados bloques finally, el control se
23 transfiere inicialmente al bloque finally de la instrucción try más interna. Cuando el control alcanza el
24 punto final de un bloque finally, el control se transfiere al bloque finally de la siguiente instrucción
25 envolvente try. Este proceso se repite hasta que se ejecuten los bloques finally de todas las instrucciones try
26 que intervienen.
27• El control se transfiere al destino de la instrucción continue.
28Como una instrucción continue transfiere incondicionalmente el control a otra parte del código, el punto final
29de una instrucción continue nunca es alcanzable.

308.9.3 Instrucción Goto


31La instrucción goto transfiere el control a una instrucción marcada con una etiqueta.
32 goto-statement:
33 goto identifier ;
34 goto case constant-expression ;
35 goto default ;
36El destino de una instrucción goto identificador (identifier) es la instrucción con etiqueta con el nombre de
37dicha etiqueta. Si no existe una etiqueta con dicho nombre en el miembro de función actual, o si la instrucción
38goto no se encuentra dentro del ámbito de la etiqueta, se produce un error en tiempo de compilación. Esta regla
39permite el uso de una instrucción goto para transferir el control fuera de un ámbito anidado, pero no en un
40ámbito anidado. En el siguiente ejemplo:
41 us ing Sys tem;

443Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 209


444Especificación del lenguaje C#

1 class Test

2 {

3 static void Main(string[] args) {

4 string[,] table = {

5 {"Red", "Blue", "Green"},


8 foreach (string str in args) {
6
9 int row, colm;
7
10 for (row = 0; row <= 1; ++row)

11 for (colm = 0; colm <= 2; ++colm)


14
12 Console.WriteLine("{0} not found", str);

15
13 continue;

16 done:

17 Console.WriteLine("Found {0} at [{1}][{2}]", str, row, colm);

18se utiliza una instrucción


21 } goto para transferir el control fuera de un ámbito anidado.
22
19El destino de una instrucción goto casees la lista de instrucciones de la instrucción sw i t chinmediatamente
23envolvente (§8.7.2), que contiene una etiqueta case con el valor constante dado. Si la instrucción goto case no
20
24está dentro de una instrucción switch, la expresión constante (constant-expression) no es convertible
25implícitamente (§6.1) al tipo aplicable en la instrucción envolvente switch más próxima, o si dicha instrucción
26switch no contiene una etiqueta case con el valor constante dado, se produce un error en tiempo de
27compilación.
28El destino de una instrucción goto de fau lest la lista de instrucciones de la instrucción sw i t chinmediatamente
29envolvente (§8.7.2), que contiene una etiqueta default. Si la instrucción goto default no se encuentra dentro
30de una instrucción switch o la instrucción envolvente switch más próxima no contiene una etiqueta default, se
31produce un error en tiempo de compilación.
32Una instrucción goto no puede salir de un bloque finally (§8.10). Cuando se ejecuta una instrucción goto
33dentro de un bloque finally, el destino de la instrucción goto debe encontrarse dentro del propio bloque finally;
34en caso contrario, se producirá un error en tiempo de compilación.
35Una instrucción goto se ejecuta de la siguiente forma:
36• Si la instrucción goto sale de uno o más bloques try que llevan asociados bloques finally, el control se
37 transfiere inicialmente al bloque finally de la instrucción try más interna. Cuando el control alcanza el
38 punto final de un bloque finally, el control se transfiere al bloque finally de la siguiente instrucción
39 envolvente try. Este proceso se repite hasta que se ejecuten los bloques finally de todas las instrucciones try
40 que intervienen.
41• El control se transfiere al destino de la instrucción goto.
42Como una instrucción goto transfiere incondicionalmente el control a otra parte del código, el punto final de
43una instrucción goto nunca es alcanzable.

448.9.4 Instrucción Return


45La instrucción return devuelve el control al llamador del miembro de función en el que aparece la instrucción
46return.

445210 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


446 Capítulo 18 Código no seguro

1 return-statement:
2 re tu rn expressionopt ;
3Una instrucción re tu rnsin expresión sólo puede utilizarse en un miembro de función que no calcule un valor, es
4decir, un método con tipo de valor devuelto vo id, un descriptor de acceso set de una propiedad o indizador,
5descriptores de acceso add y remove de un evento, un constructor de instancia, un constructor estático o un
6destructor.
7Una instrucción return con expresión se puede utilizar sólo en un miembro de función que calcule un valor, es
8decir, un método con tipo de valor devuelto que no sea void, el descriptor de acceso get de una propiedad o
9indizador o un operador definido por el usuario. Debe existir una conversión implícita (§6.1) del tipo de la
10expresión al tipo de valor devuelto por el miembro de función que la contiene.
11Se produce un error en tiempo de compilación cuando aparece una instrucción return en un bloque finally
12(§8.10).
13Una instrucción re tu rnse ejecuta de la siguiente forma:
14• Si la instrucción return especifica una expresión, se evalúa la expresión y el valor devuelto se convierte al
15 tipo de valor devuelto por el miembro de función que la contiene mediante una conversión implícita. El
16 resultado de la conversión se convierte en el valor devuelto al llamador.
17• Si la instrucción return está encerrada entre uno o varios bloques try que llevan asociados bloques finally,
18 el control se transfiere inicialmente al bloque finally de la instrucción try más interna. Cuando el control
19 alcanza el punto final de un bloque finally, el control se transfiere al bloque finally de la siguiente
20 instrucción envolvente try. Este proceso se repite hasta que se ejecuten los bloques finally de todas las
21 instrucciones try envolventes.
22• El control se devuelve al llamador del miembro de función que lo contiene.
23Como una instrucción re tu rntransfiere incondicionalmente el control a otra parte del código, el punto final de
24una instrucción re tu rnnunca es alcanzable.

258.9.5 Instrucción Throw


26La instrucción throw inicia una excepción.
27 throw-statement:
28 th row expressionopt ;
29Una instrucción throw con expresión inicia el valor resultante de evaluar la expresión. La expresión debe
30denotar un valor del tipo de clase System.Exception o de un tipo de clase que derive de System.Exception.
31Si la expresión evaluada devuelve null, se inicia entonces System.NullReferenceException.
32Una instrucción throw sin expresión sólo se puede utilizar en un bloque catch, en cuyo caso esta instrucción
33volverá a iniciar la excepción que esté controlando en ese momento el bloque catch.
34Como una instrucción th row transfiere incondicionalmente el control a otra parte del código, el punto final de
35una instrucción th row nunca es alcanzable.
36Cuando se inicia una excepción, el control se transfiere a la primera cláusula catch de una instrucción try
37envolvente que pueda controlar la excepción. El proceso que tiene lugar desde el punto de inicio de la excepción
38hasta el punto en que se transfiere el control a un controlador de excepciones adecuado es conocido con el
39nombre de propagación de excepción. La propagación de una excepción consiste en evaluar repetidamente los
40siguientes pasos hasta encontrar una cláusula catch que coincida con la excepción. En esta descripción, el
41punto de inicio es la ubicación desde la que se inicia la excepción.

447Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 211


448Especificación del lenguaje C#

1• En el miembro de función actual, se examina cada instrucción t ryque envuelve al punto de inicio. Se
2 evalúan los siguientes pasos para cada instrucción S, comenzando con la instrucción try más interna y
3 terminando con la más externa:
4 o Si el bloque try de S encierra al punto de inicio y S tiene una o varias cláusulas catch, se examinan las
5 cláusulas catch en orden de aparición hasta encontrar un controlador adecuado para la excepción. La
6 primera cláusula catch que especifique el tipo de excepción o un tipo base del tipo de excepción se
7 considera una coincidencia. Una cláusula catch general (§8.10) es una coincidencia para cualquier tipo
8 de excepción. Si se encuentra una cláusula catch coincidente, termina la propagación de excepción y se
9 transfiere el control al bloque de la cláusula catch.
10 o En caso contrario, si el bloque try o un bloque catch de S encierra el punto de inicio y S tiene un
11 bloque finally, el control se transfiere al bloque finally. Si el bloque finally inicia otra excepción,
12 finaliza el procesamiento de la excepción actual. Si no, cuando el control alcanza el punto final del
13 bloque finally, continúa con el procesamiento de la excepción actual.
14• Si no se encontró un controlador de excepciones en la llamada del miembro de función actual, finaliza la
15 llamada al miembro de función. Los pasos anteriores se repiten para el llamador del miembro de función
16 con el punto de inicio que corresponda a la instrucción desde la que se invocó al miembro de función.
17• Si el procesamiento de la excepción finaliza todas las llamadas a miembros de función del subproceso actual
18 indicando que el subproceso no ha encontrado controlador para la excepción, dicho subproceso finaliza. El
19 impacto de esta terminación se define según la implementación.

208.10 Instrucción Try


21La instrucción try proporciona un mecanismo para capturar las excepciones que ocurren durante la ejecución de
22un bloque. La instrucción try permite además especificar un bloque de código que siempre se ejecuta cuando el
23control abandona la instrucción try.
24 try-statement:
25 t r y block catch-clauses
26 t r y block finally-clause
27 try block catch-clauses finally-clause
28 catch-clauses:
29 specific-catch-clauses general-catch-clauseopt
30 specific-catch-clausesopt general-catch-clause
31 specific-catch-clauses:
32 specific-catch-clause
33 specific-catch-clauses specific-catch-clause
34 specific-catch-clause:
35 catch ( class-type identifieropt ) block
36 general-catch-clause:
37 catch block
38 finally-clause:
39 finally block
40Existen tres formas posibles de instrucciones try:
41• Un bloque try seguido de uno o varios bloques catch.
42• Un bloque try seguido de un bloque finally.

449212 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


450 Capítulo 18 Código no seguro

1• Un bloque t r yseguido de uno o varios bloques ca tchseguidos de un bloque finally.


2Cuando una cláusula ca tch especifica un tipo de clase (class-type), el tipo debe ser Sys tem.Except i ono un
3tipo que derive de System.Exception.
4Cuando una cláusula catch especifica tanto un tipo de clase (class-type) como un identificador (identifier), se
5declara una variable de excepción con el nombre y tipo dados. La variable de excepción es una variable local
6con un ámbito que se extiende a lo largo de todo el bloque catch. Durante la ejecución de un bloque catch, la
7variable de excepción representa la excepción que se está controlando en ese momento. Desde el punto de vista
8de comprobación de asignación definitiva, la variable de excepción se considera asignada definitivamente en
9todo su ámbito.
10Si la cláusula catch no incluye el nombre de una variable de excepción, no es posible tener acceso al objeto de
11excepción en el bloque catch.
12Una cláusula catch general es una cláusula catch que no especifica el tipo ni la variable de excepción. Una
13instrucción try sólo puede tener una cláusula catch general y, si existe, debe ser la última cláusula catch.
14Algunos lenguajes de programación pueden aceptar excepciones que no son representables como un objeto
15derivado de System.Exception, aunque estas excepciones nunca pueden ser generadas por código C#. Para
16detectar este tipo de excepciones se puede utilizar una cláusula catch general. Por lo tanto, una cláusula catch
17general es semánticamente diferente de otra que especifica el tipo System.Exception, ya que la primera
18también puede detectar excepciones de otros lenguajes.
19Las cláusulas catch se examinan en orden léxico para encontrar un controlador para la excepción. Si una
20cláusula catch especifica un tipo que es igual o derivado de un tipo especificado en una cláusula catch anterior
21de la misma instrucción try, se produce un error en tiempo de compilación. Si no existiera esta restricción, sería
22posible escribir cláusulas catch inalcanzables.
23Dentro de un bloque catch, puede utilizar una instrucción throw (§8.9.5) sin expresión para volver a iniciar la
24excepción que capturó el bloque catch. Las asignaciones a una variable de excepción no modifican la
25excepción que vuelve a iniciarse.
26En el siguiente ejemplo:
27 us ing Sys tem;
28 c lass Tes t
29 {
30 s ta t i c vo id F ( ) {
31 t ry {
32 G( ) ;
33 }
34 ca tch (Excep t i on e ) {
35 Conso le .Wr i teL ine ( "Except i on i n F : " + e .Message ) ;
36 e = new Except i on ( "F " ) ;
37 th row; / / re - th row
40 s ta t i c vo id G( ) {
38
41 th row new Except i on ( "G" ) ;
39
42 }

451Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 213


452Especificación del lenguaje C#

1 static void Main() {

2 try {

3 F();

4 }

5 catch (Exception e) {

106el método F captura Console.WriteLine("Exception in Main: en


una excepción, escribe información de diagnóstico " la
+ consola,
e.Message);
modifica la variable de
117excepción y vuelve a iniciar la excepción. La excepción que se vuelve a iniciar es la excepción original, de
12modo que el resultado producido es el siguiente:
8
13 Exception in F: G
9
14Si el primer
15 Exception in iniciara
bloque catch Main: e G en lugar de volver a iniciar la excepción actual, el resultado producido sería
16el siguiente:

17 Exception in F: G

18Se produce
19 Exception
un error enintiempo
Main: F
de compilación cuando una instrucción break, cont inueo goto transfiere el
20control fuera del bloque finally. Cuando ocurre una instrucción break, continue o goto dentro de un bloque
21finally, el destino de la instrucción debe encontrarse dentro del propio bloque finally. En caso contrario, se
22producirá un error en tiempo de compilación.
23También se produce un error en tiempo de compilación si una instrucción return ocurre dentro de un bloque
24finally.
25Una instrucción t r yse ejecuta de la siguiente forma:
26• El control se transfiere al bloque try.
27• Cuando el control alcanza el punto final del bloque t r y:
28 o Si la instrucción try tiene un bloque finally, se ejecuta dicho bloque finally.
29 o El control se transfiere al punto final de la instrucción try.
30• Si se propaga una excepción a la instrucción t rydurante la ejecución del bloque t r y:
31 o Si existen cláusulas catch, se examinan en orden de aparición para buscar un controlador adecuado para
32 la excepción. La primera cláusula catch que especifique el tipo de excepción o un tipo base del tipo de
33 excepción se considera una coincidencia. Una cláusula catch general es una coincidencia para cualquier
34 tipo de excepción. Si se encuentra una cláusula catch coincidente:
35 • Si la cláusula catch coincidente declara una variable de excepción, se asigna el objeto de excepción
36 a dicha variable.
37 • El control se transfiere al bloque catch coincidente.
38 • Cuando el control alcanza el punto final del bloque ca tch:
39 o Si la instrucción try tiene un bloque finally, se ejecuta dicho bloque finally.
40 o El control se transfiere al punto final de la instrucción try.
41 • Si se propaga una excepción a la instrucción t rydurante la ejecución del bloque ca tch:

453214 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


454 Capítulo 18 Código no seguro

1 o Si la instrucción t rytiene un bloque f i na l ,l yse ejecuta dicho bloque finally.


2 o La excepción se propaga a la siguiente instrucción try envolvente.
3 o Si la instrucción try no tiene cláusulas catch o si ninguna cláusula catch coincide con la excepción:
4 • Si la instrucción try tiene un bloque finally, se ejecuta dicho bloque finally.
5 • La excepción se propaga a la siguiente instrucción try envolvente.
6Las instrucciones de un bloque finally siempre se ejecutan cuando el control abandona la instrucción try. Esto
7se cumple cuando el control se transfiere como resultado de una ejecución normal, de la ejecución de una
8instrucción break, continue, goto o return, o de la propagación de una excepción fuera de la instrucción try.

455Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 215


456Especificación del lenguaje C#

1Si se vuelve a iniciar una excepción durante la ejecución de un bloque f i na l ,l yla excepción se propaga a la
2siguiente instrucción t r yenvolvente. Si se estaba propagando otra excepción anterior, ésta se pierde. El proceso
3de propagación de una excepción se explica con mayor detalle en la descripción de la instrucción throw
4(§8.9.5).
5El bloque t r yde una instrucción t r yes alcanzable si la instrucción try es alcanzable.
6El bloque catch de una instrucción try es alcanzable si la instrucción try es alcanzable.
7El bloque finally de una instrucción try es alcanzable si la instrucción try es alcanzable.
8El punto final de una instrucción try es alcanzable si se cumplen las dos condiciones siguientes:
9• El punto final de un bloque try es alcanzable o el punto final de al menos uno de sus bloques catch es
10 alcanzable.
11• Si existe un bloque finally, el punto final del bloque finally es alcanzable.

128.11 Instrucciones Checked y Unchecked


13Las instrucciones checked y unchecked se utilizan con el fin de controlar el contexto de comprobación de
14desbordamiento para operaciones aritméticas y conversiones.
15 checked-statement:
16 checked block
17 unchecked-statement:
18 unchecked block
19Con la instrucción checked, todas las expresiones del bloque (block) se evalúan en un contexto de
20comprobación y, con la instrucción unchecked, en un contexto donde no se realicen comprobaciones.
21Las instrucciones checked y unchecked son equivalentes a los operadores checked y unchecked (§7.5.12),
22con la excepción de que operan con bloques en lugar de con expresiones.

238.12 Instrucción Lock


24La instrucción lock bloquea un objeto mediante exclusión mutua, ejecuta una instrucción y, a continuación,
25libera el bloqueo.
26 lock-statement:
27 lock ( expression ) embedded-statement
28La expresión de una instrucción lock debe denotar un valor de tipo de referencia (reference-type). Nunca se
29realiza una conversión boxing implícita (§6.1.5) para la expresión de una instrucción lock y, por lo tanto, se
30produce un error en tiempo de compilación cuando la expresión denota un valor de un tipo de valor (value-type).
31Una instrucción lock de la forma
32 l ock ( x ) ...
33donde x es una expresión de tipo de referencia (reference-type), es equivalente a
34 Sys tem.Th read ing .Mon i to r . En te r (x ) ;
35 t ry {
36 ...
37 }
38 finally {
39 System.Threading.Monitor.Exit(x);
40 }

457216 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


458 Capítulo 18 Código no seguro

1salvo que x sólo se evalúa una vez.


2Mientras esté activo un bloqueo de exclusión mutua, el código que está ejecutándose en el mismo subproceso
3también puede obtener y liberar tal bloqueo. Sin embargo, el código que esté ejecutándose en otros subprocesos
4no puede recibir el bloqueo hasta que el éste se libere.
5Se recomienda no bloquear objetos de tipo para sincronizar el acceso a datos estáticos. Otro código, sobre el
6que no tiene ningún control, podría bloquear también el tipo de clase. Esto puede provocar un interbloqueo. En
7su lugar, sincronice el acceso a datos estáticos bloqueando un objeto estático privado. Por ejemplo:

8 class Cache

9 {

10
11 public static void Add(object x) {

12 lock (Cache.synchronizationObject) {
13 ...
14 }
15
16 public static void Remove(object x) {

17 lock (Cache.synchronizationObject) {
18 ...
19 }
20 }
21
228.13 Instrucción Using
23La instrucción us ingobtiene uno o varios recursos, ejecuta una instrucción y, a continuación, elimina el recurso.
24 using-statement:
25 using ( resource-acquisition ) embedded-statement
26 resource-acquisition:
27 local-variable-declaration
28 expression
29Un recurso es una clase o estructura que implementa Sys tem. ID i sposab le
, que incluye un único método sin
30parámetros denominado Dispose. El código que utiliza un recurso puede llamar a Dispose para indicar que ya
31no lo necesita. Si no llama a Dispose, se elimina temporalmente de forma automática como consecuencia de la
32recolección de elementos no utilizados.
33Si la adquisición de recurso (resource-acquisition) se realiza mediante una declaración de variable local (local-
34variable-declaration), el tipo de ésta última debe ser System.IDisposable o un tipo que pueda ser convertido
35implícitamente a System.IDisposable. Si la adquisición de recurso (resource-acquisition) se realiza mediante
36una expresión (expression), el tipo de la expresión debe ser System.IDisposable o un tipo que pueda ser
37convertido implícitamente a System.IDisposable.
38Las variables locales que se declaran en la adquisición de recurso (resource-acquisition) son de sólo lectura y
39deben incluir un inicializador. Se producirá un error en tiempo de compilación si la instrucción incrustada
40intenta modificar estas variables locales (por medio de asignación o utilizando los operadores ++ y --) o bien si
41obtiene su dirección o las pasa como parámetros ref o out.
42Una instrucción using se traduce en tres partes: adquisición, uso y eliminación. El uso de un recurso está
43incluido implícitamente en una instrucción try que contiene una cláusula finally. La cláusula finally elimina el
44recurso. Si se obtiene un recurso null, no se llama a Dispose y no se inicia ninguna excepción.

459Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 217


460Especificación del lenguaje C#

1Una instrucción us ingde la forma


2 us ing (Resou rceType resource = express ion ) s ta tement
3corresponde a una de dos posibles expansiones. Cuando ResourceTypees un tipo de valor, la expansión es:
4 {
5 ResourceType resource = express ion ;
6 t ry {
7 s ta tement ;
8 }
9 f i na l l y {
10 ( ( ID i sposab le ) resource ) .D i spose ( ) ;
11En caso contrario, cuando ResourceTypees un tipo de referencia, la expansión es:
13
12
14 {
15 ResourceType resource = express ion ;
16 t ry {
17 s ta tement ;
18 }
19 f i na l l y {
20 i f ( resource != nu l l ) ( ( ID i sposab le ) resource ) .D i spose ( ) ;
21En ambas expansiones, la variable resou rcees de sólo lectura en la instrucción incrustada.
23
22
24Una instrucción us ingde la forma
25 us ing ( exp ress i on ) s ta tement
26tiene las dos mismas posibilidades de expansión, pero en este caso ResourceTypees implícitamente el tipo en
27tiempo de compilación de express ion, y la variable resource no es accesible (ni visible) en la instrucción
28incrustada.
29Cuando una adquisición de recurso (resource-acquisition) toma la forma de una declaración de variable local
30(local-variable-declaration), es posible adquirir múltiples recursos de un tipo dado. Una instrucción us ingde la
31forma
32 us ing (Resou rceType r1 = e1 , r2 = e2 , . . . , rN = eN) s ta tement
33es perfectamente equivalente a una secuencia de instrucciones using anidadas.
34 us ing (Resou rceType r1 = e1)
35 us ing (Resou rceType r2 = e2)
36 ...
37 us ing (Resou rceType rN = eN)
38
39En el ejemplo siguiente se crea un archivo llamado log.txt y se escriben dos líneas de texto en él.
40Posteriormente, se abre el mismo archivo para leerlo y copiar las líneas de texto que contiene en la consola.
41 us ing Sys tem;
42 us ing Sys tem. IO ;

461218 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


462 Capítulo 18 Código no seguro

1 class Test

2 {

3 static void Main() {

4 using (TextWriter w = File.CreateText("log.txt")) {

5 w.WriteLine("This is line one");


8 using (TextReader r = File.OpenText("log.txt")) {
6
9 string s;
7
10 while ((s = r.ReadLine()) != null) {

11
13 }
12
14 }
16Dado que las clases Tex tWr i te yr Tex tReaderimplementan la interfaz IDisposable, en el ejemplo se pueden
15
17utilizar instrucciones using para garantizar que el archivo subyacente esté correctamente cerrado después de las
18operaciones de lectura o escritura.

463Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 219


464Especificación del lenguaje C#

1 9.Espacios de nombres

2Los programas de C# se organizan utilizando espacios de nombres. Los espacios de nombres se utilizan, tanto
3como sistema de organización “interna” de un programa, como sistema de organización “externa”; es una forma
4de presentar los elementos de un programa que están expuestos a otros programas.
5Las directivas using (§9.3) se proporcionan para facilitar el uso de los espacios de nombres.

69.1 Unidades de compilación


7Una unidad de compilación (compilation-unit) define la estructura completa de un archivo de código fuente.
8Consiste en cero o varias directivas using (using-directives), seguidas de cero o varios atributos globales
9(global-attributes), seguidos de cero o varias declaraciones de miembro espacio de nombres (namespace-
10member-declarations).
11 compilation-unit:
12 using-directivesopt global-attributesopt namespace-member-declarationsopt
13Un programa de C# consiste en una o varias unidades de compilación, cada una de ellas en un archivo de código
14fuente independiente. Cuando se compila un programa de C#, todas las unidades de compilación se procesan
15conjuntamente. Por lo tanto, las unidades de compilación pueden depender unas de otras, posiblemente de forma
16circular.
17Las directivas using (using-directives) de una unidad de compilación afectan a sus atributos globales y
18declaraciones miembro espacio de nombre (namespace-member-declarations), pero no al resto de unidades.
19Los atributos globales (global-attributes) (§17) de una unidad de compilación permiten especificar los atributos
20del ensamblado y del módulo de destino. Los ensamblados y los módulos actúan como contenedores físicos de
21tipos. Un ensamblado puede estar formado por varios módulos separados físicamente.
22Las declaraciones miembro espacio de nombre de cada unidad de compilación de un programa contribuyen los
23miembros de una declaración a un único espacio de declaración, denominado espacio global de nombres. Por
24ejemplo:
25 Archivo A.cs:
26 c lass A {}
27 Archivo B .cs:
28 c lass B {}
29Las dos unidades de compilación contribuyen al espacio global de nombres, en este caso, declarando dos clases
30con los nombres completos A y B. Como las dos unidades de compilación contribuyen al mismo espacio de
31declaración, si cada una contiene una declaración de miembro con el mismo nombre se producirá un error.

329.2 Declaraciones de espacio de nombres


33Una declaración de espacio de nombres (namespace-declaration) consiste en la palabra clave namespace
34seguida de un nombre y de un cuerpo y, opcionalmente, de un punto y coma.
35 namespace-declaration:
36 namespace qualified-identifier namespace-body ;opt

465220 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


466 Capítulo 18 Código no seguro

1 qualified-identifier:
2 identifier
3 qualified-identifier . identifier
4 namespace-body:
5 { using-directivesopt namespace-member-declarationsopt }
6Una declaración de espacio de nombres (namespace-declaration) puede ocurrir como declaración de alto nivel
7de una unidad de compilación (compilation-unit) o como declaración de miembro dentro de otra declaración de
8espacio de nombres. Cuando una declaración de espacio de nombres ocurre como declaración de alto nivel de
9una unidad de compilación (compilation-unit), el espacio de nombres se convierte en miembro del espacio
10global de nombres. Cuando una declaración de espacio de nombres ocurre dentro de otra declaración de espacio
11de nombres, el espacio de nombres más interno se convierte en miembro del espacio de nombres externo. En
12ambos casos, el nombre del espacio de nombres debe ser único dentro del espacio de nombres que lo contiene.
13Los espacios de nombres son pub l i cimplícitamente y la declaración de un espacio de nombres no puede incluir
14modificadores de acceso.
15Dentro del cuerpo de espacio de nombres (namespace-body), las directivas using opcionales importan los
16nombres de otros espacios de nombres y tipos, permitiendo que se haga referencia a los mismos directamente y
17no a través de nombres calificados. Las declaraciones miembro espacio de nombres (namespace-member-
18declarations) opcionales contribuyen miembros al espacio de declaración del espacio de nombres. Observe que
19todas las directivas using deben aparecer antes de cualquier declaración de miembro.
20El identificador calificado (qualified-identifier) de una declaración de espacio de nombres puede ser un único
21identificador o una secuencia de identificadores separados por símbolos (token) “.”. Esta última forma permite
22que un programa defina un espacio de nombres anidado sin anidar léxicamente varias declaraciones de espacio
23de nombres. Por ejemplo,
24 namespace N1.N2
25 {
26 c lass A {}
27 c lass B {}
28 }
29equivale semánticamente a
30 namespace N1
31 {
32 namespace N2
33 {
34
35 c lass B {}
36 }
37 }
38Los espacios de nombres tienen extremo abierto, y dos declaraciones de espacio de nombres con el mismo
39nombre completo contribuyen al mismo espacio de declaración (§3.3). En el siguiente ejemplo:
40 namespace N1.N2
41 {
42 c lass A {}
43 }

467Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 221


468Especificación del lenguaje C#

1 namespace N1.N2

2 {

3 class B {}
5las dos declaraciones de espacios de nombres anteriores contribuyen al mismo espacio de declaración, en este
64caso declarando dos clases con los nombres completos N1.N2 .A y N1.N2 .B. Como las dos declaraciones
7contribuyen al mismo espacio de declaración, si cada una contiene una declaración de miembro con el mismo
8nombre se producirá un error.

99.3 Directivas Using


10Las directivas using (using-directives) facilitan el uso de espacios de nombres y tipos definidos en otros
11espacios de nombres. Las directivas using tienen un impacto en el proceso de resolución de nombres de espacios
12de nombres o nombres de tipo (namespace-or-type-names) (§3.8) y de nombres simples (simple-names)
13(§7.5.2), pero, exceptuando las declaraciones, las directivas using no contribuyen nuevos miembros a los
14espacios de declaración subyacentes de las unidades de compilación o espacios de nombres dentro de las que se
15utilizan.
16 using-directives:
17 using-directive
18 using-directives using-directive
19 using-directive:
20 using-alias-directive
21 using-namespace-directive
22Una directiva using alias (using-alias-directive) (§9.3.1) define un alias para un espacio de nombres o un tipo.
23Una directiva using de espacio de nombres (using-namespace-directive) (§9.3.2) importa los miembros de tipo
24de un espacio de nombres.
25El ámbito de una directiva using se extiende a lo largo de las declaraciones miembro espacio de nombres de la
26unidad de compilación o espacio de nombres que la contiene inmediatamente. El ámbito de una directiva using
27no incluye específicamente las directivas using de su mismo nivel. Por lo tanto, las directivas using de un mismo
28nivel no se afectan unas a otras y no influye el orden en el que están escritas.

299.3.1 Directivas Using alias


30Una directiva using alias (using-alias-directive) define un identificador que funciona como un alias para un
31espacio de nombres o un tipo dentro de la unidad de compilación o cuerpo de espacio de nombres
32inmediatamente envolvente.
33 using-alias-directive:
34 us ing identifier = namespace-or-type-name ;
35Dentro de las declaraciones de miembros de una unidad de compilación o del cuerpo de un espacio de nombres
36que contiene una directiva using alias, se puede utilizar el identificador definido por la directiva using alias para
37hacer referencia al espacio de nombres o al tipo. Por ejemplo:
38 namespace N1.N2
39 {
40 c lass A {}
41 }
42 namespace N3
43 {
44 us ing A = N1.N2 .A ;

469222 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


470 Capítulo 18 Código no seguro

1 class B: A {}

23Dentro de} las declaraciones de miembros en el espacio de nombres N3 , A es un alias para N1.N2.A y, por tanto,
4la clase N3.B se deriva de la clase N1.N2.A. Se puede obtener el mismo efecto mediante la creación de un alias
5R para N1.N2 y, a continuación, haciendo referencia a R.A:

6 namespace N3

7 {

89 class B: R.A {}

10 }
11El identificador (identifier) de una directiva using alias debe ser único dentro del espacio de declaraciones de la
12unidad de compilación o espacio de nombres que contiene inmediatamente a dicha directiva using alias. Por
13ejemplo:

14 namespace N3

15 {

16 class A {}
18 namespace N3
17
19 {

20 using A = N1.N2.A; // Error, A already exists


22En el ejemplo anterior, N3 ya contiene un miembro A, así que es un error que una directiva using alias (using-
23
21alias-directive) utilice ese identificador. También es un error que dos o más directivas using alias de una misma
24unidad de compilación o cuerpo de espacio de nombres declaren un alias con el mismo nombre.
25Una directiva using alias ofrece un alias dentro de una determinada unidad de compilación o cuerpo de espacio
26de nombres, pero no contribuye ningún miembro nuevo al espacio de declaración subyacente. En otras palabras,
27una directiva using alias no es transitiva sino que sólo afecta a la unidad de compilación o cuerpo de espacio de
28nombres en donde ocurre. En el siguiente ejemplo:
29 namespace N3
30 {
31 us ing R = N1.N2 ;
32 }
33 namespace N3
34 {
35 c lass B: R.A {} / / Er ro r , R unknown
36
37el ámbito} de la directiva using alias (using-alias-directive) que define R sólo se extiende a las declaraciones de
38los miembros en el cuerpo de espacio de nombres que lo contiene y, por lo tanto, R es desconocido en la
39declaración del segundo espacio de nombres. Sin embargo, si se sitúa la directiva using alias en la unidad de
40compilación que la contiene, el alias estará disponible para ambas declaraciones de espacios de nombres:
41 us ing R = N1.N2 ;
42 namespace N3
43 {
44 c lass B: R.A {}
45 }

471Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 223


472Especificación del lenguaje C#

1 namespace N3

2 {

3 class C: R.A {}
5Al igual que con los miembros normales, los nombres definidos por las directivas using alias están ocultos por
64miembros nombrados de forma similar en ámbitos anidados. En el siguiente ejemplo:

7 using R = N1.N2;
8 namespace N3

9 {

10
11 class B: R.A {} // Error, R has no member A

12 }
13la referencia a R.A en la declaración de B produce un error en tiempo de compilación porque R hace referencia a
14N3.R, no a N1.N2.
15El orden en el que se escriben las directivas using alias no tiene importancia y la resolución del espacio de
16nombres o nombre de tipo a los que hace referencia una directiva using alias tampoco se ve afectado por la
17misma directiva using alias ni por otras directivas using de la unidad de compilación o cuerpo de espacio de
18nombres que la contiene inmediatamente. Es decir, el espacio de nombres o nombre de tipo de una directiva
19using alias se resuelve como si la unidad de compilación o cuerpo de espacio de nombres que la contiene
20inmediatamente no tuviera directivas using. En el siguiente ejemplo:

21 namespace N1.N2 {}
22 namespace N3

23 {

24
25 using R2 = N1.N2; // OK
26 using R3 = R1.N2; // Error, R1 unknown

27la última}directiva using alias (using-alias-directive) da como resultado un error en tiempo de compilación, ya
28
29que no se ve afectada por la primera directiva using alias.
30Una directiva using alias puede crear un alias para cualquier espacio de nombres o tipo, incluyendo el espacio de
31nombres dentro del que aparece y cualquier espacio de nombres o tipo anidado dentro de ese espacio de
32nombres.
33El acceso a un espacio de nombres o a un tipo mediante un alias da exactamente el mismo resultado que el
34acceso a ese espacio de nombres o tipo mediante su nombre declarado. Por ejemplo

35 namespace N1.N2

36 {

37 class A {}
39 namespace N3
38
40 {

41 using R1 = N1;

42

473224 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


474 Capítulo 18 Código no seguro

1 class B

2 {

3 N1.N2.A a; // refers to N1.N2.A

4 R1.N2.A b; // refers to N1.N2.A

58los nombres N1.N2


R2.A
.A, c;
R1 .N2 .A y R2.A //
son refers to yN1.N2.A
equivalentes todos se refieren a la clase cuyo nombre completo es
9N1.N2.A.
6
1079.3.2 Directivas Using espacio de nombres
11Una directiva using espacio de nombres (using-namespace-directive) importa los tipos que contiene un espacio
12de nombres en la unidad de compilación o cuerpo de espacio de nombres inmediatamente envolvente,
13permitiendo el uso del identificador sin calificar de cada tipo.
14 using-namespace-directive:
15 us ing namespace-name ;
16Dentro de las declaraciones de miembros de una unidad de compilación o del cuerpo de un espacio de nombres
17que contiene una directiva using espacio de nombres, se puede hacer referencia directamente a los tipos que
18contiene ese espacio de nombres. Por ejemplo:
19 namespace N1.N2
20 {
21 c lass A {}
22 }
23 namespace N3
24 {
25 us ing N1.N2 ;
26 c lass B: A {}
27 }
28En el anterior, dentro de las declaraciones de miembros en el espacio de nombres N3, los miembros de tipo
29N1.N2 están directamente disponibles y, por tanto, la clase N3.B se deriva de la clase N1.N2.A.
30Una directiva using espacio de nombres importa los tipos que contiene un determinado espacio de nombres,
31pero no importa específicamente los espacios de nombres anidados. En el siguiente ejemplo:
32 namespace N1.N2
33 {
34 c lass A {}
35 }
36 namespace N3
37 {
38 us ing N1;
39 c lass B: N2.A {} / / Er ro r , N2 unknown
40 }
41la directiva using espacio de nombres (using-namespace-directive) importa los tipos que contiene N1 , pero no
42los espacios de nombres anidados en N1 . Por ello, la referencia a N2.A en la declaración de B produce un error
43en tiempo de compilación porque no existen miembros con el nombre N2 en el ámbito.
44A diferencia de una directiva using alias (using-alias-directive), una directiva using espacio de nombres puede
45importar tipos cuyos identificadores ya están definidos dentro de la unidad de compilación o cuerpo de espacio

475Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 225


476Especificación del lenguaje C#

1de nombres envolvente. En realidad, los nombres importados por una directiva using espacio de nombres están
2ocultos por miembros nombrados de forma similar en la unidad de compilación o cuerpo de espacio de nombres
3que la contiene. Por ejemplo:

4 namespace N1.N2

5 {

67 class B {}

8 }
9 namespace N3

10 {

11
12 class A {}

13 }
14Aquí, dentro de las declaraciones de miembros en el espacio de nombres N3 , A hace referencia a N3.A en lugar
15de a N1.N2.A.
16Cuando más de un espacio de nombres importados mediante directivas using espacio de nombres (using-
17namespace-directives) de la misma unidad de compilación o cuerpo de espacio de nombres contiene tipos para
18el mismo nombre, las referencias a dicho nombre se consideran ambiguas. En el siguiente ejemplo:

19 namespace N1

20 {

21 class A {}
23 namespace N2
22
24 {

25 class A {}
27 namespace N3
26
28 {

29
30 using N2;
31 class B: A {} // Error, A is ambiguous

32
33tanto N1}como N2 contienen un miembro A. Como N3 importa ambas, se produce un error en tiempo de
34compilación al hacer referencia a A en N3. En este caso, el conflicto se puede resolver mediante la calificación
35de referencias a A o mediante la introducción de una directiva using alias (using-alias-directive) que elige un A
36determinado. Por ejemplo:
37 namespace N3
38 {
39 us ing N1;
40 us ing N2;
41 us ing A = N1.A ;
42 c lass B: A {} / / A means N1.A
43 }

477226 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


478 Capítulo 18 Código no seguro

1Al igual que una directiva-using-alias, una directiva-using-espacio-de-nombres no contribuye ningún miembro
2nuevo al espacio de declaración subyacente de la unidad de compilación o espacio de nombres, sino que afecta
3sólo a la unidad de compilación o cuerpo de espacio de nombres donde aparece.
4El nombre de espacio de nombres al que hace referencia una directiva using espacio de nombres se resuelve de
5la misma forma que el espacio de nombres o nombre de tipo al que hace referencia una directiva using alias. Por
6lo tanto, las directivas using espacio de nombres de una misma unidad de compilación o cuerpo de espacio de
7nombres no se afectan unas a otras y pueden escribirse en cualquier orden.

89.4 Miembros de espacio de nombres


9Una declaración de miembro de espacio de nombres (namespace-member-declaration) puede ser una
10declaración de espacio de nombres (namespace-declaration) (§9.2) o una declaración de tipo (type-declaration)
11(§9.5).
12 namespace-member-declarations:
13 namespace-member-declaration
14 namespace-member-declarations namespace-member-declaration
15 namespace-member-declaration:
16 namespace-declaration
17 type-declaration
18Una unidad de compilación o un cuerpo de espacio de nombres puede contener declaraciones de miembros de
19espacios de nombres (namespace-member-declaration), y dichas declaraciones contribuyen nuevos miembros al
20espacio de declaración subyacente de la unidad de compilación o cuerpo de espacio de nombres que las
21contienen.

229.5 Declaraciones de tipo


23Una declaración de tipo (type-declaration) es una declaración de clase (class-declaration) (§10.1), una
24declaración de estructura (struct-declaration) (§11.1), una declaración de interfaz (interface-declaration)
25(§13.1), una declaración de enumeración (enum-declaration) (§14.1) o una declaración de delegado (delegate-
26declaration) (§15.1).
27 type-declaration:
28 class-declaration
29 struct-declaration
30 interface-declaration
31 enum-declaration
32 delegate-declaration
33Una declaración de tipo (type-declaration) puede ocurrir como declaración de alto nivel en una unidad de
34compilación o como declaración de miembro dentro de un espacio de nombres, de una clase o de una estructura.
35Cuando ocurre una declaración de tipo para un tipo T como declaración de alto nivel en una unidad de
36compilación, el nombre completo del nuevo tipo declarado es simplemente T. Cuando una declaración de tipo
37para un tipo T se ejecuta dentro de un espacio de nombres, de una clase o de una estructura, el nombre completo
38del nuevo tipo declarado es N.T, donde N es el nombre completo del espacio de nombres, de la clase o de la
39estructura que lo contiene.
40Un tipo declarado dentro de una clase o de una estructura se denomina tipo anidado (§10.2.6).
41Los modificadores de acceso permitidos y el acceso predeterminado para una declaración de tipo dependen del
42contexto en el que tenga lugar la declaración (§3.5.1):

479Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 227


480Especificación del lenguaje C#

1• Los tipos declarados en las unidades de compilación o espacios de nombres pueden tener acceso pub l i co
2 i n te rna. lEl acceso predeterminado es internal.
3• Los tipos declarados en clases pueden tener acceso public, protected internal, protected, internal o
4 private. El acceso predeterminado es private.
5• Los tipos declarados en estructuras pueden tener acceso public, internal o private. El acceso
6 predeterminado es private.

481228 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


482 Capítulo 18 Código no seguro

1 10.Clases

2Una clase es una estructura de datos que puede contener miembros de datos (constantes y campos), miembros de
3función (métodos, propiedades, indizadores, eventos, operadores, constructores de instancia, constructores
4estáticos y destructores) y tipos anidados. Los tipos de clase admiten la herencia, un mecanismo mediante el
5cual una clase derivada puede extender y especializar una clase base.

610.1 Declaraciones de clases


7Una declaración de clase (class-declaration) es una declaración de tipo (type-declaration) (§9.5) que declara
8una clase nueva.
9 class-declaration:
10 attributesopt class-modifiersopt c lass identifier class-baseopt class-body ;opt
11Una declaración de clase (class-declaration) consiste en un conjunto de atributos (attributes) (§17) opcionales,
12seguidos de un conjunto de modificadores de clase (class-modifiers) (§10.1.1) opcionales, de la palabra clave
13c lassy de un identificador (identifier) que da nombre a la clase, de una especificación de clase base (class-
14base) (§10.1.2) opcional, de un cuerpo de clase (class-body) (§10.1.3) y de un punto y coma opcional.

1510.1.1 Modificadores de clase


16Una declaración de clase (class-declaration) puede incluir opcionalmente una secuencia de modificadores de
17clase:
18 class-modifiers:
19 class-modifier
20 class-modifiers class-modifier
21 class-modifier:
22 new
23 pub l i c
24 pro tec ted
25 i n te rna l
26 private
27 abstract
28 sealed
29Cuando el mismo modificador aparece varias veces en una declaración de clase, se produce un error en tiempo
30de compilación.
31El modificador new está permitido en clases anidadas. Especifica que la clase oculta un miembro heredado con
32el mismo nombre, como se describe en §10.2.2. Se produce un error en tiempo de compilación si el modificador
33new aparece en una declaración de clase no anidada.
34Los modificadores pub l i c, pro tec ted, internal y private controlan la accesibilidad a la clase. Dependiendo
35del contexto en el que ocurra la declaración de clase, algunos de estos modificadores pueden no estar permitidos
36(§3.5.1).
37Los modificadores abstract y sealed se explican en las siguientes secciones.

483Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 229


484Especificación del lenguaje C#

110.1.1.1 Clases abstractas


2El modificador abs t rac tse utiliza para indicar que una clase está incompleta y que sólo se va a utilizar como
3una clase base. Una clase abstracta se diferencia de una clase no abstracta en lo siguiente:
4• No se puede crear una instancia de una clase abstracta directamente, y es un error en tiempo de compilación
5 utilizar el operador new en una clase abstracta. Aunque es posible tener variables y valores cuyos tipos en
6 tiempo de compilación sean abstractos, tales variables y valores serán nu l lo contendrán referencias a
7 instancias de clases no abstractas derivadas de los tipos abstractos.
8• Se permite que una clase abstracta contenga miembros abstractos, aunque no es necesario.
9• No se puede sellar una clase abstracta.
10Cuando una clase no abstracta se deriva de una clase abstracta, la clase no abstracta debe incluir
11implementaciones reales de todos los miembros abstractos heredados; por lo tanto, reemplaza a estos miembros
12abstractos. En el siguiente ejemplo:

13 abstract class A

14 {

15 public abstract void F();


17 abstract class B: A
16
18 {

19 public void G() {}


21 class C: B
20
22 {

23 public override void F() {

24 // actual implementation of F
27la clase abstracta A define el método abstracto F. La clase B define un método adicional G, pero no proporciona
25
28una implementación de F, por lo que B también debe ser declarada como abstracta. La clase C reemplaza F y
29
26proporciona una implementación real. Dado que no hay miembros abstractos en C, está permitido que C sea no
30abstracta, aunque no es necesario.

3110.1.1.2 Clases Sealed


32El modificador sealed se utiliza para impedir la derivación de una clase. Si se especifica que una clase sealed es
33la clase base de otra clase, se produce un error en tiempo de compilación.
34Una clase sealed no puede ser tampoco una clase abstracta.
35El modificador sealed se utiliza principalmente para impedir la derivación no intencionada, pero también
36permite algunas optimizaciones en tiempo de ejecución. En particular, como una clase sealed no puede tener
37clases derivadas, es posible transformar las llamadas virtuales a miembros de función en instancias de clase
38sealed en llamadas no virtuales.

3910.1.2 Especificación de clase base


40Una declaración de clase puede incluir una especificación de clase base (class-base), que define la clase base
41directa de la clase y las interfaces (13) implementadas por la clase.

485230 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


486 Capítulo 18 Código no seguro

1 class-base:
2 : class-type
3 : interface-type-list
4 : class-type , interface-type-list
5 interface-type-list:
6 interface-type
7 interface-type-list , interface-type

810.1.2.1 Clases base


9Cuando se incluye un tipo de clase (class-type) en la clase base, éste especifica la clase base directa de la clase
10que se declara. Si la declaración de una clase no tiene clase base (class-base) o ésta sólo proporciona una lista de
11tipos de interfaz, se supone que la clase base directa es object. Una clase hereda los miembros de su clase base
12directa, como se describe en §10.2.1.
13En el siguiente ejemplo:
14 c lass A {}
15 c lass B: A {}
16se dice que la clase A es la clase base directa de B y que B se deriva de A. Dado que A no especifica
17explícitamente una clase base directa, su clase base directa es implícitamente object.
18La clase base directa de un tipo de clase debe ser al menos tan accesible como el propio tipo de clase (§3.5.4).
19Por ejemplo, se produce un error en tiempo de compilación cuando una clase public se deriva de una clase
20private o internal.
21La clase base directa de un tipo de clase no debe ser de ninguno de los siguientes tipos: System.Array,
22System.Delegate, System.MulticastDelegate, System.Enum o System.ValueType.
23Las clases base de una clase son la clase base directa y sus clases base. En otras palabras, el conjunto de las
24clases base es el cierre transitivo de la relación de clase base directa. En el ejemplo anterior, las clases base de B
25son A y object.
26Exceptuando la clase object, todas las clases tienen una clase base directa. La clase object no tiene clase base
27directa y es la última clase base del resto de clases.
28Cuando una clase B se deriva de una clase A, se produce un error si A depende de B. Una clase depende
29directamente de su clase base directa (si existe), y depende directamente de la clase dentro de la cual está
30inmediatamente anidada (si es el caso). Dada esta definición, el conjunto completo de clases de las cuales
31depende una clase es el cierre transitivo de la relación de dependencia directa.
32En el ejemplo
33 c lass A: B {}
34 c lass B: C {}
35 c lass C: A {}
36hay un error, porque las clases tienen una dependencia circular entre ellas. Igualmente, el ejemplo
37 c lass A: B .C {}
38 c lass B: A
39 {
40 pub l i c c lass C {}
41 }

487Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 231


488Especificación del lenguaje C#

1produce un error en tiempo de compilación porque A depende de B .C (su clase base directa), que a su vez
2depende de B (su clase envolvente inmediata), que depende circularmente de A.

489232 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


490 Capítulo 18 Código no seguro

1Observe que una clase no depende de las clases anidadas internas. En el siguiente ejemplo:

2 class A

3 {

4 class B: A {}
6B depende de A (porque A es tanto su clase base directa como su clase envolvente inmediata), pero A no
75depende de B (porque B no es ni una clase base ni una clase envolvente de A). Por tanto, el ejemplo es correcto.
8No es posible derivar de una clase sealed. En el siguiente ejemplo:

9 sealed class A {}
10 class B: A {} // Error, cannot derive from a sealed class
11la clase B es errónea porque intenta derivarse de la clase sealed A.

1210.1.2.2 Implementaciones de interfaces


13Una especificación de clase base puede incluir una lista de tipos de interfaz, en cuyo caso se dice que la clase
14implementa esos tipos de interfaz. Las implementaciones de interfaces se explican más detalladamente en §13.4.

1510.1.3 Cuerpo de clase


16El cuerpo de clase (class-body) de una clase define los miembros de la clase.
17 class-body:
18 { class-member-declarationsopt }

1910.2 Miembros de clase


20Los miembros de una clase se componen de los miembros que introducen las declaraciones de miembros de
21clase (class-member-declarations) y los miembros que ésta hereda de su clase base directa.
22 class-member-declarations:
23 class-member-declaration
24 class-member-declarations class-member-declaration
25 class-member-declaration:
26 constant-declaration
27 field-declaration
28 method-declaration
29 property-declaration
30 event-declaration
31 indexer-declaration
32 operator-declaration
33 constructor-declaration
34 destructor-declaration
35 static-constructor-declaration
36 type-declaration
37Los miembros de una clase se dividen en las siguientes categorías:
38• Constantes, que representan valores constantes asociados a la clase (§10.3).
39• Campos, que son las variables de la clase (§10.4).
40• Métodos, que implementan los cálculos y acciones que puede realizar la clase (§10.5).

491Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 233


492Especificación del lenguaje C#

1• Propiedades, que definen características con nombre y las acciones asociadas con la lectura y la escritura de
2 esas características (§10.6).
3• Eventos, que definen las notificaciones que puede generar la clase (§10.7).
4• Indizadores, que permiten indizar las instancias de la clase de la misma manera (sintácticamente) que las
5 matrices (§10.8).
6• Operadores, que definen los operadores de expresión que se pueden aplicar a las instancias de la clase
7 (§10.9).
8• Constructores de instancia, que implementan las acciones necesarias para inicializar las instancias de la
9 clase (§10.10).
10• Destructores, que implementan las acciones que se deben realizar antes de que las instancias de la clase sean
11 descartadas de forma permanente (§10.12).
12• Constructores estáticos, que implementan las acciones necesarias para inicializar la propia clase (§10.11).
13• Tipos, que representan los tipos locales de la clase (§9.5).
14A los miembros que pueden contener código ejecutable se los conoce colectivamente como los miembros de
15función de la clase. Los miembros de función de una clase son los métodos, propiedades, eventos, indizadores,
16operadores, constructores de instancia, destructores y constructores estáticos de dicha clase.
17Una declaración de clase (class-declaration) crea un nuevo espacio de declaración (§3.3), y las declaraciones de
18miembros de clase (class-member-declarations) inmediatamente contenidas en la declaración de clase (class-
19declaration) introducen nuevos miembros en este espacio de declaración. Se aplican las siguientes reglas a las
20declaraciones de miembros de clase (class-member-declarations):
21• Los constructores de instancia, los constructores estáticos y los destructores deben tener el mismo nombre
22 que su clase envolvente inmediata. El resto de miembros deben tener nombres diferentes al de la clase
23 envolvente inmediata.
24• El nombre de una constante, campo, propiedad, evento o tipo debe ser diferente de los nombres del resto de
25 miembros declarados en la misma clase.
26• El nombre de un método debe ser diferente de los nombres del resto de miembros que no sean métodos
27 declarados en la misma clase. Además, la firma (§3.6) de un método debe ser distinta de las firmas de todos
28 los demás métodos declarados en la misma clase, y dos métodos declarados en la misma clase no pueden
29 tener firmas que sólo se diferencien por re fy out.
30• La firma de un constructor de instancia debe diferir de las firmas de todos los demás constructores de
31 instancia declaradas en la misma clase, y dos constructores declarados en la misma clase no pueden tener
32 firmas que sólo se diferencien en re fy out.
33• La firma de un indizador debe ser diferente de las firmas de los demás indizadores declarados en la misma
34 clase.
35• La firma de un operador debe ser diferente de las firmas de los demás operadores declarados en la misma
36 clase.
37Los miembros heredados de una clase (§10.2.1) no forman parte del espacio de declaración de la clase. Por ello,
38una clase derivada puede declarar un miembro con el mismo nombre o firma que el de un miembro heredado
39(que en realidad oculta el miembro heredado).

493234 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


494 Capítulo 18 Código no seguro

110.2.1 Herencia
2Una clase hereda los miembros de su clase base directa. La herencia significa que una clase contiene
3implícitamente todos los miembros de su clase base directa, excepto los constructores de instancia, los
4constructores estáticos y los destructores de la clase base. Estos son algunos aspectos importantes de la herencia:
5• La herencia es transitiva. Si C se deriva de B y B se deriva de A, entonces C hereda los miembros declarados
6 en B y los miembros declarados en A.
7• Una clase derivada extiende su clase base directa. Una clase derivada puede agregar nuevos miembros a los
8 heredados, pero no puede quitar la definición de un miembro heredado.
9• Los constructores de instancia, los constructores estáticos y los destructores no se heredan, pero el resto de
10 miembros sí, independientemente de la accesibilidad que tengan declarada (§3.5). Sin embargo,
11 dependiendo de la accesibilidad que tengan declarada, es posible que los miembros heredados no sean
12 accesibles en una clase derivada.
13• Una clase derivada puede ocultar (§3.7.1.2) miembros heredados declarando nuevos miembros con el
14 mismo nombre o firma. No obstante, tenga en cuenta que ocultar un miembro heredado no significa quitarlo,
15 simplemente hace que el miembro no sea directamente accesible a través de la clase derivada.
16• Una instancia de una clase contiene un conjunto de todos los campos de instancia declarados en la clase y en
17 sus clases base y se produce una conversión implícita (§6.1.4) de los tipos de la clase derivada a cualquiera
18 de los tipos de su clase base. Una referencia a una instancia de una clase derivada puede ser tratada como
19 referencia a una instancia de cualquiera de sus clases base.
20• Una clase puede declarar métodos, propiedades e indizadores virtuales, y las clases derivadas reemplazan la
21 implementación de dichos miembros de función. Esto permite que las clases muestren un comportamiento
22 polimórfico, ya que las acciones que realiza una llamada a un miembro de función varían según el tipo de
23 instancia a través de la que se invoca al miembro de función en tiempo de ejecución.

2410.2.2 Modificador New


25Una declaración de miembro de clase (class-member-declaration) puede declarar un miembro con el mismo
26nombre o firma que el de un miembro heredado. Cuando esto ocurre, se dice que el miembro de la clase
27derivada oculta el miembro de la clase base. Ocultar un miembro heredado no se considera un error, pero hace
28que el compilador emita una advertencia. Para suprimir la advertencia, la declaración del miembro de la clase
29derivada puede incluir un modificador new para indicar que el miembro derivado pretende ocultar el miembro
30base. Este tema se explica con más detalle en la sección §3.7.1.2.
31Si se incluye un modificador new en una declaración que no oculta un miembro heredado, se emite una
32advertencia a tal efecto. Esta advertencia se evita quitando el modificador new.

3310.2.3 Modificadores de acceso


34Una declaración de miembro de clase (class-member-declaration) puede tener uno de los cinco posibles tipos de
35accesibilidad declarada (§3.5.1): pub l i c, pro tec ted i n te rna
, protected
l , internal o private. Excepto en la
36combinación protected internal, especificar más de un modificador de acceso produce un error en tiempo de
37compilación. Cuando una declaración de miembro de clase (class-member-declaration) no incluye ningún
38modificador de acceso, se supone que es private.

3910.2.4 Tipos constituyentes


40Los tipos que se utilizan en la declaración de un miembro se denominan tipos constituyentes del miembro. Los
41posibles tipos constituyentes son los tipos de una constante, de un campo, de una propiedad, de un evento o de
42un indizador, el tipo de valor devuelto de un método u operador y los tipos de los parámetros de un método,

495Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 235


496Especificación del lenguaje C#

1indizador, operador o constructor de instancia. Los tipos constituyentes de un miembro deben ser al menos tan
2accesibles como el propio miembro (§3.5.4).

310.2.5 Miembros estáticos y de instancia


4Los miembros de una clase son miembros estáticos o miembros de instancia. En términos generales, se podría
5decir que los miembros estáticos pertenecen a las clases y los miembros de instancia pertenecen a los objetos
6(instancias de clases).
7Cuando la declaración de un campo, método, propiedad, evento, operador o constructor incluye un modificador
8s ta t i,cestá declarando un miembro estático. Una declaración de constante o tipo declara implícitamente un
9miembro estático. Los miembros estáticos tienen las siguientes características:
10• Cuando se hace referencia a un miembro estático M en un acceso a miembro (member-access) (§7.5.4) de la
11 forma E .M, E debe denotar un tipo que contenga M. Se produce un error en tiempo de compilación si E
12 denota una instancia.
13• Un campo estático identifica exactamente una ubicación de almacenamiento. Independientemente del
14 número de instancias que se creen de una clase, sólo habrá una copia de un campo estático.
15• Un miembro de función estático (método, propiedad, evento, operador o constructor) no opera en una
16 instancia específica, y se produce un error en tiempo de compilación si se hace referencia a th i sen dicho
17 miembro de función.
18Cuando la declaración de un campo, método, propiedad, evento, indizador, constructor o destructor no incluye
19un modificador s ta t i,cestá declarando un miembro de instancia (los miembros de instancia se conocen también
20como miembros no estáticos). Los miembros de instancia tienen las siguientes características:
21• Cuando se hace referencia a un miembro de instancia M en un acceso a miembro (member-access) (§7.5.4)
22 de la forma E.M, E debe denotar una instancia de un tipo que contenga M. Se produce un error en tiempo de
23 compilación si E denota un tipo.
24• Cada instancia de una clase contiene un conjunto independiente de todos los campos de instancia de la clase.
25• Un miembro de función de instancia (método, propiedad, indizador, constructor de instancia o destructor)
26 opera en una determinada instancia de la clase y es posible tener acceso a dicha instancia con th i s(§7.5.7).
27En el ejemplo siguiente se ilustran las reglas para el acceso a miembros estáticos y de instancia:
28 c lass Tes t
29 {
30 int x;
31 s ta t i c i n t y ;
32 vo id F ( ) {
33 x = 1; / / Ok, same as th i s . x = 1
34 y = 1; / / Ok, same as Tes t . y = 1
35 }
36 s ta t i c vo id G( ) {
37 x = 1; / / Er ro r , cannot access th i s . x
38 y = 1; / / Ok, same as Tes t . y = 1
39 }

497236 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


498 Capítulo 18 Código no seguro

1 static void Main() {

2 Test t = new Test();

3 t.x = 1; // Ok

4 t.y = 1; // Error, cannot access static member through instance

5 Test.x = 1; // Error, cannot access instance member through type


9El método F muestra que se puede utilizar un nombre simple (simple-name) (§7.5.2) en un miembro de función
106de instancia para tener acceso tanto a miembros de instancia como a miembros estáticos. El método G muestra
117que en un miembro de función estático, se produce un error en tiempo de compilación si se obtiene acceso a un
12miembro de instancia a través de un nombre simple (simple-name). El método Main muestra que en un acceso
138de miembro (member-access) (§7.5.4), se deben utilizar instancias para tener acceso a los miembros de instancia
14y tipos para tener acceso a los miembros estáticos.

1510.2.6 Tipos anidados


16Un tipo declarado dentro de una clase o de una estructura se denomina tipo anidado. Un tipo declarado dentro
17de una unidad de compilación o de un espacio de nombres se denomina tipo no anidado.
18En el siguiente ejemplo:
19 us ing Sys tem;
20 c lass A
21 {
22 c lass B
23 {
24 s ta t i c vo id F ( ) {
25 Conso le .Wr i teL ine ( "A .B .F " ) ;
26 }
27la clase B es un tipo anidado porque se declara dentro de la clase A y la clase A es un tipo no anidado porque se
29
30
28declara dentro de una unidad de compilación.

3110.2.6.1 Nombre completo


32El nombre completo (§3.8.1) de un tipo anidado es S.N, donde S es el nombre completo del tipo en el que se
33declara el tipo N.

3410.2.6.2 Accesibilidad declarada


35Los tipos no anidados pueden tener accesibilidad declarada public o internal, aunque la accesibilidad declarada
36predeterminada es internal. Los tipos anidados también pueden tener esas mismas formas de accesibilidad
37declarada y otras adicionales, en función de si el tipo que los contiene es una clase o una estructura:
38• Un tipo anidado declarado en una clase puede tener cualquiera de las cinco formas de accesibilidad
39 declarada (public, protected internal, protected, internal o private) y, como en otros miembros de
40 clase, la forma predeterminada es private.
41• Un tipo anidado declarado en una estructura puede tener cualquiera de las formas de accesibilidad declarada
42 public, internal o private y, como en otros miembros de estructura, la forma predeterminada es private.
43En el ejemplo

499Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 237


500Especificación del lenguaje C#

1 public class List

2 {

3 // Private data structure

4 private class Node

5 {
8 public Node(object data, Node next) {
6
9 this.Data = data;
7
10 this.Next = next;

11
13 private Node first = null;
12
14 private Node last = null;
15 // Public interface
16 public void AddToFront(object o) {...}
17 public void AddToBack(object o) {...}
18 public object RemoveFromFront() {...}
19 public object RemoveFromBack() {...}
20 public int Count { get {...} }

21declara una
22 } clase anidada privada denominada Node .

2310.2.6.3 Ocultar
24Un tipo anidado puede ocultar (§3.7.1) un miembro de base. Está permitido utilizar el modificador new en las
25declaraciones de tipos anidados, por lo que dicha ocultación se puede expresar explícitamente. En el ejemplo

26 using System;
27 class Base

28 {

29 public static void M() {

30 Console.WriteLine("Base.M");
33
31 class Derived: Base

34
32 {

35 new public class M

36 {

37 public static void F() {

38 Console.WriteLine("Derived.M.F");
42 class Test
39
43 {
40
44 static void Main() {
41
45 Derived.M.F();

46
501238 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
47
502 Capítulo 18 Código no seguro

1muestra una clase anidada M que oculta el método M definido en Base.

210.2.6.4 Acceso this


3Un tipo anidado y el tipo que lo contiene no tienen una relación especial con respecto al acceso this (this-access)
4(§7.5.7). En concreto, no se puede utilizar th i sdentro de un tipo anidado para hacer referencia a miembros de
5instancia del tipo que lo contiene. En los casos en los que un tipo anidado necesite tener acceso a los miembros
6de instancia del tipo contenedor, se puede utilizar th i spara la instancia del tipo contenedor como argumento de
7constructor del tipo anidado. El siguiente ejemplo
8 us ing Sys tem;
9 c lass C
10 {
11 i n t i = 123 ;
12 pub l i c vo id F ( ) {
13 Nes ted n = new Nes ted ( th i s ) ;
14 n .G( ) ;
15 }
16 pub l i c c lass Nes ted
17 {
18 C th i s _c ;
19 pub l i c Nes ted (C c ) {
20 th i s _c = c ;
21 }
22 pub l i c vo id G( ) {
23 Conso le .Wr i teL ine ( th i s _c . i ) ;
24 }
25 }
26
27 c lass Tes t
28 {
29 s ta t i c vo id Main ( ) {
30 C c = new C( ) ;
31 c .F ( ) ;
32 }
34muestra esta técnica. Una instancia de C crea una instancia de Nested y pasa su propio this al constructor de
33
35Nested para proporcionar acceso posterior a los miembros de instancia de C.

3610.2.6.5 Acceso a miembros privados y protegidos del tipo contenedor


37Un tipo anidado tiene acceso a todos los miembros accesibles a su tipo contenedor, incluyendo miembros que
38tienen accesibilidad declarada private o protected. En el ejemplo
39 us ing Sys tem;
40 c lass C
41 {
42 pr i va te s ta t i c vo id F ( ) {
43 Conso le .Wr i teL ine ( "C .F " ) ;
44

503Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 239


504Especificación del lenguaje C#

1 public class Nested

2 {

3 public static void G() {

4 F();

5 }
8 class Test
6
9 {
7
10 static void Main() {

11 C.Nested.G();
14muestra una clase C que contiene una clase anidada Nes ted. Dentro de Nested, el método G llama al método
12
15estático F definido en C, y F tiene accesibilidad declarada privada.
13
16Un tipo anidado también puede tener acceso a miembros protegidos definidos en un tipo de base de su tipo
17contenedor. En el siguiente ejemplo:

18 using System;
19 class Base

20 {

21 protected void F() {

22 Console.WriteLine("Base.F");
25
23 class Derived: Base

26
24 {

27 public class Nested

28 {

29 public void G() {

30 Derived d = new Derived();

31
35 class Test
32
36 {
33
37 static void Main() {
34
38 Derived.Nested n = new Derived.Nested();

39la clase anidada n.G();


42 Derived.Nested obtiene acceso al método protegido F definido en la clase base de Derived,
43Base, llamando a través de una instancia de Derived.
40
44
4110.2.7 Nombres de miembro reservados
45Para facilitar la implementación subyacente de C# en tiempo de ejecución, la implementación, para cada
46declaración de miembro del código fuente que sea una propiedad, un evento o un indizador, debe reservar dos
47firmas de métodos basadas en el tipo de la declaración de miembro, su nombre y su tipo. Se produce un error en
48tiempo de compilación cuando un programa declara un miembro cuya firma coincida con una de estas firmas

505240 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


506 Capítulo 18 Código no seguro

1reservadas, incluso aunque la implementación subyacente en tiempo de ejecución no haga uso de dichas
2reservas.
3Los nombres reservados no presentan declaraciones, por lo que no intervienen en la búsqueda de miembros. Sin
4embargo, las firmas de métodos reservados asociados a una declaración sí intervienen en la herencia (§10.2.1) y
5pueden ser ocultados por el nuevo modificador (§10.2.2).
6La reserva de estos nombres tiene tres propósitos:
7• Permitir que la implementación subyacente utilice un identificador normal como nombre de método para
8 obtener o establecer el acceso a la característica del lenguaje C#.
9• Permitir la interoperabilidad con otros lenguajes utilizando un identificador normal como nombre de método
10 para obtener o establecer el acceso a la característica del lenguaje C#.
11• Asegurar que el código fuente aceptado por un compilador compatible también es aceptado por otros
12 compiladores, haciendo que las especificaciones de nombres de miembros reservados sean consistentes a
13 través de todas las implementaciones de C#.
14La declaración de un destructor (§10.12) también causa la reserva de una firma (§10.2.7.4).

507Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 241


508Especificación del lenguaje C#

110.2.7.1 Nombres de miembros reservados para propiedades


2Para una propiedad P (§10.6) de tipo T, se reservan las siguientes firmas:

3 T get_P();

45Se reservan
void set_P(T
ambas firmas,value);
tanto si la propiedad es de sólo lectura como de sólo escritura.
6En el siguiente ejemplo:

7 using System;
8 class A

9 {

10 public int P {

11 get { return 123; }


14
12 class B: A

15
13 {

16 new public int get_P() {

17
19 new public void set_P(int value) {
18
20 }

21
22 class Test

23 {

24 static void Main() {

25 B b = new B();

26 A a = b;

27 Console.WriteLine(a.P);
32
28una clase A define una propiedad de sólo lectura P, reservando así las firmas para los métodos get_P y set_P.
33Una clase B se deriva de A y oculta ambas firmas reservadas. El ejemplo produce el resultado:
29
34 123
30
35 123
31
36
3710.2.7.2 Nombres de miembros reservados para eventos
38Para un evento E (§10.7) de tipo delegado T, se reservan las siguientes firmas:

39 void add_E(T handler);

40 void remove_E(T handler);


4110.2.7.3 Nombres de miembros reservados para indizadores
42Para un indizador (§10.8) de tipo T con la lista de parámetros L, se reservan las siguientes firmas:

43 T get_Item(L);

44 void set_Item(L, T value);

509242 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


510 Capítulo 18 Código no seguro

1Se reservan ambas firmas, tanto si el indizador es de sólo lectura como de sólo escritura.

210.2.7.4 Nombres de miembros reservados para destructores


3Para una clase que contiene un destructor (§10.12), se reserva la siguiente firma:

4 void Finalize();

510.3 Constantes
6Una constante es un miembro de clase que representa un valor constante: un valor que puede calcularse en
7tiempo de compilación. Una declaración de constante (constant-declaration) especifica una o varias constantes
8de un determinado tipo.
9 constant-declaration:
10 attributesopt constant-modifiersopt cons t type constant-declarators ;
11 constant-modifiers:
12 constant-modifier
13 constant-modifiers constant-modifier
14 constant-modifier:
15 new
16 pub l i c
17 protected
18 internal
19 private
20 constant-declarators:
21 constant-declarator
22 constant-declarators , constant-declarator
23 constant-declarator:
24 identifier = constant-expression
25Una declaración de constante (constant-declaration) puede incluir un conjunto de atributos (§17), un
26modificador new (§10.2.2) y una combinación válida de los cuatro modificadores de acceso (§10.2.3). Los
27atributos y modificadores se aplican a todos los miembros declarados en la declaración-de-constante. Aunque las
28constantes se consideran miembros estáticos, una declaración de constante (constant-declaration) no necesita ni
29permite un modificador s ta t i.cCuando el mismo modificador aparece varias veces en una declaración de
30constante, se produce un error.
31El tipo (type) de una declaración de constante (constant-declaration) especifica el tipo de los miembros que se
32incluyen en la declaración. El tipo es seguido de una lista de declaradores-de-constante, cada uno de los cuales
33incluye una nueva constante. Un declarador de constante (constant-declarator) consta de un identificador
34(identifier) que da nombre al miembro, seguido del símbolo (token) “=” y de una expresión constante (constant-
35expression) (§7.15) que establece el valor del miembro.
36El tipo (type) especificado en una declaración de constante debe ser sby te, byte, short, ushort, int, uint,
37long, ulong, char, float, double, decimal, bool, string, un tipo de enumeración (enum-type) o un tipo de
38referencia (reference-type). Cada expresión constante (constant-expression) debe dar un valor del tipo destino o
39de un tipo que se pueda convertir al tipo destino mediante una conversión implícita (§6.1).
40El tipo (type) de una constante debe ser al menos tan accesible como la propia constante (§3.5.4).
41Para obtener el valor de una constante en una expresión se utiliza un nombre simple (simple-name) (§7.5.2) o un
42acceso a miembro (member-access) (§7.5.4).

511Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 243


512Especificación del lenguaje C#

1Una constante puede participar en una expresión constante (constant-expression). Por lo tanto, se puede utilizar
2una constante en cualquier constructor que necesite una expresión constante. Ejemplos de este tipo de
3constructores incluyen las etiquetas case, instrucciones goto case, declaraciones de miembros enum,
4atributos y otras declaraciones de constantes.
5Como se describe en la §7.15, una expresión constante (constant-expression) es una expresión que se puede
6evaluar completamente en tiempo de compilación. Puesto que la única forma de crear un valor no nulo de un
7tipo de referencia (reference-type) distinto de string es aplicar el operador new, y como el operador new no
8está permitido en una expresión constante (constant-expression), el único valor posible para las constantes de
9tipos de referencia (reference-types) distintos de string es null.
10Cuando se desea un nombre simbólico para un valor constante, pero el tipo del valor no se permite en una
11declaración de constante, o cuando el valor no se puede calcular en tiempo de compilación con una expresión
12constante (constant-expression), en su lugar puede utilizarse un campo readonly (§10.4.2).
13Una declaración de constante que declara varias constantes equivale a varias declaraciones de una sola constante
14con los mismos atributos, los mismos modificadores y el mismo tipo. Por ejemplo:
15 c lass A
16 {
17 pub l i c cons t doub le X = 1 .0 , Y = 2 .0 , Z = 3 .0 ;
18equivale}a
19
20 c lass A
21 {
22 pub l i c cons t doub le X = 1 .0 ;
23 pub l i c cons t doub le Y = 2 .0 ;
24 pub l i c cons t doub le Z = 3 .0 ;
26
25 Está permitido que las constantes dependan de otras constantes dentro del mismo programa, siempre y cuando
27las dependencias no sean circulares. El compilador evalúa automáticamente las declaraciones de constantes en el
28orden apropiado. En el siguiente ejemplo:
29 c lass A
30 {
31 pub l i c cons t i n t X = B .Z + 1 ;
32 pub l i c cons t i n t Y = 10 ;
33
34 c lass B
35 {
36 pub l i c cons t i n t Z = A.Y + 1 ;
37el compilador
38 } evalúa primero A.Y, después B.Z y finalmente A.X, produciendo los valores 10, 11 y 12. Las
39declaraciones de constantes pueden depender de constantes de otros programas, pero dichas dependencias sólo
40son posibles en una dirección. Haciendo referencia al ejemplo anterior, si A y B fueran declaradas en programas
41independientes, sería posible que A.X dependiera de B.Z, pero entonces B.Z no podría depender
42simultáneamente de A.Y.

4310.4 Campos
44Un campo es un miembro que representa una variable asociada a un objeto o una clase. Una declaración de
45campo (field-declaration) especifica uno o varios campos de un determinado tipo.

513244 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


514 Capítulo 18 Código no seguro

1 field-declaration:
2 attributesopt field-modifiersopt type variable-declarators ;
3 field-modifiers:
4 field-modifier
5 field-modifiers field-modifier
6 field-modifier:
7 new
8 pub l i c
9 protected
10 internal
11 private
12 static
13 readonly
14 volatile
15 variable-declarators:
16 variable-declarator
17 variable-declarators , variable-declarator
18 variable-declarator:
19 identifier
20 identifier = variable-initializer
21 variable-initializer:
22 expression
23 array-initializer
24Una declaración de campo (field-declaration) puede incluir un conjunto de atributos (§17), un modificador new
25(§10.2.2), una combinación válida de los cuatro modificadores de acceso (§10.2.3) y un modificador s ta t i c
26(§10.4.1). Además, una declaración de campo (field-declaration) puede incluir un modificador readonly
27(§10.4.2) o un modificador volatile (§10.4.3) pero no ambos. Los atributos y modificadores se aplican a todos
28los miembros declarados en la declaración-de-campo. Es un error que el mismo modificador aparezca varias
29veces en una declaración de campo.
30El tipo (type) de una declaración de campo (field-declaration) especifica el tipo de los miembros que se
31incluyen en la declaración. El tipo es seguido de una lista de declaradores de variable (variable-declarators),
32cada uno de los cuales incluye un nuevo miembro. Un declarador de variable (variable-declarator) está formado
33por un identificador (identifier) que da nombre al miembro, que opcionalmente puede ir seguido del símbolo
34(token) “=” y de un inicializador de variable (variable-initializer) (§10.4.5) que establece el valor inicial del
35miembro.
36El tipo (type) de un campo debe ser al menos tan accesible como el propio campo (§3.5.4).
37Para obtener el valor de un campo en una expresión se utiliza un nombre simple (simple-name) (§7.5.2) o un
38acceso a miembro (member-access) (§7.5.4). El valor de un campo que no sea de sólo lectura se modifica
39mediante una asignación (assignment) (§7.13). El valor de un campo que no sea de sólo lectura se puede obtener
40y modificar utilizando operadores de incremento y decremento postfijos (§7.5.9), y operadores de incremento y
41decremento prefijos (§7.6.5).
42Una declaración de campo que declara varios campos equivale a varias declaraciones de un único campo con los
43mismos atributos, los mismos modificadores y el mismo tipo. Por ejemplo:

515Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 245


516Especificación del lenguaje C#

1 class A

2 {

3 public static int X = 1, Y, Z = 100;


5equivale a
4
6 class A

7 {

8 public static int X = 1;

9 public static int Y;


12
1010.4.1 Campos estáticos y de instancia
13Cuando una declaración de campo incluye un modificador s ta t i,clos campos presentados en la declaración son
11
14campos estáticos. Cuando no existe un modificador s ta t i,clos campos presentados en la declaración son
15campos de instancia. Los campos estáticos y los campos de instancia son dos de los distintos tipos de variables
16(§5) compatibles con C# y en ocasiones se les llama variables estáticas y variables de instancia,
17respectivamente.
18Un campo estático no forma parte de una instancia específica, sino que identifica exactamente una ubicación de
19almacenamiento. No importa cuántas instancias de una clase se creen, nunca hay más de una copia de un campo
20estático para el dominio de aplicación asociado.
21Un campo de instancia pertenece siempre a una instancia. De manera específica, cada instancia de una clase
22contiene un conjunto independiente de todos los campos de instancia de la clase.
23Cuando se hace referencia a un campo en un acceso a miembro (member-access) (§7.5.4) de la forma E .M, si M
24es un campo estático, E debe denotar un tipo que contenga M, y si M es un campo de instancia, E debe denotar
25una instancia de un tipo que contenga M.
26Las diferencias entre miembros estáticos y de instancia se tratan con más detalle en §10.2.5.

2710.4.2 Campos de sólo lectura


28Cuando una declaración de campo (field-declaration) incluye un modificador readon ly, los campos presentados
29en la declaración son campos de sólo lectura. Las asignaciones directas a campos de sólo lectura únicamente
30pueden formar parte de la declaración, de un constructores de instancia o bien de un constructor estático de la
31misma clase. (Es posible asignar varias veces un campo de sólo lectura en estos contextos). En concreto, sólo se
32permiten asignaciones directas a un campo readon lyen las siguientes situaciones:
33• En el declarador de variable (variable-declarator) que introduce la variable; mediante la inclusión de un
34 inicializador de variable (variable-initializer) en la declaración).
35• Para un campo de instancia, en los constructores de instancia de la clase que contiene la declaración de
36 campo; para un campo estático, en el constructor estático de la clase que contiene la declaración de campo.
37 Éstos son también los únicos contextos en los que es válido pasar un campo readonly como parámetro out
38 o ref.
39Se produce un error en tiempo de compilación al intentar hacer una asignación a un campo readonly o pasarlo
40como parámetro out o ref en cualquier otro contexto.

517246 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


518 Capítulo 18 Código no seguro

110.4.2.1 Utilizar campos de sólo lectura estáticos para constantes


2Un campo s ta t i c readones lyútil cuando se desea un nombre simbólico para un valor constante pero el tipo del
3valor no se permite en una declaración cons t, o cuando el valor no se puede calcular en tiempo de compilación.
4En el siguiente ejemplo:

5 public class Color

6 {

7 public static readonly Color Black = new Color(0, 0, 0);

8 public static readonly Color White = new Color(255, 255, 255);

9 public static readonly Color Red = new Color(255, 0, 0);


12 private byte red, green, blue;
10
13 public Color(byte r, byte g, byte b) {
11
14 red = r;

15 green = g;

16 blue = b;
19los miembros B lack, Whi te, Red, Green y Blue no pueden ser declarados como miembros const porque sus
17valores no se pueden calcular en tiempo de compilación. Sin embargo, produce el mismo efecto declararlos
20
21
18como static readonly.
2210.4.2.2 Versiones de constantes y campos de sólo lectura estáticos
23Las constantes y los campos de sólo lectura tienen diferentes semánticas para el control de versiones binarias.
24Cuando una expresión hace referencia a una constante, el valor de la constante se obtiene en tiempo de
25compilación, pero cuando una expresión hace referencia a un campo de sólo lectura, el valor del campo se
26obtiene en tiempo de ejecución. Considere una aplicación que conste de dos programas independientes:

27 using System;
28 namespace Program1

29 {

30 public class Utils

31 {

32 public static readonly int X = 1;


35 namespace Program2
33
36 {
34
37 class Test

38 {

39 static void Main() {

40Los espacios de nombres


44 Console.WriteLine(Program1.Utils.X);
Program1 y Program2 indican dos programas que se compilan de forma
45
41 independiente. Debido a que el campo Program1.Utils.X se declara como de sólo lectura estático, el resultado
46de la instrucción Console.WriteLine no se conoce en tiempo de compilación, sino que se obtiene en tiempo de
42
47ejecución. Por lo tanto, si se cambia el valor de X y se vuelve a compilar Program1, la instrucción
48
43Console.WriteLine genera el nuevo valor aunque no se vuelva a compilar Program2. No obstante, si X

519Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 247


520Especificación del lenguaje C#

1hubiera sido una constante, el valor de X se habría obtenido al compilar Program2 y no se vería afectado por
2los cambios realizados en Program1 hasta que se volviera a compilar Program2.

310.4.3 Campos volatile


4Cuando una declaración de campo (field-declaration) incluye un modificador vo la t i l, elos campos presentados
5en la declaración son campos volátiles.
6Para los campos no volátiles, las técnicas de optimización que reordenan las instrucciones pueden dar resultados
7inesperados e impredecibles en programas multiproceso que tengan acceso no sincronizado a campos, como los
8que proporciona la instrucción lock (lock-statement) (§8.12). Estas optimizaciones pueden ser realizadas por el
9compilador, el sistema en tiempo de ejecución o el hardware. Para campos volátiles, tales optimizaciones de
10reordenación están restringidas:
11• La lectura de los campos volátiles se denomina lectura volátil. Una lectura volátil tiene “semántica de
12 adquisición”: está garantizado que se produce antes de cualquier referencia a la memoria que tenga lugar
13 con posterioridad a ella en la secuencia de la instrucción.
14• La escritura de campos volátiles se denomina escritura volátil. Una escritura volátil tiene “semántica de
15 liberación”: está garantizado que se produce después de cualquier referencia a la memoria que tenga lugar
16 con anterioridad a la instrucción de escritura en la secuencia de la instrucción.
17Estas restricciones aseguran que todos los subprocesos reconocerán las escrituras volátiles realizadas por
18cualquier otro subproceso en el orden en que se realizaron. No se requiere de una implementación correcta que
19proporcione una sola reordenación total de las escrituras volátiles desde la perspectiva de todos los subprocesos
20de ejecución. El tipo de un campo volátil debe ser uno de los siguientes:
21• Un tipo de referencia (reference-type).
22• El tipo byte, sby te, short, ushort, int, uint, char, float, bool, System.IntPtr o System.UIntPtr.
23• Un tipo enum (enum-type) con un tipo base enum de byte, sbyte, short, ushort, int o uint.
24En el ejemplo
25 us ing Sys tem;
26 us ing Sys tem.Th read ing ;
27 c lass Tes t
28 {
29 pub l i c s ta t i c i n t resu l t ;
30 pub l i c s ta t i c vo la t i l e boo l f i n i shed ;
31 s ta t i c vo id Thread2 ( ) {
32 resu l t = 143 ;
33 f i n i shed = t rue ;
34 }
35 s ta t i c vo id Main ( ) {
36 f i n i shed = f a l se ;
37 / / Run Thread2 ( ) i n a new th read
38 new Thread(new ThreadSta r t (Th read2 ) ) . S ta r t ( ) ;

521248 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


522 Capítulo 18 Código no seguro

1 // Wait for Thread2 to signal that it has a result by setting

2 // finished to true.

3 for (;;) {

4 if (finished) {

5 Console.WriteLine("result = {0}", result);

6 return;
117produce el resultado:

128 result = 143


13En este ejemplo, el método Main inicia un nuevo subproceso que ejecuta el método Thread2. Este método
149almacena un valor en un campo no volátil denominado result y a continuación almacena true en el campo
15
10volátil finished. El subproceso principal espera a que se establezca el campo finished en true y a continuación
16lee el campo result. Dado que se declaró finished como volatile, el subproceso principal debe leer el valor
17143 del campo result. Si el campo finished no se hubiera declarado como volatile, estaría permitido que la
18operación de almacenamiento en result fuese visible para el subproceso principal después del almacenamiento
19en finished, y por tanto también lo sería que el subproceso principal leyese el valor 0 del campo result.
20Declarar finished como un campo volatile impide tales incoherencias.

2110.4.4 Inicialización de campos


22El valor inicial de un campo, tanto si se trata de un campo estático como de un campo de instancia, es el valor
23predeterminado (§5.2) del tipo del campo. No es posible observar el valor de un campo antes de que ocurra su
24inicialización predeterminada y, por lo tanto, un campo nunca es “desinicializado”. En el ejemplo

25 using System;
26 class Test

27 {

28 static bool b;
30 static void Main() {
29
31 Test t = new Test();

32 Console.WriteLine("b = {0}, i = {1}", b, t.i);


35
33produce el resultado
34
36 b = False, i = 0
37porque b e i se inicializan automáticamente a valores predeterminados.

3810.4.5 Inicializadores de variables


39Las declaraciones de campos pueden incluir inicializadores de variable (variable-initializers). En campos
40estáticos, los inicializadores de variables corresponden a instrucciones de asignación que se ejecutan durante la
41inicialización de la clase. En campos de instancia, los inicializadores de variables corresponden a instrucciones
42de asignación que se ejecutan cuando se crea una instancia de la clase.
43En el ejemplo

44 using System;

523Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 249


524Especificación del lenguaje C#

1 class Test

2 {

3 static double x = Math.Sqrt(2.0);

46 static void Main() {


57 Test a = new Test();

8 Console.WriteLine("x = {0}, i = {1}, s = {2}", x, a.i, a.s);


119produce el resultado
10
12 x = 1.4142135623731, i = 100, s = Hello
13porque la asignación de x se produce cuando se ejecutan los inicializadores de campos estáticos y las
14asignaciones a i y a s se producen cuando se ejecutan los inicializadores de campos de instancia.
15La inicialización al valor predeterminado descrita en §10.4.4 ocurre para todos los campos, incluyendo campos
16que tienen inicializadores de variables. Por lo tanto, cuando se inicializa una clase, primero se inicializan todos
17sus campos estáticos a sus valores predeterminados y, a continuación, se ejecutan los inicializadores de campos
18estáticos en orden textual. Igualmente, cuando se crea una instancia de una clase, primero se inicializan todos
19sus campos de instancia a sus valores predeterminados y, a continuación, se ejecutan los inicializadores de
20campos de instancia en orden textual.
21Es posible observar campos estáticos con inicializadores de variables en su estado de valor predeterminado. Sin
22embargo, está totalmente desaconsejado por razones de estilo. En el ejemplo

23 using System;
24 class Test

25 {

26 static int a = b + 1;
28 static void Main() {
27
29 Console.WriteLine("a = {0}, b = {1}", a, b);

30 }
32expone este comportamiento. A pesar de las definiciones circulares de a y b, el programa es correcto. Da como
33
31resultado
34 a = 1, b = 2
35porque los campos estáticos a y b se inicializan a 0 (el valor predeterminado para int) antes de que se ejecuten
36sus inicializadores. Cuando se ejecuta el inicializador para a, el valor de b es cero y a se inicializa a 1. Cuando
37se ejecuta el inicializador para b, el valor de a ya es 1 y b se inicializa a 2.

3810.4.5.1 Inicialización de campos estáticos


39Los inicializadores de variable de un campo estático de una clase corresponden a una secuencia de asignaciones
40que se ejecutan en el orden textual en que aparecen en la declaración de la clase. Si en la clase existe un
41constructor estático (§10.11), la ejecución de los inicializadores de campo estático se produce inmediatamente
42antes de la ejecución de tal constructor estático. En caso contrario, los inicializadores de campo estático se
43ejecutan en un momento que depende de la inicialización y que es anterior a la primera utilización de un campo
44estático de esta clase. En el ejemplo

45 using System;

525250 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


526 Capítulo 18 Código no seguro

1 class Test

2 {

3 static void Main() {

46 public static int F(string s) {


57 Console.WriteLine(s);

8 return 1;

119 class A
10
12 {

13 public static int X = Test.F("Init A");


15 class B
14
16 {

17 public static int Y = Test.F("Init B");


19podría producir el resultado:
18
20 Init A

21 Init B
23
22o el siguiente:
24 Init B

25 Init A
27porque la ejecución de los inicializadores de X e Y puede producirse en cualquier orden; sólo es imprescindible
26
28que se produzca antes de las referencias a esos campos. No obstante, en el ejemplo

29 using System;
30 class Test

31 {

32 static void Main() {

33
35 public static int F(string s) {
34
36 Console.WriteLine(s);

37 return 1;

38
40 class A
39
41 {

42
43 public static int X = Test.F("Init A");

44 }
45 class B

46 {

47

527Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 251


528Especificación del lenguaje C#

1 public static int Y = Test.F("Init B");

23el resultado
} debe ser:

4 Init B

5 Init A
76porque las reglas de ejecución de los constructores estáticos (como se define en §10.11) dictan que el
8constructor estático de B (y por lo tanto, los inicializadores de campo estático de B) debe ejecutarse antes que el
9constructor estático de A y sus inicializadores de campo.

1010.4.5.2 Inicialización de campos de instancia


11Los inicializadores de variable de un campo de instancia de una clase corresponden a una secuencia de
12asignaciones que se ejecutan inmediatamente al entrar en cualquiera de los constructores de instancia (§10.10.1)
13de la clase. Los inicializadores de variable se ejecutan en el orden textual en que aparecen en la declaración de
14clase. El proceso de creación e inicialización de una instancia de clase se describe con más detalle en §10.10.
15Un inicializador de variable para un campo de instancia no puede hacer referencia a la instancia que está
16creando. Por lo tanto, supone un error en tiempo de compilación que en un inicializador de variable se haga
17referencia a th i so a un miembro de instancia mediante un nombre simple (simple-name). En el siguiente
18ejemplo:
19 c lass A
20 {
21 i n t x = 1;
22 i n t y = x + 1; / / Er ro r , re fe rence to i n s tance member o f th i s
23
24el inicializador de variable para y produce un error en tiempo de compilación porque hace referencia a un
25miembro de la instancia que se está creando.

2610.5 Métodos
27Un método es un miembro que implementa un cálculo o una acción que puede realizar un objeto o una clase.
28Los métodos se declaran mediante declaraciones de métodos (method-declarations):
29 method-declaration:
30 method-header method-body
31 method-header:
32 attributesopt method-modifiersopt return-type member-name ( formal-parameter-listopt )
33 method-modifiers:
34 method-modifier
35 method-modifiers method-modifier

529252 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


530 Capítulo 18 Código no seguro

1 method-modifier:
2 new
3 pub l i c
4 pro tec ted
5 i n te rna l
6 pr i va te
7 s ta t i c
8 v i r tua l
9 sea led
10 ove r r i de
11 abs t rac t
12 exte rn
13 return-type:
14 type
15 vo id
16 member-name:
17 identifier
18 interface-type . identifier
19 method-body:
20 block
21 ;
22Una declaración de método (method-declaration) puede incluir un conjunto de atributos (§17) y una
23combinación válida de los cuatro modificadores de acceso (§10.2.3),new (§10.2.2), s ta t i c(§10.5.2), virtual
24(§10.5.3), override (§10.5.4), sealed (§10.5.5), abstract (§10.5.6) y extern (§10.5.7).
25Una declaración tiene una combinación válida de modificadores si se cumplen las siguientes condiciones:
26• La declaración incluye una combinación válida de modificadores de acceso (§10.2.3).
27• La declaración no incluye el mismo modificador varias veces.
28• La declaración incluye al menos uno de los modificadores siguientes: s ta t i,cv i r tuayl override.
29• La declaración incluye como máximo uno de los modificadores siguientes: new y override.
30• Si la declaración incluye el modificador abstract, no podrá incluir ninguno de los siguientes modificadores:
31 static, virtual, sealed o extern.
32• Si la declaración incluye el modificador private, no podrá incluir ninguno de los siguientes modificadores:
33 virtual, override o abstract.
34• Si la declaración incluye el modificador sealed, también incluirá el modificador override.
35El tipo de valor devuelto (return-type) de una declaración de método especifica el tipo del valor calculado y
36devuelto por el método. Si el método no devuelve un valor, el tipo del valor devuelto (return-type) es void.
37El nombre de miembro (member-name) especifica el nombre del método. Salvo que el método sea una
38implementación de miembro de interfaz explícita (§13.4.1), el nombre de miembro (member-name) es
39simplemente un identificador. Para una implementación de miembro de interfaz explícita, el nombre de
40miembro (member-name) consiste en un tipo de interfaz (interface-type) seguido de “.” y un identificador.
41La lista de parámetros formales (formal-parameter-list) opcional especifica los parámetros del método
42(§10.5.1).

531Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 253


532Especificación del lenguaje C#

1El tipo de valor devuelto (return-type) y cada uno de los tipos a los que se hace referencia en la lista de
2parámetros formales (formal-parameter-list de un método deben tener como mínimo el mismo nivel de
3accesibilidad que el método (§3.5.4).
4Para los métodos abs t rac ty ex te rn, el cuerpo del método (method-body) consiste simplemente en un punto y
5coma. Para el resto de métodos, el cuerpo del método (method-body) consiste en un bloque (block) que
6especifica las instrucciones a ejecutar cuando se invoca el método.
7El nombre y la lista de parámetros formales de un método definen la firma (§3.6) del método. En concreto, la
8firma de un método se compone del nombre del método y del número, modificadores y tipos de sus parámetros
9formales. El tipo de valor devuelto no forma parte de la firma de un método, ni tampoco los nombres de los
10parámetros formales.
11El nombre de un método debe ser diferente de los nombres del resto de miembros que no sean métodos
12declarados en la misma clase. Además, la firma de un método debe ser distinta de las firmas de todos los demás
13métodos declarados en la misma clase, y dos métodos declarados en la misma clase no pueden tener firmas que
14sólo se diferencien por re fy out.

1510.5.1 Parámetros de métodos


16Los parámetros de un método, si existen, se declaran mediante la lista de parámetros formales (formal-
17parameter-list) del método.
18 formal-parameter-list:
19 fixed-parameters
20 fixed-parameters , parameter-array
21 parameter-array
22 fixed-parameters:
23 fixed-parameter
24 fixed-parameters , fixed-parameter
25 fixed-parameter:
26 attributesopt parameter-modifieropt type identifier
27 parameter-modifier:
28 re f
29 out
30 parameter-array:
31 attributesopt params array-type identifier
32La lista de parámetros formales consiste en uno o varios parámetros separados por comas de los cuales sólo el
33último puede ser una matriz de parámetros (parameter-array).
34Un parámetro fijo (fixed-parameter) consiste en un conjunto opcional de atributos (§17), un modificador re fu
35out opcional, un tipo y un identificador. Cada parámetro fijo (fixed-parameter) declara un parámetro con el tipo
36y nombre especificados.
37Una matriz de parámetros (parameter-array) consiste en un conjunto opcional de atributos (§17), un
38modificador params , un tipo de matriz (array-type) y un identificador. Una matriz de parámetros declara un
39único parámetro con el tipo de matriz y nombre especificados. El tipo de matriz (array-type) matriz de
40parámetros debe ser unidimensional (§12.1). Cuando se llama a un método, la matriz de parámetros permite
41especificar un único argumento del tipo de matriz dado, o cero o varios argumentos del tipo de elemento de
42matriz. Las matrices de parámetros se describen más detalladamente en §.
43Una declaración de método crea un espacio de declaración independiente para los parámetros y las variables
44locales. Los nombres son introducidos en este espacio de declaración mediante la lista de parámetros formales

533254 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


534 Capítulo 18 Código no seguro

1del método y mediante las declaraciones de variables locales en el bloque (block) del método. Que dos
2miembros de un espacio de declaración del método tengan el mismo nombre supone un error. Que el espacio de
3declaración del método y el espacio de declaración de la variable local de un espacio de declaración anidado
4contengan elementos con el mismo nombre supone un error.
5La invocación de un método (§7.5.5.1) crea una copia, específica de esa invocación, de los parámetros formales
6y de las variables locales del método, y la lista de argumentos de la invocación asigna valores o referencias a
7variables a los parámetros formales recién creados. Dentro del bloque (block) de un método, se puede hacer
8referencia a los parámetros formales mediante sus identificadores en expresiones de nombre simple (simple-
9name) (§7.5.2).
10Existen cuatro tipos de parámetros formales:
11• Parámetros de valor, que se declaran sin modificadores.
12• Parámetros de referencia, que se declaran con el modificador re f.
13• Parámetros de salida, que se declaran con el modificador out.
14• Matrices de parámetros, que se declaran con el modificador params .
15Como se describe en §3.6, los modificadores re fy out forman parte de la firma de un método, pero no el
16modificador params.

1710.5.1.1 Parámetros de valor


18Un parámetro declarado sin modificadores es un parámetro de valor. Un parámetro de valor corresponde a una
19variable local que obtiene su valor inicial del correspondiente argumento proporcionado en la invocación del
20método.
21Cuando un parámetro formal es un parámetro de valor, el argumento correspondiente de la invocación del
22método debe ser una expresión de un tipo que sea convertible implícitamente (§6.1) al tipo del parámetro
23formal.
24Un método puede asignar nuevos valores a un parámetro de valor. Tales asignaciones sólo afectan a la ubicación
25de almacenamiento local representada por el parámetro de valor, no tienen ningún efecto sobre el argumento real
26definido en la invocación del método.

535Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 255


536Especificación del lenguaje C#

110.5.1.2 Parámetros de referencia


2Un parámetro de referencia es un parámetro que se declara con un modificador re f. A diferencia de un
3parámetro de valor, un parámetro de referencia no crea una nueva ubicación de almacenamiento. En lugar de
4ello, un parámetro de referencia representa la misma ubicación de almacenamiento que la variable especificada
5como argumento en la invocación del método.
6Cuando un parámetro formal es un parámetro de referencia, el argumento correspondiente de la invocación del
7método se debe componer de la palabra clave re fseguida de una referencia de variable (variable-reference)
8(§5.3.3) del mismo tipo del parámetro formal. Una variable debe estar asignada de manera definitiva antes de
9que se pueda pasar como parámetro de referencia.
10Dentro de un método, un parámetro de referencia siempre se considera asignado de manera definitiva.
11En el ejemplo
12 us ing Sys tem;
13 c lass Tes t
14 {
15 s ta t i c vo id Swap( re f i n t x , re f i n t y ) {
16 i n t temp = x ;
17 x = y;
18 y = temp;
20
19 s ta t i c vo id Main ( ) {
21 i n t i = 1, j = 2;
22 Swap( re f i , re f j ) ;
23 Conso le .Wr i teL ine ( " i = {0} , j = {1}" , i , j ) ;
24 }
26
25 produce el resultado
27 i = 2, j = 1
28Para la invocación de Swap en Main, x representa a i e y representa a j. Por lo tanto, la invocación tiene el
29efecto de intercambiar los valores de i y de j.
30En un método que toma parámetros de referencia, varios nombres pueden representar la misma ubicación de
31almacenamiento. En el siguiente ejemplo:
32 c lass A
33 {
34 s t r i ng s ;
35 vo id F ( re f s t r i ng a , re f s t r i ng b) {
36 s = "One" ;
37 a = " Two" ;
38 b = "Th ree" ;
39
40 vo id G( ) {
41 F ( re f s , re f s ) ;
42 }
43 }

537256 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


538 Capítulo 18 Código no seguro

1la invocación de F en G pasa una referencia a s para a y b. De esta forma, en esa invocación, los nombres s, a y
2b hacen referencia todos ellos a la misma ubicación de almacenamiento, y las tres asignaciones modifican el
3campo de instancia s.

410.5.1.3 Parámetros de salida


5Un parámetro de salida es un parámetro que se declara con un modificador out. Al igual que un parámetro de
6referencia, un parámetro de salida no crea una nueva ubicación de almacenamiento. En lugar de ello, un
7parámetro de salida representa la misma ubicación de almacenamiento que la variable especificada como
8argumento en la invocación del método.
9Cuando un parámetro formal es un parámetro de salida, el argumento correspondiente de la invocación del
10método se debe componer de la palabra clave out seguida de una referencia de variable (variable-reference)
11(§5.3.3) del mismo tipo del parámetro formal. Una variable no necesita estar asignada de manera definitiva antes
12de ser pasada como parámetro de salida, pero si se hace un seguimiento de una invocación donde se pasó una
13variable como parámetro de salida, la variable se considera asignada de manera definitiva.
14Dentro de un método, de manera similar a una variable local, un parámetro de salida se considera inicialmente
15no asignado y debe ser asignado de manera definitiva antes de utilizar su valor.
16Cada parámetro de salida de un método debe estar asignado de manera definitiva antes de que el método
17devuelva la llamada.
18Los parámetros de salida se utilizan normalmente en métodos que devuelven varios valores. Por ejemplo:
19 us ing Sys tem;
20 c lass Tes t
21 {
22 s ta t i c vo id Sp l i t Pa th (s t r i ng path , out s t r i ng d i r , out s t r i ng name) {
23 i n t i = path . Length ;
24 whi l e ( i > 0) {
25 char ch = path [ i – 1] ;
26 i f ( ch == ' \ \ ' | | ch == ' / ' | | ch == ' : ' ) break ;
27 i--;
28 }
29 d i r = path .Subs t r i ng (0 , i ) ;
32 s ta t i c vo id Main ( ) {
30
33 s t r i ng d i r , name;
31
34 Sp l i t Pa th ( " c : \ \W indows \ \Sys tem\ \he l l o . t x t " , out d i r , out name) ;
35 Conso le .Wr i teL ine (d i r ) ;
36 Conso le .Wr i teL ine (name) ;
37 }
39El ejemplo produce el resultado:
38
40 c : \W indows \Sys tem\
41 he l l o . t x t
42Observe que las variables dir y name pueden estar sin asignar antes de que sean pasadas a SplitPath, y que se
43las considera asignadas de manera definitiva siguiendo la llamada.

539Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 257


540Especificación del lenguaje C#

110.5.1.4 Matrices de parámetros


2Un parámetro declarado con un modificador params es una matriz de parámetros. Si una lista de parámetros
3formales incluye una matriz de parámetros, ésta debe ser el último parámetro de la lista y debe ser de tipo
4unidimensional. Por ejemplo, los tipos s t r i ng [y] string[][] se pueden utilizar como tipo de una matriz de
5parámetros, pero el tipo string[,] no. No se puede combinar el modificador params con los modificadores ref
6y out.

541258 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


542 Capítulo 18 Código no seguro

1Una matriz de parámetros permite especificar argumentos en cualquiera de las dos formas de una invocación de
2método:
3• El argumento especificado para una matriz de parámetros puede ser una única expresión de un tipo que sea
4 convertible implícitamente (§6.1) al tipo de la matriz de parámetros. En este caso, la matriz de parámetros
5 actúa exactamente como un parámetro de valor.
6• Alternativamente, la invocación puede especificar cero o más argumentos para la matriz de parámetros,
7 donde cada argumento es una expresión de un tipo implícitamente convertible (§6.1) al tipo de elemento de
8 la matriz de parámetros. En este caso, la invocación crea una instancia del tipo de la matriz de parámetros
9 con una longitud correspondiente al número de argumentos, inicializa los elementos de la instancia de
10 matriz con los valores de los argumentos especificados y utiliza la instancia de matriz recién creada como
11 argumento real.
12Excepto en lo que se refiere a permitir un número variable de argumentos en una invocación, una matriz de
13parámetros equivale exactamente a un parámetro de valor (§10.5.1.1) del mismo tipo.
14En el ejemplo

15 using System;
16 class Test

17 {

18 static void F(params int[] args) {

19 Console.Write("Array contains {0} elements:", args.Length);

20 foreach (int i in args)


24
21 static void Main() {

25
22 int[] arr = {1, 2, 3};

26
23 F(arr);

27 F(10, 20, 30, 40);

28
31produce el resultado
F();

29
32 Array contains 3 elements: 1 2 3
30
33 Array contains 4 elements: 10 20 30 40
35
34La primera invocación de F simplemente pasa la matriz a como un parámetro de valor. La segunda invocación
36de F crea automáticamente una matriz int[] de cuatro elementos con los valores de elemento especificados y
37pasa esa instancia de matriz como parámetro de valor. Del mismo modo, la tercera invocación de F crea una
38matriz int[] de cero elementos y pasa esa instancia como parámetro de valor. Las invocaciones segunda y
39tercera equivalen exactamente a escribir:

40 F(new int[] {10, 20, 30, 40});

41Cuando F(new
42 int[]
se realiza {}); de sobrecarga, un método con una matriz de parámetros puede ser aplicable en
la resolución
43su forma normal o en su forma expandida (§7.4.2.1). La forma expandida de un método está disponible sólo si
44no es aplicable la forma normal del método y únicamente cuando no esté declarado ya en el mismo tipo un
45método con la misma firma que la forma expandida.

543Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 259


544Especificación del lenguaje C#

1En el ejemplo

2 using System;
3 class Test

4 {

5 static void F(params object[] a) {

68 static void F() {


79 Console.WriteLine("F()");

10
11 static void F(object a0, object a1) {

12 Console.WriteLine("F(object,object)");

13
14 static void Main() {

15 F();

16 F(1);

17 F(1, 2);

18 F(1, 2, 3);
22produce el resultado
19
23
20 F();

24
21 F(object[]);

25 F(object,object);
28
26En el ejemplo, dos de las posibles formas expandidas del método con una matriz de parámetros ya están
29incluidas en la clase como métodos regulares. Por lo tanto, estas formas expandidas no son consideradas cuando
27se realiza la resolución de sobrecarga, y las invocaciones del primer y tercer método seleccionan los métodos
30
31regulares. Cuando una clase declara un método con una matriz de parámetros, se suelen incluir también algunas
32de las formas expandidas como métodos regulares. Así es posible evitar la asignación de una instancia de matriz
33que se produce cuando se invoca una forma expandida de un método con una matriz de parámetros.
34Cuando el tipo de una matriz de parámetros es ob jec t [, ]puede producirse una ambigüedad entre la forma
35normal del método y la forma expandida para un único parámetro ob jec t. La razón de la ambigüedad es que un
36tipo object[] es implícitamente convertible al tipo object. La ambigüedad no representa un problema, ya que se
37puede resolver insertando una conversión si es necesario.
38En el ejemplo

39 using System;
40 class Test

41 {

42 static void F(params object[] args) {

43 foreach (object o in args) {

44 Console.Write(o.GetType().FullName);

45 Console.Write(" ");

46
545260 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
47
48
546 Capítulo 18 Código no seguro

1 static void Main() {

2 object[] a = {1, "Hello", 123.456};

3 object o = a;

4 F(a);

5 F((object)a);

106produce el resultado
F(o);

7
11 System.Int32 System.String System.Double
8
12 System.Object[]
9
13 System.Object[]
15En la primera y última invocación de F, la forma normal de F es aplicable porque existe una conversión
16
14implícita del tipo del argumento al tipo del parámetro (ambos son de tipo object[]). Por lo tanto, la resolución
17de sobrecarga selecciona la forma normal de F y el argumento se pasa como un parámetro de valor regular. En
18las invocaciones segunda y tercera, la forma normal de F no es aplicable porque no existe una conversión
19implícita del tipo del argumento al tipo del parámetro (el tipo object no se puede convertir implícitamente al
20tipo object[]). Sin embargo, la forma expandida de F es aplicable, y por ello la selecciona la resolución de
21sobrecarga. Como resultado, la invocación crea una matriz object[] de un elemento, y dicho elemento de la
22matriz se inicializa con el valor del argumento especificado (que a su vez es una referencia a un object[]).

2310.5.2 Métodos estáticos y de instancia


24Cuando una declaración de método incluye un modificador static, se dice que el método es un método estático.
25Si no existe un modificador static, se dice que el método es un método de instancia.
26Un método estático no opera en una instancia específica, y se produce un error en tiempo de compilación al
27hacer referencia a this en un método estático.
28Un método de instancia opera en una determinada instancia de una clase y es posible tener acceso a dicha
29instancia con this (§7.5.7).
30Cuando se hace referencia a un método en un acceso a miembro (member-access) (§7.5.4) de la forma E .M, si M
31es un método estático, E debe denotar un tipo que contenga M, y si M es un método de instancia, E debe denotar
32una instancia de un tipo que contenga M.
33Las diferencias entre miembros estáticos y de instancia se tratan con más detalle en §10.2.5.

3410.5.3 Métodos virtuales


35Cuando una declaración de método de instancia incluye un modificador virtual, se dice que el método es un
36método virtual. Si no existe un modificador virtual, se dice que el método es un método no virtual.
37La implementación de un método no virtual es invariable. La implementación es la misma tanto si se invoca un
38método en una instancia de la clase en la que se declaró o en una instancia de una clase derivada. En cambio, la
39implementación de un método virtual se puede sustituir por clases derivadas. El proceso de sustitución de la
40implementación de un método virtual heredado es conocido como reemplazamiento del método (§10.5.4).
41En la invocación de un método virtual, el tipo en tiempo de ejecución de la instancia para la que tiene lugar la
42invocación determina la implementación del método real a invocar. Cuando se invoca un método no virtual, el
43factor determinante es el tipo en tiempo de compilación de la instancia. Concretamente, cuando se invoca un
44método denominado N con una lista de argumentos A en una instancia con un tipo C en tiempo de compilación y

547Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 261


548Especificación del lenguaje C#

1un tipo R en tiempo de ejecución (donde R es C o una clase derivada de C), la invocación se procesa de la forma
2siguiente:
3• En primer lugar, la resolución de sobrecarga se aplica a C, N y A para seleccionar un método específico M
4 del conjunto de métodos declarados en y heredados por C. Este proceso se describe en §7.5.5.1.
5• A continuación, si M es un método no virtual, se invoca M.
6• Si no, M es un método virtual, y se invoca la implementación más derivada de M con respecto a R.
7Para cada método virtual que se ha declarado en una clase o heredado por ella, existe una implementación más
8derivada del método con respecto a esa clase. La implementación más derivada de un método virtual M con
9respecto a una clase R está determinada de la siguiente manera:
10• Si R contiene la declaración virtual de M, ésta es la implementación más derivada de M.
11• En caso contrario, si R contiene un override de M, ésta es la implementación más derivada de M.
12• En caso contrario, la implementación más derivada de M con respecto a R es la misma que la
13 implementación más derivada de M con respecto a la clase base directa de R.
14En el siguiente ejemplo se ilustran las diferencias entre los métodos virtuales y los métodos no virtuales:

15 using System;
16 class A

17 {

18
19 public virtual void G() { Console.WriteLine("A.G"); }

20 }
21 class B: A

22 {

23
24 public override void G() { Console.WriteLine("B.G"); }

25 }
26 class Test

27 {

28 static void Main() {

29 B b = new B();

30 A a = b;

31 a.F();

32 b.F();
37En el ejemplo, A introduce un método no virtual F y un método virtual G. La clase B introduce un nuevo método
38
33no virtual F, con lo cual oculta el método F heredado y además reemplaza el método G heredado. El ejemplo
39produce el resultado:
34
40
35 A.F

41
36 B.F

42 B.G

43

549262 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


550 Capítulo 18 Código no seguro

1Observe que la instrucción a .G( )invoca a B .G, no a A.G. Esto se debe a que el tipo en tiempo de ejecución de la
2instancia (que es B), y no el tipo en tiempo de compilación (que es A), determina la implementación del método
3real que se invoca.
4Debido a que se permite que los métodos oculten métodos heredados, es posible que una clase contenga varios
5métodos virtuales con la misma firma. Esto no presenta un problema de ambigüedad puesto que todos los
6métodos, salvo el más derivado, están ocultos. En el siguiente ejemplo:

7 using System;
8 class A

9 {

10 public virtual void F() { Console.WriteLine("A.F"); }


12 class B: A
11
13 {

14 public override void F() { Console.WriteLine("B.F"); }


16 class C: B
15
17 {

18 new public virtual void F() { Console.WriteLine("C.F"); }


20 class D: C
19
21 {

22 public override void F() { Console.WriteLine("D.F"); }


24 class Test
23
25 {

26 static void Main() {

27 D d = new D();

28 A a = d;

29 B b = d;

30 C c = d;

31 a.F();
37las clases C y D contienen dos métodos virtuales con la misma firma: el que introduce A y el que introduce C. El
32método introducido por C oculta el método que se hereda de A. De este modo, la declaración override en D
38
39
33reemplaza el método introducido por C, y no es posible que D reemplace el método introducido por A. El
40ejemplo produce el resultado:
34
41 B.F
35
42 B.F
36
43 D.F
45Observe que es posible invocar el método virtual oculto mediante el acceso a una instancia de D a través de un
46tipo menos derivado en el que el método no está oculto.
44

551Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 263


552Especificación del lenguaje C#

110.5.4 Métodos de reemplazo


2Cuando una declaración de método de instancia incluye un modificador ove r r i de, se dice que el método es un
3método de reemplazo. Un método de reemplazo reemplaza un método virtual heredado con la misma firma.
4Mientras que una declaración de método virtual introduce un método nuevo, una declaración de método de
5reemplazo especializa un método virtual heredado existente proporcionando una nueva implementación de ese
6método.
7El método reemplazado por una declaración ove r r i dese conoce como método base reemplazado. Para un
8método de reemplazo M declarado en una clase C, el método base reemplazado se determina examinando cada
9clase base de C, comenzando con la clase base directa de C y continuando con cada una de las sucesivas clases
10base directas hasta encontrar un método accesible con la misma firma de M. Para encontrar el método base
11reemplazado, un método se considera accesible si es public, protected, protected internal o internal y está
12declarado en el mismo programa que C.
13Se produce un error en tiempo de compilación a menos que se cumplan todas las condiciones siguientes en una
14declaración de reemplazo:
15• Se puede encontrar un método base reemplazado como se ha descrito anteriormente.
16• El método base reemplazado es un método virtual, abstracto o de reemplazo. En otras palabras, el método
17 base reemplazado no puede ser estático ni no virtual.
18• El método base reemplazado no es un método sellado.
19• La declaración de reemplazo y el método base reemplazado tienen el mismo tipo de valor devuelto.
20• La declaración de reemplazo y el método base reemplazado tienen la misma accesibilidad declarada. Es
21 decir, una declaración de reemplazo no puede cambiar la accesibilidad del método virtual. Sin embargo, si el
22 método base reemplazado es protected (protegido) o internal (interno) y está declarado en un ensamblado
23 diferente del ensamblado que contiene el método de reemplazo, se debe proteger la accesibilidad declarada
24 del método de reemplazo.
25Una declaración de reemplazo puede obtener acceso al método base reemplazado mediante un acceso base
26(base-access) (§7.5.8). En el siguiente ejemplo:

27 class A

28 {

29
30 public virtual void PrintFields() {

31 Console.WriteLine("x = {0}", x);

32 }
34 class B: A
33
35 {

36
37 public override void PrintFields() {

38 base.PrintFields();

39 Console.WriteLine("y = {0}", y);


42
40la invocación de base .P r i n t F i e lds
en( B
) invoca al método PrintFields declarado en A. Un acceso base (base-
43access) deshabilita el mecanismo de llamada virtual y simplemente trata el método base como un método no
41virtual. Si la llamada en B se hubiera escrito ((A)this).PrintFields(), invocaría de forma recursiva el método
44

553264 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


554 Capítulo 18 Código no seguro

1Pr in t F i e lds
declarado en B, no el declarado en A ya que PrintFields es virtual y el tipo en tiempo de ejecución
2de ((A)this) es B.
3Sólo cuando un método incluye un modificador ove r r i depuede reemplazar otro método. En los demás casos,
4un método con la misma firma que un método heredado simplemente oculta el método heredado. En el siguiente
5ejemplo:
6 c lass A
7 {
8 pub l i c v i r tua l vo id F ( ) {}
9 }
10 c lass B: A
11 {
12 pub l i c v i r tua l vo id F ( ) {} / / Warn ing , h id ing i nhe r i ted F ( )
13 } F de B no incluye un modificador override y, por tanto, no reemplaza el método F en A. En realidad,
14el método
15el método F de B oculta el método en A y se emite una advertencia porque la declaración no incluye un
16modificador new.
17En el siguiente ejemplo:
18 c lass A
19 {
20 pub l i c v i r tua l vo id F ( ) {}
21 }
22 c lass B: A
23 {
24 new pr i va te vo id F ( ) {} / / Hides A.F wi th in B
25 }
26 c lass C: B
27 {
28 pub l i c ove r r i de vo id F ( ) {} / / Ok, ove r r i des A.F
29 } F en B oculta el método F virtual heredado de A. Como el nuevo método F de B tiene acceso privado,
30el método
31su ámbito sólo incluye el cuerpo de la clase de B y no se extiende hacia C. Por tanto, se permite la declaración
32de F en C para reemplazar el método F que ha heredado de A.

3310.5.5 Métodos sellados


34Cuando una declaración de método de instancia incluye un modificador sealed, se dice que el método es un
35método sellado. Si una declaración de método de instancia incluye el modificador sealed, también debe incluir
36el modificador override. El uso del modificador sealed impide que una clase derivada siga reemplazando el
37método.
38En el ejemplo
39 us ing Sys tem;
40 c lass A
41 {
42 pub l i c v i r tua l vo id F ( ) {
43 Conso le .Wr i teL ine ( "A .F " ) ;
44

555Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 265


556Especificación del lenguaje C#

1 public virtual void G() {

2 Console.WriteLine("A.G");

3 }
5 class B: A
4
6 {

7 sealed override public void F() {

108 override public void G() {

119 Console.WriteLine("B.G");

12 }
14 class C: B
13
15 {

16 override public void G() {

17 Console.WriteLine("C.G");
20la clase B proporciona dos métodos de reemplazo: un método F que tiene el modificador sealed y un método G
18que no lo tiene. El uso en B del modificador sealed impide que C siga reemplazando F.
21
19
2210.5.6 Métodos abstractos
23Cuando una declaración de método de instancia incluye un modificador abstract, se dice que el método es un
24método abstracto. Aunque un método abstracto es también implícitamente un método virtual, no puede tener el
25modificador virtual.
26Una declaración de método abstracto introduce un nuevo método virtual pero no proporciona una
27implementación del método. En cambio, es necesario que las clases derivadas no abstractas proporcionen su
28propia implementación mediante el reemplazo del método. Debido a que un método abstracto no proporciona
29una implementación real, el cuerpo del método (method-body) de un método abstracto consiste simplemente en
30un punto y coma.
31Las declaraciones de métodos abstractos sólo se permiten en clases abstractas (§10.1.1.1).
32En el siguiente ejemplo:

33 public abstract class Shape

34 {

35 public abstract void Paint(Graphics g, Rectangle r);


37 public class Ellipse: Shape
36
38 {

39 public override void Paint(Graphics g, Rectangle r) {

40 g.DrawEllipse(r);
43
41 public class Box: Shape

44
42 {

45 public override void Paint(Graphics g, Rectangle r) {

46 g.DrawRect(r);

47
557266 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
48
558 Capítulo 18 Código no seguro

1la clase Shape define la noción abstracta de un objeto de forma geométrica que puede dibujarse a sí mismo. El
2método Pa in tes abstracto porque no existe una implementación predeterminada significativa. Las clases Ellipse
3y Box son implementaciones Shape concretas. Ya que estas clases no son abstractas, son necesarias para
4reemplazar el método Paint y proporcionar una implementación real.
5Supone un error en tiempo de compilación que un acceso base (base-access) (§7.5.8) haga referencia a un
6método abstracto. En el siguiente ejemplo:

7 abstract class A

8 {

9 public abstract void F();


11 class B: A
10
12 {

13 public override void F() {

14 base.F(); // Error, base.F is abstract


17se produce un error en tiempo de compilación en la llamada base.F() debido a que hace referencia a un método
15
18abstracto.
16Una declaración de método abstracto puede reemplazar un método virtual. Esto permite que una clase abstracta
19
20fuerce una nueva implementación del método en las clases derivadas, e impide la disponibilidad de la
21implementación original del método. En el siguiente ejemplo:

22 using System;
23 class A

24 {

25 public virtual void F() {

26 Console.WriteLine("A.F");
29
27 abstract class B: A

30
28 {

31 public abstract override void F();


33 class C: B
32
34 {

35 public override void F() {

36 Console.WriteLine("C.F");
39la clase A declara un método virtual, la clase B reemplaza este método con un método abstracto y la clase C
37reemplaza el método abstracto para proporcionar su propia implementación.
40
38
4110.5.7 Métodos externos
42Cuando una declaración de método incluye un modificador ex te rn, se dice que el método es un método
43externo. Los métodos externos se implementan externamente, utilizando generalmente un lenguaje distinto de
44C#. Debido a que la declaración de un método externo no proporciona una implementación real, el cuerpo del
45método (method body) de un método externo consiste simplemente en un punto y coma.

559Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 267


560Especificación del lenguaje C#

1El modificador ex te rnse suele utilizar junto con el atributo Dl l Impor t(§17.5.1), permitiendo implementar los
2métodos externos mediante Bibliotecas de vínculos dinámicos (.dll). El entorno de ejecución puede ser
3compatible con otros mecanismos que permitan las implementaciones de métodos externos.
4Cuando un método externo incluye un atributo Dl l Impor,tla declaración del método también debe incluir el
5modificador s ta t i.cEn este ejemplo se demuestra el uso del modificador extern y del atributo DllImport:

6 using System.Text;

7 using System.Security.Permissions;

89 class Path

10 {

11 [DllImport("kernel32", SetLastError=true)]
13 [DllImport("kernel32", SetLastError=true)]
12
14 static extern bool RemoveDirectory(string name);
15 [DllImport("kernel32", SetLastError=true)]

16 static extern int GetCurrentDirectory(int bufSize, StringBuilder buf);


17 [DllImport("kernel32", SetLastError=true)]

18 static extern bool SetCurrentDirectory(string name);

1910.5.8 Cuerpo del método


20
21El cuerpo de método (method-body) de una declaración de método consiste en un bloque (block) o en un punto y
22coma.
23Las declaraciones de métodos abstractos y externos no proporcionan una implementación del método, por lo que
24los cuerpos de estos métodos son simplemente un punto y coma. Para el resto de métodos, el cuerpo del método
25es un bloque (§8.2) que contiene las instrucciones a ejecutar cuando se invoca el método.
26Cuando el tipo de valor devuelto por un método es void, no está permitido que las instrucciones return (§8.9.4)
27del cuerpo del método especifiquen una expresión. Si la ejecución del cuerpo del método de un método void
28termina normalmente (es decir, el control llega al final del cuerpo del método), el método regresa al llamador.
29Cuando el tipo de valor devuelto por un método no es void, cada instrucción return del cuerpo del método debe
30especificar una expresión de un tipo convertible implícitamente al tipo de valor devuelto. El final del cuerpo de
31un método que devuelve un valor no debe ser alcanzable. En otras palabras, en los métodos que devuelven
32valores no está permitido que el control llegue al final del cuerpo del método.
33En el siguiente ejemplo:

34 class A

35 {

36
37 public int G() {

38 return 1;

39

561268 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


562 Capítulo 18 Código no seguro

1 public int H(bool b) {

2 if (b) {

3 return 1;

4 }

5 else {

106el método F que devuelve


return 0; produce un error en tiempo de compilación, porque el control puede alcanzar
un valor
117el final del cuerpo del método. Los métodos G y H son correctos porque todos los caminos de ejecución posibles
12terminan en una instrucción return que especifica un valor a devolver.
8
13910.5.9 Sobrecarga de métodos
14Las reglas de resolución de sobrecarga de métodos se describen en §7.4.2.

1510.6 Propiedades
16Una propiedad es un miembro que proporciona acceso a una característica de un objeto o una clase. Los
17ejemplos de propiedades incluyen la longitud de una cadena, el tamaño de una fuente, el título de una ventana, el
18nombre de un cliente, etc. Las propiedades son una extensión natural de los campos; ambos son miembros
19denominados con tipos asociados, y la sintaxis que se utiliza para el acceso a campos y propiedades es la misma.
20No obstante, a diferencia de los campos, las propiedades no denotan ubicaciones de almacenamiento. Las
21propiedades tienen descriptores de acceso que especifican las instrucciones que deben ejecutarse para leer o
22escribir sus valores. Las propiedades proporcionan un mecanismo para asociar acciones a la lectura y escritura
23de los atributos de un objeto. Además, permiten el cálculo de dichos atributos.
24Las propiedades se declaran mediante declaraciones de propiedad (property-declarations):
25 property-declaration:
26 attributesopt property-modifiersopt type member-name { accessor-declarations }
27 property-modifiers:
28 property-modifier
29 property-modifiers property-modifier
30 property-modifier:
31 new
32 public
33 protected
34 internal
35 private
36 static
37 virtual
38 sealed
39 override
40 abstract
41 extern
42 member-name:
43 identifier
44 interface-type . identifier

563Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 269


564Especificación del lenguaje C#

1Una declaración de propiedad (property-declaration) puede incluir un conjunto de atributos (§17) y una
2combinación válida de los cuatro modificadores de acceso (§10.2.3), los modificadores new (§10.2.2), s ta t i c
3(§10.5.2), virtual (§10.5.3), override (§10.5.4), sealed (§10.5.5), abstract (§10.5.6) y extern (§10.5.7).
4Las declaraciones de propiedad están sujetas a las mismas reglas que las declaraciones de método (§10.5) en lo
5que respecta a las combinaciones válidas de modificadores.
6El tipo (type) de una declaración de propiedad especifica el tipo de la propiedad introducida en la declaración, y
7el nombre de miembro (member-name) especifica el nombre de la propiedad. Salvo que la propiedad sea una
8implementación de miembro de interfaz explícita, el nombre de miembro (member-name) es simplemente un
9identificador. Para una implementación de miembro de interfaz explícita (§13.4.1), el nombre de miembro
10(member-name) consiste en un tipo de interfaz (interface-type) seguido de “.” y un identificador.
11El tipo (type) de una propiedad debe ser al menos tan accesible como la misma propiedad (§3.5.4).
12Las declaraciones de descriptores de acceso (accessor-declarations), que deben estar encerradas entre los
13símbolos (token) “{” y “}”, declaran los descriptores de acceso (§10.6.2) de la propiedad. Los descriptores de
14acceso especifican las instrucciones ejecutables asociadas a la lectura y escritura de la propiedad.
15Aunque la sintaxis para tener acceso a una propiedad es idéntica a la de un campo, una propiedad no está
16clasificada como variable. Por tanto, no se puede transferir una propiedad como un argumento re fu out.
17Cuando una declaración de propiedad incluye un modificador extern, se dice que la propiedad es una propiedad
18externa. Debido a que la declaración de una propiedad externa no proporciona una implementación real, cada
19una de sus declaraciones de descriptores de acceso (accesor-declarations) consiste en un punto y coma.

2010.6.1 Propiedades estáticas y de instancia


21Cuando una declaración de propiedad incluye un modificador s ta t i,cse dice que la propiedad es una propiedad
22estática. Si no existe un modificador s ta t i,cse dice que la propiedad es una propiedad de instancia.
23Una propiedad estática no está asociada a una instancia específica y supone un error en tiempo de compilación
24hacer referencia a th i sen los descriptores de acceso de una propiedad estática.
25Una propiedad de instancia está asociada a una determinada instancia de una clase y es posible tener acceso a
26dicha instancia con th i s(§7.5.7) en los descriptores de acceso de la propiedad.
27Cuando se hace referencia a una propiedad en un acceso a miembro (member-access) (§7.5.4) de la forma E.M,
28si M es una propiedad estática, E debe denotar un tipo que contenga M, y si M es una propiedad de instancia, E
29debe denotar una instancia de un tipo que contenga M.
30Las diferencias entre miembros estáticos y de instancia se tratan con más detalle en §10.2.5.

3110.6.2 Descriptores de acceso


32Las declaraciones de descriptores de acceso (accessor-declarations) de una propiedad especifican las
33instrucciones ejecutables asociadas a la lectura y escritura de la propiedad.
34 accessor-declarations:
35 get-accessor-declaration set-accessor-declarationopt
36 set-accessor-declaration get-accessor-declarationopt
37 get-accessor-declaration:
38 attributesopt get accessor-body
39 set-accessor-declaration:
40 attributesopt se t accessor-body

565270 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


566 Capítulo 18 Código no seguro

1 accessor-body:
2 block
3 ;
4Las declaraciones del descriptor de acceso consisten en una declaración de descriptor de acceso get (get-
5accessor-declaration), en una declaración de descriptor de acceso set (set-accessor-declaration) o en ambas.
6Cada declaración de descriptor de acceso consiste en el símbolo get o se t seguido de un cuerpo de descriptor de
7acceso (accessor-body). Para las propiedades abstract y extern, el cuerpo de descriptor de acceso (accessor-
8body) de cada descriptor de acceso especificado consiste simplemente en un punto y coma. Para los descriptores
9de acceso de cualquier propiedad no abstracta y no externa, el cuerpo de descriptor de acceso (accessor-body) es
10un bloque (block) que especifica las instrucciones que deben ejecutarse cuando se llame al correspondiente
11descriptor de acceso.
12Un descriptor de acceso get corresponde a un método sin parámetros que devuelve un valor del tipo de la
13propiedad. Exceptuando cuando se trata del destino de una asignación, al hacer referencia a una propiedad en
14una expresión, se invoca al descriptor de acceso get de la propiedad para calcular su valor (§7.1.1). El cuerpo de
15un descriptor de acceso get debe cumplir las reglas de los métodos que devuelven valores, descritas en §10.5.8.
16En concreto, todas las instrucciones return del cuerpo de un descriptor de acceso get deben especificar una
17expresión que sea convertible implícitamente al tipo de la propiedad. Además, el final de un descriptor de acceso
18get no debe ser alcanzable.
19Un descriptor de acceso set corresponde a un método con un solo parámetro de valor del tipo de la propiedad y
20un tipo de valor devuelto void. El parámetro implícito de un descriptor de acceso set siempre se denomina
21value. Cuando se hace referencia a una propiedad como el destino de una asignación (§7.13) o como el
22operando de los operadores ++ o -- (§7.5.9, §7.6.5), se invoca el descriptor de acceso set con un argumento
23(cuyo valor es el del lado derecho de la asignación o del operando del operador ++ o --), que proporciona el
24nuevo valor (§7.13.1). El cuerpo de un descriptor de acceso set debe cumplir las reglas de los métodos void,
25descritas en §10.5.8. En concreto, no está permitido que las instrucciones return del cuerpo de un descriptor de
26acceso set especifiquen una expresión. Debido a que un descriptor de acceso set tiene implícitamente un
27parámetro denominado value, supone un error en tiempo de compilación que una declaración de variable local
28o una declaración de constante en un descriptor de acceso set tengan ese nombre.
29Basándose en la existencia o no de los descriptores de acceso get y set, las propiedades se clasifican en:
30• Una propiedad que incluye los descriptores de acceso get y set se dice que es una propiedad de lectura-
31 escritura.
32• Una propiedad que sólo tiene un descriptor de acceso get se dice que es una propiedad de sólo lectura.
33 Supone un error en tiempo de compilación que una propiedad de sólo lectura sea el destino de una
34 asignación.
35• Una propiedad que sólo tiene un descriptor de acceso set se dice que es una propiedad de sólo escritura.
36 Salvo cuando la propiedad sea el destino de una asignación, supone un error en tiempo de compilación hacer
37 referencia en una expresión a una propiedad de sólo escritura.
38En el siguiente ejemplo:
39 pub l i c c lass But ton : Cont ro l
40 {
41 pr i va te s t r i ng capt i on ;

567Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 271


568Especificación del lenguaje C#

1 public string Caption {

2 get {

3 return caption;

4 }

5 set {

6 if (caption != value) {

7 caption = value;
12 public override void Paint(Graphics g, Rectangle r) {
8
13 // Painting code goes here
9
14 }
16el control But ton declara una propiedad pública Capt i on. El descriptor de acceso get de la propiedad Caption
10
17devuelve la cadena almacenada en el campo privado caption. El descriptor de acceso set comprueba si el
15
11nuevo valor es diferente del valor actual y, en ese caso, almacena el nuevo valor y vuelve a pintar el control. Las
18
19propiedades siguen a menudo el diseño descrito anteriormente: el descriptor de acceso get simplemente
20devuelve un valor almacenado en un campo privado, y el descriptor de acceso set modifica el campo privado y
21después realiza otras acciones adicionales necesarias para actualizar completamente el estado del objeto.
22Continuando con la clase Button definida anteriormente, en el siguiente ejemplo se muestra el uso de la
23propiedad Caption:

24 Button okButton = new Button();

25 okButton.Caption = "OK"; // Invokes set accessor


27Aquí, el descriptor de acceso set se invoca mediante la asignación de un valor a la propiedad, y el descriptor de
26
28acceso get se invoca haciendo referencia a ésta en una expresión.
29Los descriptores de acceso get y set de una propiedad no son miembros distintos, y no es posible declarar los
30descriptores de acceso de una propiedad de forma independiente. Por ello, no es posible que los dos descriptores
31de acceso de una propiedad de lectura-escritura tengan distinta accesibilidad. En el ejemplo

32 class A

33 {

34
35 public string Name { // Error, duplicate member name

36 get { return name; }

37
38 public string Name { // Error, duplicate member name

39 set { name = value; }

40 }
42no se declara una única propiedad de sólo lectura. Se declaran dos propiedades con el mismo nombre, una de
43
41sólo lectura y otra de sólo escritura. Dado que dos miembros que se declaran en la misma clase no pueden tener
44el mismo nombre, el código del ejemplo generará un error de compilación.
45Cuando una clase derivada declara una propiedad con el mismo nombre que una propiedad heredada, la
46propiedad derivada oculta la propiedad heredada tanto para lectura como para escritura. En el siguiente ejemplo:

569272 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


570 Capítulo 18 Código no seguro

1 class A

2 {

3 public int P {

4 set {...}
75 class B: A
6
8 {

9 new public int P {

10 get {...}
13
11 la propiedad P en B oculta la propiedad P en A para lectura y para escritura. Por lo tanto, en las instrucciones
12
14 B b = new B();

15 b.P = 1; // Error, B.P is read-only


17las asignaciones a b.P causan un error en tiempo de compilación del que debe informarse, ya que la propiedad P
16
18de sólo lectura en B oculta la propiedad P de sólo escritura en A. Observe, sin embargo, que se puede utilizar una
19conversión para tener acceso a la propiedad P oculta.
20A diferencia de los campos públicos, las propiedades proporcionan una separación entre el estado interno de un
21objeto y su interfaz pública. Considere este ejemplo:

22 class Label

23 {

24 private int x, y;
26 public Label(int x, int y, string caption) {
25
27 this.x = x;

28 this.y = y;

29
31 public int X {
30
32 get { return x; }

33
34 public int Y {

35 get { return y; }

36
37 public Point Location {

38 get { return new Point(x, y); }

39
40 public string Caption {

41 get { return caption; }

42 }
44Aquí, la clase Label utiliza dos campos int, x e y, para almacenar su ubicación. La ubicación se expone
45
43públicamente con las propiedades X e Y, y con la propiedad Location de tipo Point. Si en una futura versión de
46Label fuera más conveniente almacenar internamente la ubicación como un Point, se podría realizar el cambio
47sin que afectara a la interfaz pública de la clase:

571Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 273


572Especificación del lenguaje C#

1 class Label

2 {

3 private Point location;


5 public Label(int x, int y, string caption) {
4
6 this.location = new Point(x, y);

7 this.caption = caption;
9 public int X {
8
10 get { return location.x; }

11
12 public int Y {

13 get { return location.y; }

14
15 public Point Location {

16 get { return location; }

17
18 public string Caption {

19 get { return caption; }

20 }
22Si x e y fueran campos public readonly, no sería posible realizar ese cambio en la clase Label.
21La exposición del estado mediante propiedades no es menos eficiente que exponer los campos directamente. En
23
24concreto, cuando una propiedad no es virtual y contiene sólo una pequeña cantidad de código, el entorno de
25ejecución puede reemplazar las llamadas a los descriptores de acceso con el código real de los descriptores de
26acceso. Este proceso es conocido con el nombre de en línea, y permite que el acceso a las propiedades sea tan
27eficiente como el acceso a los campos, preservando la creciente flexibilidad de las propiedades.
28Puesto que invocar un descriptor de acceso get equivale conceptualmente a leer el valor de un campo, se
29considera una técnica de programación poco recomendable el que los descriptores de acceso get muestren
30efectos secundarios. En el siguiente ejemplo:

31 class Counter

32 {

33
34 public int Next {

35 get { return next++; }

36 }
38el valor de la propiedad Next depende del número de veces que se haya tenido acceso anteriormente a la
39
37propiedad. Por consiguiente, puesto que el acceso a la propiedad produce un efecto secundario observable, es
40preferible implementar la propiedad como método.
41La convención “sin efectos secundarios” que se aplica a los descriptores de acceso get no significa que éstos
42deban escribirse siempre únicamente para que devuelvan los valores almacenados en campos. De hecho, los
43descriptores de acceso get a menudo calculan el valor de una propiedad mediante el acceso a varios campos o
44invocando métodos. No obstante, un descriptor de acceso get diseñado correctamente no lleva a cabo acciones
45que generen cambios observables en el estado del objeto.
46Pueden utilizarse propiedades para retrasar la inicialización de un recurso hasta el momento en que se haga
47referencia al mismo. Por ejemplo:

573274 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


574 Capítulo 18 Código no seguro

1 using System.IO;
2 public class Console

3 {

4 private static TextReader reader;

57 public static TextReader In {


68 get {

9 if (reader == null) {

10 reader = new StreamReader(Console.OpenStandardInput());

11 }
15
12 public static TextWriter Out {

16
13 get {

17
14 if (writer == null) {

18 writer = new StreamWriter(Console.OpenStandardOutput());

19 }
23
20 public static TextWriter Error {

24
21 get {

25
22 if (error == null) {

26 error = new StreamWriter(Console.OpenStandardError());

27 }

28 returntres
32La clase Conso le contiene error;
propiedades, I n, Out y Error, que representan los dispositivos estándar de
33
29 entrada, salida y error, respectivamente. Mediante la exposición de estos miembros como propiedades, la clase
34ConsoleStreams puede retrasar su inicialización hasta que se utilicen realmente. Por ejemplo, en la primera
30referencia a la propiedad Out del código siguiente
35
31
36 Console.Out.WriteLine("hello, world");
37se crea el TextWriter subyacente para el dispositivo de salida. Pero si la aplicación no hace referencia a las
38propiedades In y Error, entonces no se crean objetos para esos dispositivos.

3910.6.3 Descriptores de acceso virtual, sellado, de reemplazo y abstracto


40Una declaración de propiedad virtual especifica que los descriptores de acceso de la propiedad son virtuales. El
41modificador virtual se aplica a los dos descriptores de acceso de una propiedad de lectura-escritura. No es
42posible que sólo uno de los descriptores de acceso de una propiedad de lectura-escritura sea virtual.
43Una declaración de propiedad abs t rac tespecifica que los descriptores de acceso de la propiedad son virtuales,
44pero no proporcionan una implementación real de los descriptores de acceso. En cambio, es necesario que las
45clases derivadas no abstractas proporcionen su propia implementación para los descriptores de acceso mediante
46el reemplazo de la propiedad. Como un descriptor de acceso para una declaración de propiedad abstracta no
47proporciona una implementación real, su cuerpo de descriptor de acceso (accesor-body) consiste simplemente
48en un punto y coma.

575Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 275


576Especificación del lenguaje C#

1Una declaración de propiedad que incluye los dos modificadores abs t rac ty ove r r i deespecifica que la
2propiedad es abstracta y que reemplaza una propiedad base. Los descriptores de acceso de este tipo de propiedad
3son también abstractos.
4Las declaraciones de propiedades abstractas sólo se permiten en clases abstractas (§10.1.1.1). Los descriptores
5de acceso de una propiedad virtual heredada se pueden reemplazar en una clase derivada incluyendo una
6declaración de propiedad que especifique una directiva ove r r i de
. Esto se conoce como declaración de
7propiedad reemplazada. Una declaración de propiedad reemplazada no declara una nueva propiedad. En su
8lugar, simplemente especializa las implementaciones de los descriptores de acceso de una propiedad virtual
9existente.
10Una declaración de propiedad reemplazada debe especificar exactamente los mismos modificadores de
11accesibilidad, tipo y nombre que la propiedad heredada. Si la propiedad heredada sólo tiene un descriptor de
12acceso (por ejemplo, si la propiedad heredada es de sólo lectura o de sólo escritura), la propiedad reemplazada
13debe incluir sólo ese descriptor de acceso. Si la propiedad heredada incluye ambos descriptores de acceso (por
14ejemplo, si la propiedad heredada es de lectura-escritura), la propiedad reemplazada puede incluir un descriptor
15de acceso o ambos.
16Una declaración de propiedad reemplazada puede incluir el modificador sea led. El uso de este modificador
17impide que una clase derivada siga reemplazando la propiedad. Los descriptores de acceso de una propiedad
18sellada también son sellados.
19Salvo en las diferencias referentes a la sintaxis de declaración e invocación, los descriptores de acceso virtuales,
20sellados, de reemplazo y abstractos se comportan exactamente igual que los métodos virtuales, sellados, de
21reemplazo y abstractos. Específicamente, las reglas descritas en las secciones §10.5.3, §10.5.4, §10.5.5 y
22§10.5.6 se aplican como si los descriptores de acceso fueran métodos con una forma correspondiente:
23• Un descriptor de acceso get corresponde a un método sin parámetros que devuelve un valor del tipo de la
24 propiedad y los mismos modificadores que la propiedad contenedora.
25• Un descriptor de acceso se t corresponde a un método con un único parámetro de valor del tipo de la
26 propiedad, un tipo de valor devuelto vo id y los mismos modificadores que la propiedad contenedora.
27En el siguiente ejemplo:

28 abstract class A

29 {

30
31 public virtual int X {

32 get { return 0; }

33
34 public virtual int Y {

35 get { return y; }

36 set { y = value; }
38 public abstract int Z { get; set; }
37
39
40X es una}propiedad virtual de sólo lectura, Y es una propiedad virtual de sólo escritura, y Z es una propiedad
41abstracta de lectura-escritura. Puesto que Z es abstracta, la clase contenedora A debe declararse como abstracta.
42A continuación se muestra una clase que se deriva de la clase A:

43 class B: A

44 {

45
577276 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
578 Capítulo 18 Código no seguro

1 public override int X {

2 get { return base.X + 1; }

34 public override int Y {

5 set { base.Y = value < 0? 0: value; }

67 public override int Z {

8 get { return z; }

9 set { z = value; }
12
10Aquí, las declaraciones de X, Y y Z son declaraciones de propiedades reemplazadas. Cada declaración de
13propiedad coincide exactamente con los modificadores de accesibilidad, tipo y nombre de la propiedad heredada
11
14correspondiente. El descriptor de acceso get de X y el descriptor de acceso set de Y utilizan la palabra clave
15base para tener acceso a los descriptores de acceso heredados. La declaración de Z reemplaza ambos
16descriptores de acceso abstractos; de este modo, no hay miembros de función abstracta pendientes en B, y ésta
17puede ser una clase no abstracta.

1810.7 Eventos
19Un evento es un miembro que permite a un objeto o una clase proporcionar notificaciones. Los clientes pueden
20adjuntar código ejecutable a los eventos mediante controladores de eventos.
21Los eventos se declaran mediante declaraciones de eventos (event-declarations):
22 event-declaration:
23 attributesopt event-modifiersopt event type variable-declarators ;
24 attributesopt event-modifiersopt event type member-name { event-accessor-declarations }
25 event-modifiers:
26 event-modifier
27 event-modifiers event-modifier
28 event-modifier:
29 new
30 public
31 protected
32 internal
33 private
34 static
35 virtual
36 sealed
37 override
38 abstract
39 extern
40 event-accessor-declarations:
41 add-accessor-declaration remove-accessor-declaration
42 remove-accessor-declaration add-accessor-declaration
43 add-accessor-declaration:
44 attributesopt add block
45 remove-accessor-declaration:
46 attributesopt remove block

579Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 277


580Especificación del lenguaje C#

1Una declaración de evento (event-declaration) puede incluir un conjunto de atributos (§17) y una combinación
2válida de los cuatro modificadores de acceso (§10.2.3), new (§10.2.2), s ta t i c(§10.5.2), virtual (§10.5.3),
3override (§10.5.4), sealed (§10.5.5), abstract (§10.5.6) y extern (§10.5.7).
4Las declaraciones de evento están sujetas a las mismas reglas que las declaraciones de método (§10.5) en lo que
5respecta a las combinaciones válidas de modificadores.
6El tipo de una declaración de evento debe ser un tipo delegado (delegate-type) (§4.2) y dicho tipo delegado debe
7ser al menos tan accesible como el propio evento (§3.5.4).
8Una declaración de evento puede incluir declaraciones de descriptores de acceso de eventos (event-accessor-
9declarations). Sin embargo, si no fuera así, el compilador las proporcionará automáticamente para los eventos
10no externos y no abstractos (§10.7.1); para los eventos externos, los descriptores de acceso se proporcionan
11externamente.
12Una declaración de evento que omite declaraciones de acceso a eventos (event-accessor-declarations) define
13uno o varios eventos, uno para cada declarador de variable (variable-declarator). Los atributos y modificadores
14se aplican a todos los miembros declarados en esa declaración de evento (event-declarator).
15Es un error en tiempo de compilación que una declaración de evento (event-declaration) incluya el modificador
16abstract y declaraciones de acceso a eventos (event-accessor-declarations) entre llaves.
17Cuando la declaración de un evento incluye un modificador extern, se dice que el evento es un evento externo.
18Debido a que una declaración de evento externo no proporciona ninguna implementación real, supone un error
19que incluir a la vez el modificador extern y las declaraciones de descriptor de acceso a eventos (event-
20accessor-declarations).
21Un evento se puede utilizar como el operando izquierdo de los operadores += y - =(§7.13.3). Estos operadores
22se utilizan para adjuntar o quitar respectivamente controladores de eventos de un evento, y los modificadores de
23acceso del evento controlan los contextos en los que se permiten estas operaciones.
24Como += y - = son las únicas operaciones permitidas en un evento fuera del tipo que declara el evento,
25mediante código externo se pueden agregar o quitar controladores de un evento, pero no se puede obtener ni
26modificar la lista subyacente de controladores de eventos de ninguna otra forma.
27En una operación de la forma x += y o x -= y, cuando x es un evento y la referencia tiene lugar fuera del tipo
28que contiene la declaración de x, el resultado de la operación es de tipo void (en oposición al tipo x, con el valor
29de x después de la asignación). Esta regla prohíbe que el código externo examine indirectamente el delegado
30subyacente de un evento.
31En el siguiente ejemplo se muestra cómo adjuntar controladores de eventos a instancias de la clase Button:
32 pub l i c de legate vo id EventHand le r (ob jec t sender , EventA rgs e ) ;
33 pub l i c c lass But ton : Cont ro l
34 {
35 pub l i c event EventHand le r C l i ck ;
36 }
37 pub l i c c lass Log inD ia log : Fo rm
38 {
39 But ton OkBut ton ;
40 But ton Cance lBu t ton ;

581278 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


582 Capítulo 18 Código no seguro

1 public LoginDialog() {

2 OkButton = new Button(...);

3 OkButton.Click += new EventHandler(OkButtonClick);

4 CancelButton = new Button(...);


75 void OkButtonClick(object sender, EventArgs e) {

86 // Handle OkButton.Click event

109 void CancelButtonClick(object sender, EventArgs e) {

11 // Handle CancelButton.Click event

12 }
14Aquí, el constructor de instancia para Log inD ia logcrea dos instancias de But ton y adjunta controladores de
15
13evento a los eventos de Click.

1610.7.1 Eventos como campos


17Dentro del texto del programa de la clase o estructura que contiene la declaración de un evento, algunos eventos
18se pueden utilizar como campos. Para utilizarlo de esta forma, un evento no debe ser abstract ni extern, ni
19debe incluir explícitamente declaraciones de descriptores de acceso a eventos (event-accessor-declarations).
20Este tipo de eventos se puede utilizar en cualquier contexto que admita campos. El campo contiene un delegado
21(§15) que hace referencia a la lista de controladores de eventos que se han agregado al evento. Si no se ha
22agregado ningún controlador de evento, el campo contiene null.
23En el siguiente ejemplo:

24 public delegate void EventHandler(object sender, EventArgs e);


25 public class Button: Control

26 {

27
28 protected void OnClick(EventArgs e) {

29 if (Click != null) Click(this, e);

30
31 public void Reset() {

32 Click = null;

33 }
35Click se utiliza como un campo dentro de la clase Button. Como se demuestra en el ejemplo, el campo se puede
36
34examinar, modificar y utilizar en expresiones de invocación a delegados. El método OnClick en la clase Button
37“produce” el evento Click. La noción de iniciar un evento equivale exactamente a invocar el delegado
38representado por el evento; por lo tanto, no hay construcciones especiales del lenguaje para producir eventos.
39Observe que la invocación del delegado está precedida por una comprobación que confirma que el delegado no
40es nulo.
41Fuera de la declaración de la clase Button, el miembro Click sólo se puede utilizar como operando izquierdo de
42los operadores += y -=, como en

43 b.Click += new EventHandler(…);


44que anexa un delegado a la lista de invocación del evento Click, y

45 b.Click –= new EventHandler(…);

583Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 279


584Especificación del lenguaje C#

1que quita un delegado de la lista de invocación del evento C l i ck.


2Al compilar un campo como un evento, el compilador crea automáticamente espacio de almacenamiento para
3alojar el delegado, y crea descriptores de acceso para el evento que agregan o quitan controladores de evento del
4campo del delegado. Para no causar ningún tipo de inseguridad en el subproceso, las operaciones de agregado o
5eliminación se efectúan bloqueando (§8.12) el objeto contenedor para un evento de instancia; o bien el objeto de
6tipo (§7.5.11) para un evento estático.
7Por lo tanto, una declaración de evento de instancia de la forma:

8 class X

9 {

10 public event D Ev;


12puede compilarse de manera equivalente a:
11
13 class X

14 {

15
16 public event D Ev {

17 add {

18 lock(this) { __Ev = __Ev + value; }


20 remove {
19
21 lock(this) { __Ev = __Ev - value; }

22 }
25
23En la clase X, sin embargo, las referencias a Ev se compilan para hacer referencia al campo oculto __Ev. El
26nombre “__Ev” es arbitrario; el campo oculto puede tener cualquier nombre o incluso no tener ninguno.
24
27De igual modo, una declaración de evento estático de la forma:

28 class X

29 {

30 public static event D Ev;


32puede compilarse de manera equivalente a:
31
33 class X

34 {

35
36 public static event D Ev {

37 add {

38 lock(typeof(X)) { __Ev = __Ev + value; }


40 remove {
39
41 lock(typeof(X)) { __Ev = __Ev - value; }

42 }

43
44

585280 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


586 Capítulo 18 Código no seguro

110.7.2 Descriptores de acceso de evento


2Las declaraciones de eventos normalmente omiten las declaraciones de descriptores de acceso a eventos (event-
3accessor-declarations), como en el ejemplo anterior de But ton. Una situación en la que puede darse es en el
4caso de que el costo de almacenamiento de un campo por evento no sea aceptable. En estos casos, una clase
5puede incluir declaraciones de descriptores de acceso de eventos (event-accessor-declarations) y utilizar un
6mecanismo privado para almacenar la lista de controladores de eventos.
7Las declaraciones de descriptores de acceso de eventos (event-accessor-declarations) de un evento especifican
8las instrucciones ejecutables asociadas a la agregación o eliminación de controladores de eventos.
9Las declaraciones del descriptor de acceso consisten en una declaración de descriptor de acceso add (add-
10accessor-declaration) y en una declaración de descriptor de acceso remove (remove-accessor-declaration).
11Cada declaración de descriptor de acceso consiste en el símbolo add o remove seguido de un bloque (block).
12El bloque (block) asociado a una declaración de descriptor de acceso add (add-accessor-declaration) especifica
13las instrucciones que se van a ejecutar cuando se agrega un controlador de evento, y el bloque (block) asociado a
14una declaración de descriptor de acceso remove (remove-accessor-declaration) especifica las instrucciones a
15ejecutar cuando se quita un controlador de evento.
16Cada declaración de descriptor de acceso add (add-accessor-declaration) y declaración de descriptor de acceso
17remove (remove-accessor-declaration) corresponde a un método con un solo parámetro de valor del tipo del
18evento y un tipo de valor devuelto vo id. El parámetro implícito de un descriptor de acceso de evento se
19denomina va lue. Cuando se utiliza un evento en una asignación de evento, se utiliza el descriptor de acceso de
20evento apropiado. Si el operador de asignación es específicamente +=, se utiliza el descriptor de acceso add y,
21si es -=, el descriptor de acceso remove. En ambos casos, el operando derecho del operador de asignación se
22utiliza como argumento para el descriptor de acceso del evento. El bloque de una declaración de descriptor de
23acceso add (add-accessor-declaration) o de una declaración de descriptor de acceso remove (remove-accessor-
24declaration) debe cumplir las reglas para los métodos void descritas en §10.5.8. En concreto, no está permitido
25que las instrucciones return del bloque especifiquen una expresión.
26Debido a que un descriptor de acceso de evento tiene implícitamente un parámetro denominado va lue, se
27producirá un error en tiempo de compilación si una variable local o una constante declaradas en un descriptor de
28acceso de evento tienen ese nombre.
29En el siguiente ejemplo:
30 c lass Cont ro l : Component
31 {
32 / / Un ique keys f o r events
33 s ta t i c readon ly ob jec t mouseDownEventKey = new ob jec t ( ) ;
34
35 / / Retu rn event hand le r assoc ia ted wi th key
36 pro tec ted De lega te GetEventHand le r (ob jec t key ) { . . . }
37 / / Add event hand le r assoc ia ted wi th key
38 pro tec ted vo id AddEventHand le r (ob jec t key , De lega te hand le r ) { . . . }
39 / / Remove event hand le r assoc ia ted wi th key
40 pro tec ted vo id RemoveEventHand le r (ob jec t key , De lega te hand le r ) { . . . }
41 / / MouseDown event
42 pub l i c event MouseEventHand le r MouseDown {
43 add { AddEventHand le r (mouseDownEventKey , va lue ) ; }
44 remove { RemoveEventHand le r (mouseDownEventKey , va lue ) ; }
45

587Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 281


588Especificación del lenguaje C#

1 // MouseUp event

2 public event MouseEventHandler MouseUp {

3 add { AddEventHandler(mouseUpEventKey, value); }

46 // Invoke the MouseUp event


57 protected void OnMouseUp(MouseEventArgs args) {

8 MouseEventHandler handler;

9 handler = (MouseEventHandler)GetEventHandler(mouseUpEventKey);

10 if (handler != null)
14la clase Cont ro limplementa un mecanismo de almacenamiento interno para los eventos. El método
11AddEventHand le r asocia un valor de delegado a una clave, el método GetEventHandler devuelve el
15
16delegado asociado actualmente a una clave, y el método RemoveEventHandler quita un delegado como
12
17controlador de evento del evento especificado. Presumiblemente, el mecanismo de almacenamiento subyacente
13
18está diseñado para que no suponga un costo el hecho de asociar un valor de delegado null a una clave y, por lo
19tanto, los eventos no controlados no consumen almacenamiento.

2010.7.3 Eventos estáticos y de instancia


21Cuando la declaración de un evento incluye un modificador static, se dice que el evento es un evento estático.
22Cuando no existe un modificador static, se dice que el evento es un evento de instancia.
23Un evento estático no está asociado a una instancia específica y supone un error en tiempo de compilación hacer
24referencia a this en los descriptores de acceso de un evento estático.
25Un evento de instancia está asociado a una determinada instancia de una clase y es posible tener acceso a la
26instancia con this (§7.5.7) en los descriptores de acceso del evento.
27Cuando se hace referencia a un evento en un acceso a miembro (member-access) (§7.5.4) de la forma E .M, si M
28es un evento estático, E debe denotar un tipo que contenga M, y si M es un evento de instancia, E debe denotar
29una instancia de un tipo que contenga M.
30Las diferencias entre miembros estáticos y de instancia se tratan con más detalle en §10.2.5.

3110.7.4 Descriptores de acceso virtual, sellado, de reemplazo y abstracto


32Una declaración de evento virtual especifica que los descriptores de acceso del evento son virtuales. El
33modificador virtual se aplica a todos los descriptores de acceso de un evento.
34Una declaración de evento abstract especifica que los descriptores de acceso del evento son virtuales, pero no
35proporciona una implementación real de los descriptores de acceso. En su lugar, es necesario que las clases
36derivadas no abstractas proporcionen su propia implementación para los descriptores de acceso mediante el
37reemplazo del evento. Debido a que un descriptor de acceso para una declaración de evento abstracto no
38proporciona una implementación real, su cuerpo de descriptor de acceso (accessor-body) consiste simplemente
39en un punto y coma.
40Una declaración de evento que incluye los modificadores abstract y override especifica que el evento es
41abstracto y que reemplaza un evento base. Los descriptores de acceso de este tipo de eventos son también
42abstractos.
43Las declaraciones de eventos abstractos sólo se permiten en clases abstractas (§10.1.1.1).

589282 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


590 Capítulo 18 Código no seguro

1Los descriptores de acceso de un evento virtual heredado pueden ser reemplazados en una clase derivada
2incluyendo una declaración de evento que especifique un modificador ove r r i de
. Esto se conoce con el nombre
3de declaración de evento reemplazado. Una declaración de evento reemplazado no declara un evento nuevo. En
4lugar de eso, simplemente especializa las implementaciones de los descriptores de acceso de un evento virtual
5existente.
6Una declaración de evento reemplazado debe especificar exactamente los mismos modificadores de
7accesibilidad, tipo y nombre que el evento reemplazado.
8Una declaración de evento reemplazado puede incluir el modificador sea led. El uso de este modificador impide
9que una clase derivada siga reemplazando el evento. Los descriptores de acceso de un evento sellado también
10son sellados.
11Supone un error en tiempo de compilación que una declaración de evento reemplazado incluya el modificador
12new .
13Salvo en las diferencias referentes a la sintaxis de declaración e invocación, los descriptores de acceso virtuales,
14sellados, de reemplazo y abstractos se comportan exactamente igual que los métodos virtuales, sellados, de
15reemplazo y abstractos. Específicamente, las reglas descritas en las secciones §10.5.3, §10.5.4, §10.5.5 y
16§10.5.6 se aplican como si los descriptores de acceso fueran métodos con una forma correspondiente. Cada
17descriptor de acceso corresponde a un método con un sólo parámetro de valor del tipo del evento, un tipo de
18valor devuelto vo id y los mismos modificadores que el evento contenedor.

1910.8 Indizadores
20Un indizador es un miembro que permite indizar un objeto de la misma manera que una matriz. Los indizadores
21se declaran mediante declaraciones de indizadores (indexer-declarations):
22 indexer-declaration:
23 attributesopt indexer-modifiersopt indexer-declarator { accessor-declarations }
24 indexer-modifiers:
25 indexer-modifier
26 indexer-modifiers indexer-modifier
27 indexer-modifier:
28 new
29 pub l i c
30 pro tec ted
31 i n te rna l
32 pr i va te
33 v i r tua l
34 sea led
35 ove r r i de
36 abs t rac t
37 exte rn
38 indexer-declarator:
39 type th i s [ formal-parameter-list ]
40 type interface-type . this [ formal-parameter-list ]
41Una declaración de indizador (indexer-declaration) puede incluir un conjunto de atributos (§17) y una
42combinación válida de los cuatro modificadores de acceso (§10.2.3), new (§10.2.2), v i r tua(§10.5.3),
l override
43(§10.5.4), sealed (§10.5.5), abstract (§10.5.6) y extern (§10.5.7).

591Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 283


592Especificación del lenguaje C#

1Las declaraciones de indizadores están sujetas a las mismas reglas que las declaraciones de métodos (§10.5) en
2lo que respecta a las combinaciones válidas de modificadores, con la única excepción de que el modificador
3static no está permitido en una declaración de indizador.
4Los modificadores v i r tua, love r r i dey abstract se excluyen mutuamente excepto en un caso. Los
5modificadores abstract y override pueden utilizarse conjuntamente de manera que un indizador abstracto
6pueda reemplazar a uno virtual.
7El tipo (type) de una declaración de indizador especifica el tipo de elemento del indizador que se incluye en la
8declaración. Si el indizador no es una implementación explícita de miembro de interfaz, el tipo (type) va seguido
9de la palabra clave this. Para una implementación explícita de miembro de interfaz, el tipo (type) viene seguido
10de un tipo de interfaz (interface-type), de un “.” y de la palabra clave this. A diferencia de otros miembros, los
11indizadores no tienen nombres definidos por el usuario.
12La lista de parámetros formales (formal-parameter-list) especifica los parámetros del indizador. La lista de
13parámetros formales de un indizador se corresponde con la de un método (§10.5.1), exceptuando que al menos
14se debe especificar un parámetro y que no se permiten los modificadores de parámetro ref y out.
15El tipo (type) de un indizador y cada uno de los tipos a los que se hace referencia en la lista de parámetros
16formales (formal-parameter-list) deben tener como mínimo el mismo nivel de accesibilidad que el propio
17indizador (§3.5.4).
18Las declaraciones de descriptores de acceso (accessor-declarations) (§10.6.2), que deben estar encerradas entre
19los símbolos (token) “{” y “}”, declaran los descriptores de acceso del indizador. Los descriptores de acceso
20especifican las instrucciones ejecutables asociadas a los elementos de lectura y escritura del indizador.
21Aunque la sintaxis para tener acceso a un elemento de indizador es la misma que para un elemento de matriz, un
22elemento de indizador no está clasificado como variable. Por tanto, no se puede pasar un elemento de indizador
23como argumento re fu out.
24La lista de parámetros formales de un indizador define la firma (§3.6) del indizador. En concreto, la firma de un
25indizador está formada por el número de parámetros formales y sus tipos. El tipo y el nombre de los elementos
26de los parámetros formales no forman parte de la firma del indizador.
27La firma de un indizador debe ser diferente de las firmas de los demás indizadores declarados en la misma clase.
28Los indizadores y las propiedades son similares conceptualmente, pero tienen las siguientes diferencias:
29• Una propiedad se identifica por su nombre, mientras que un indizador se identifica por su firma.
30• Para tener acceso a una propiedad se utiliza un nombre simple (simple-name) (§7.5.2) o un acceso de
31 miembro (member-access) (§7.5.4), pero a un elemento del indizador se tiene acceso a través de un acceso
32 de elemento (element-access) (§7.5.6.2).
33• Una propiedad puede ser un miembro s ta t i cy un indizador siempre es un miembro de instancia.
34• Un descriptor de acceso get de una propiedad corresponde a un método sin parámetros, pero un descriptor
35 de acceso get de un indizador corresponde a un método con la misma lista de parámetros formales que el
36 indizador.
37• Un descriptor de acceso se t de una propiedad corresponde a un método con un solo parámetro denominado
38 va lue, pero un descriptor de acceso set de un indizador corresponde a un método con la misma lista de
39 parámetros formales que el indizador y un parámetro adicional denominado value.
40• Supone un error en tiempo de compilación que un descriptor de acceso de un indizador declare una variable
41 local con el mismo nombre que un parámetro del indizador.

593284 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


594 Capítulo 18 Código no seguro

1• En una declaración de propiedad reemplazada, se tiene acceso a la propiedad heredada utilizando la sintaxis
2 base .P, donde P es el nombre de la propiedad. En una declaración de indizador reemplazado, se tiene
3 acceso al indizador heredado utilizando la sintaxis base[E], donde E es una lista de expresiones separadas
4 por comas.
5Al margen de estas diferencias, todas las reglas definidas en §10.6.2 y §10.6.3 se aplican tanto a los descriptores
6de acceso de indizadores como a los descriptores de acceso de propiedades.
7Cuando la declaración de un indizador incluye un modificador exte rn, se dice que es un indizador externo.
8Debido a que la declaración de un indizador externo no proporciona una implementación real, cada una de sus
9declaraciones de descriptores de acceso (accessor-declarations) consiste en un punto y coma.
10En el ejemplo siguiente se declara una clase BitArray que implementa un indizador para obtener acceso a los
11bits individuales de una matriz de bits.
12 us ing Sys tem;
13 c lass B i tA r ray
14 {
15 in t [ ] bi ts ;
16 i n t l eng th ;
17 pub l i c B i tA r ray ( in t l eng th ) {
18 i f ( l eng th < 0) th row new ArgumentExcept i on ( ) ;
19 b i t s = new i n t [ ( ( l eng th - 1) >> 5) + 1] ;
20 th i s . l eng th = l eng th ;
21
22 pub l i c i n t Length {
23 get { re tu rn l eng th ; }
24 }
25 pub l i c boo l th i s [ i n t i ndex ] {
26 get {
27 i f ( i ndex < 0 | | i ndex >= l eng th ) {
28 th row new I ndexOutOfRangeExcept i on ( ) ;
29 }
30 re tu rn (b i t s [ i ndex >> 5] & 1 << i ndex ) != 0 ;
31 }
32 se t {
33 i f ( i ndex < 0 | | i ndex >= l eng th ) {
34 th row new I ndexOutOfRangeExcept i on ( ) ;
35 }
36 i f ( va lue ) {
37 b i t s [ i ndex >> 5] |= 1 << i ndex ;
38 }
39 e l se {
40
45Una instancia de la clasebBitArray
i t s [ i ndex >> 5] &= ~(1 << i ndex ) ;
consume mucha menos memoria que el correspondiente bool[] (ya que cada
41valor de la instancia ocupa sólo un bit en lugar de un byte), pero permite las mismas operaciones que bool[].
46
42
47La siguiente clase CountPrimes utiliza una BitArray y el clásico algoritmo de “criba” para calcular el número
43
48de primos entre 1 y un número máximo dado:
44

595Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 285


596Especificación del lenguaje C#

1 class CountPrimes

2 {

3 static int Count(int max) {

4 BitArray flags = new BitArray(max + 1);

5 int count = 1;

6 for (int i = 2; i <= max; i++) {

7 if (!flags[i]) {

8 for (int j = i * 2; j <= max; j += i) flags[j] = true;


149 static void Main(string[] args) {

15
10 int max = int.Parse(args[0]);

16
11 int count = Count(max);

17
12 Console.WriteLine("Found {0} primes between 1 and {1}", count, max);
20 Observe que la sintaxis para tener acceso a los elementos de B i tA r rayes precisamente la misma que para
18boo l [ .]
21
13
19En el siguiente ejemplo se muestra una clase de una cuadrícula de 26 por 10 que tiene un indizador con dos
22
23parámetros. El primer parámetro debe ser una letra mayúscula o minúscula dentro del intervalo A–Z, y el
24segundo debe ser un número entero dentro del intervalo 0–9.

25 using System;
26 class Grid

27 {

28 const int NumRows = 26;


30 int[,] cells = new int[NumRows, NumCols];
29
31 public int this[char c, int col] {

32 get {

33 c = Char.ToUpper(c);

34 if (c < 'A' || c > 'Z') {

35 throw new ArgumentException();

36 }

37 if (col < 0 || col >= NumCols) {

38
39
40
41

597286 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


598 Capítulo 18 Código no seguro

1 set {

2 c = Char.ToUpper(c);

3 if (c < 'A' || c > 'Z') {

4 throw new ArgumentException();

5 }

6 if (col < 0 || col >= NumCols) {

7 throw new IndexOutOfRangeException();

8
1310.8.1 Sobrecarga de indizadores
149Las reglas de resolución de sobrecarga de indizadores se describen en §7.4.2.
10
1510.9 Operadores
11
16Un operador es un miembro que define el significado de un operador de expresión que puede aplicarse a
17instancias de la clase. Los operadores se declaran mediante declaraciones de operadores (operator-
12
18declarations):
19 operator-declaration:
20 attributesopt operator-modifiers operator-declarator operator-body
21 operator-modifiers:
22 operator-modifier
23 operator-modifiers operator-modifier
24 operator-modifier:
25 pub l i c
26 s ta t i c
27 exte rn
28 operator-declarator:
29 unary-operator-declarator
30 binary-operator-declarator
31 conversion-operator-declarator
32 unary-operator-declarator:
33 type opera to r overloadable-unary-operator ( type identifier )
34 overloadable-unary-operator: uno de
35 + - ! ~ ++ -- true false
36 binary-operator-declarator:
37 type operator overloadable-binary-operator ( type identifier , type identifier )
38 overloadable-binary-operator: uno de
39 + - * / % & | ^ << >> == != > < >= <=
40 conversion-operator-declarator:
41 implicit operator type ( type identifier )
42 explicit operator type ( type identifier )

599Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 287


600Especificación del lenguaje C#

1 operator-body:
2 block
3 ;
4Hay tres categorías de operadores sobrecargables: operadores unarios (§10.9.1), operadores binarios (§10.9.2) y
5operadores de conversión (§10.9.3).
6Cuando la declaración de un operador incluye un modificador ex te rn, se dice que el operador es un operador
7externo. Debido a que un operador externo no proporciona una implementación real, su cuerpo de operador
8(operator-body) consiste en un punto y coma. Para el resto de operadores, el cuerpo de operador (operator-
9body) consiste en un bloque (block) que especifica las instrucciones que deben ejecutarse cuando se invoca al
10operador. El bloque (block) de un operador debe cumplir las reglas de los métodos que devuelven valores,
11descritas en §10.5.8.
12Las siguientes reglas se aplican a todas las declaraciones de operadores:
13• Una declaración de operador debe incluir los modificadores pub l i cy s ta t i.c
14• Los parámetros de un operador deben ser parámetros de valor. Supone un error en tiempo de compilación
15 que una declaración de operador especifique los parámetros re fu out.
16• La firma de un operador (§10.9.1, §10.9.2, §10.9.3) debe ser diferente de las firmas de todos los demás
17 operadores declarados en la misma clase.
18• Todos los tipos a los que se hace referencia en una declaración de operador deben ser por lo menos tan
19 accesibles como el propio operador (§3.5.4).
20• Cuando el mismo modificador aparece varias veces en una declaración de operador, se produce un error.
21Cada categoría de operador impone restricciones adicionales, que se describen en las secciones siguientes.
22Al igual que otros miembros, los operadores declarados en una clase base se heredan por sus clases derivadas.
23Debido a que las declaraciones de operadores siempre necesitan que la clase o la estructura donde se declara el
24operador participe en la firma del operador, no es posible que un operador declarado en una clase derivada
25oculte un operador declarado en una clase base. Por lo tanto, nunca es necesario el modificador new , y por ello
26no se permite en una declaración de operador.
27Puede encontrar información adicional sobre operadores unarios y binarios en la §7.2.
28Puede encontrar información adicional sobre operadores de conversión en §6.4.

2910.9.1 Operadores unarios


30Las siguientes reglas se aplican a las declaraciones de operadores unarios, donde T denota el tipo de la clase o
31estructura que contiene la declaración del operador:
32• Un operador unario +, -, ! o ~ debe obtener un solo parámetro de tipo T y puede devolver cualquier tipo.
33• Un operador unario ++ o - -debe obtener un solo parámetro de tipo T y debe devolver un tipo T o un tipo
34 derivado de T.
35• Un operador unario true o false debe obtener un solo parámetro de tipo T y debe devolver un tipo bool.
36La firma de un operador unario consiste en el símbolo de operador (+, -, !, ~, ++, --, true o false) y el tipo del
37parámetro formal. El tipo de valor devuelto no forma parte de la firma de un operador unario, ni es el nombre
38del parámetro formal.

601288 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


602 Capítulo 18 Código no seguro

1Los operadores unarios t ruey f a l serequieren declaraciones par a par. Se produce un error en tiempo de
2compilación si una clase declara uno de estos operadores sin declarar el otro. Los operadores true y false se
3describen más detalladamente en §7.11.2 y §7.16.
4En el siguiente ejemplo se muestra una implementación y la utilización subsiguiente del operador ++ para una
5clase de vector de tipo entero:

6 public class IntVector

7 {

89 public int Length {...} // read-only property


10 public int this[int index] {...} // read-write indexer
11 public static IntVector operator ++(IntVector iv) {

12 IntVector temp = new IntVector(iv.Length);

13 for (int i = 0; i < iv.Length; i++)

14 temp[i] = iv[i] + 1;

15 return temp;
18 class Test
16
19 {
17
20 static void Main() {

21
23 iv2 = iv1++; // iv2 contains 4 x 0, iv1 contains 4 x 1
22
24 iv2 = ++iv1; // iv2 contains 4 x 2, iv1 contains 4 x 2

25 }
27Observe cómo el método del operador devuelve el valor producido al sumar 1 al operando, al igual que los
28
26operadores de incremento y decremento postfijo (§7.5.9), y los operadores de incremento y decremento prefijo
29(§7.6.5). A diferencia de C++, este método no necesita modificar el valor de su operando directamente. De
30hecho, la modificación del valor del operando infringiría la semántica estándar del operador postfijo de
31incremento.

3210.9.2 Operadores binarios


33Un operador binario debe obtener dos parámetros, de los cuales al menos uno debe tener el tipo de la clase o
34estructura donde se declara el operador. Los operadores de desplazamiento (§7.8) están más restringidos: el tipo
35del primer parámetro debe ser la clase o tipo de estructura en el que se declara el operador y el segundo
36parámetro siempre debe tener el tipo int. Un operador binario puede devolver cualquier tipo.
37La firma de un operador binario consiste en el símbolo (token) (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <,
38>= o <=) y los tipos de los dos parámetros formales. El tipo de valor devuelto y los nombres de los parámetros
39formales no forman parte de la firma de un operador binario.
40Algunos operadores binarios requieren declaración de par a par. Para la declaración de cada operador del par,
41debe existir una declaración coincidente del otro operador del par. Las declaraciones de dos operadores
42coinciden cuando tienen el mismo tipo de valor devuelto y el mismo tipo para cada parámetro. Los siguientes
43operadores requieren declaración par a par:
44• opera to r== y operator !=

45• operator > y operator <

603Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 289


604Especificación del lenguaje C#

1• opera to r>= y operator <=

210.9.3 Operadores de conversión


3Una declaración de operador de conversión introduce una conversión definida por el usuario (§6.4) que
4aumenta las conversiones implícitas y explícitas predefinidas.
5Una declaración de operador de conversión que incluye la palabra clave imp l i c idefine
t una conversión
6implícita definida por el usuario. Las conversiones implícitas pueden ocurrir en distintas situaciones, incluyendo
7las invocaciones de miembros de función, las expresiones de conversión y las asignaciones. Esta categoría se
8explica con más detalle en la sección §6.1.
9Una declaración de operador de conversión que incluye la palabra clave exp l i c idefine
t una conversión explícita
10definida por el usuario. Las conversiones explícitas pueden ocurrir en las expresiones de conversión, y se
11describen detalladamente en §6.2.
12Un operador de conversión convierte de un tipo origen, indicado por el tipo del parámetro del operador de
13conversión, a un tipo destino, indicado por el tipo de valor devuelto del operador de conversión. Una clase o
14estructura puede declarar una conversión de un tipo origen S a un tipo destino T si se cumple todo lo siguiente:
15• S y T son tipos diferentes

16• S o T es el tipo de clase o estructura en el que tiene lugar la declaración del operador.

17• Ni S ni T son de tipo object, ni un tipo de interfaz (interface-type).


18• T no es una clase base de S, y S tampoco lo es de T.
19De la segunda regla se deriva que un operador de conversión debe convertir a o del tipo de la clase o estructura
20en la que se declara el operador. Por ejemplo, es posible que un tipo de clase o de estructura C defina una
21conversión de C a int y de int a C, pero no de int a bool.
22No es posible volver a definir una conversión predefinida. Por lo tanto, no está permitido utilizar operadores de
23conversión para convertir de o a object porque ya existen conversiones implícitas y explícitas entre object y el
24resto de tipos. Además, ni el tipo de origen ni el de destino de una conversión puede ser un tipo base del otro,
25porque entonces ya existiría una conversión.
26Las conversiones definidas por el usuario no pueden convertir de o a tipos de interfaz (interface-types). Esta
27restricción impide, en particular, que se produzcan transformaciones definidas por el usuario cuando se
28convierte a un tipo de interfaz (interface-type), y asegura que una conversión a un tipo de interfaz se ejecute
29correctamente sólo si el objeto que se está convirtiendo implementa realmente el tipo de interfaz.
30La firma de un operador de conversión está formado por el tipo de origen y el tipo de destino (ésta es la única
31forma de miembro en la que el tipo de valor devuelto participa en la firma). La clasificación implicit o explicit
32de un operador de conversión no forma parte de la firma del operador. Por lo tanto, un clase o una estructura no
33puede declarar a la vez operadores de conversión implicit y explicit con los mismos tipos de origen y destino.
34En general, las conversiones implícitas definidas por el usuario deben diseñarse para que nunca produzcan
35excepciones ni pierdan información. Si una conversión definida por el usuario puede dar lugar a excepciones
36(por ejemplo, debido a que el argumento de origen está fuera del intervalo) o a pérdida de información (como
37descartar los bits de mayor orden), dicha conversión debería definirse como explícita.
38En el siguiente ejemplo:
39 us ing Sys tem;
40 pub l i c s t ruc t Dig i t
41 {
42 byte va lue ;

605290 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


606 Capítulo 18 Código no seguro

1 public Digit(byte value) {

2 if (value < 0 || value > 9) throw new ArgumentException();

3 this.value = value;
5 public static implicit operator byte(Digit d) {
4
6 return d.value;

78 public static explicit operator Digit(byte b) {

9 return new Digit(b);

10 }
12la conversión de Dig i ta byte es implícita porque nunca produce excepciones ni pierde información, pero la
13
11conversión de byte a Digit es explícita ya que Digit sólo puede representar un subconjunto de los posibles
14valores de un byte.

1510.10Constructores de instancia
16Un constructor de instancia es un miembro que implementa las acciones que se requieren para inicializar una
17instancia de una clase. Los constructores de instancia se declaran mediante declaraciones de constructor
18(constructor declarations):
19 constructor-declaration:
20 attributesopt constructor-modifiersopt constructor-declarator constructor-body
21 constructor-modifiers:
22 constructor-modifier
23 constructor-modifiers constructor-modifier
24 constructor-modifier:
25 public
26 protected
27 internal
28 private
29 extern
30 constructor-declarator:
31 identifier ( formal-parameter-listopt ) constructor-initializeropt
32 constructor-initializer:
33 : base ( argument-listopt )
34 : this ( argument-listopt )
35 constructor-body:
36 block
37 ;
38Una declaración de constructor (constructor-declaration) puede incluir un conjunto de atributos (§17), una
39combinación válida de los cuatro modificadores de acceso (§10.2.3) y un modificador extern (§10.5.7). No se
40permite que una declaración de constructor incluya un mismo modificador varias veces.
41El identificador (identifier) de un declarador de constructor (constructor-declarator) debe nombrar la clase en la
42que se declara el constructor de instancia. Si se especifica cualquier otro nombre, se produce un error en tiempo
43de compilación.
44La lista de parámetros formales (formal-parameter-list) opcional de un constructor de instancia está sujeta a las
45mismas reglas que la lista de parámetros formales (formal-parameter-list) de un método (§10.5). La lista de

607Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 291


608Especificación del lenguaje C#

1parámetros formales define la firma (§3.6) de un constructor de instancia y dirige el proceso por el que la
2resolución de sobrecarga (§7.4.2) selecciona un constructor de instancia en particular en una invocación.
3Cada uno de los tipos a los que se hace referencia en la lista de parámetros formales (formal-parameter-list) de
4un constructor de instancia deben tener como mínimo el mismo nivel de accesibilidad que el propio constructor
5(§3.5.4).
6El inicializador de constructor (constructor-initializer) opcional especifica otro constructor de instancia al que
7hay que invocar antes de ejecutar las instrucciones que aparecen en el cuerpo del constructor (constructor-body)
8del primer constructor de instancia. Esta categoría se explica con más detalle en la sección §10.10.1.
9Cuando la declaración de un constructor incluye un modificador ex te rn, se dice que el constructor es un
10constructor externo. Debido a que la declaración de constructor externo no proporciona una implementación
11real, su cuerpo del constructor (constructor-body) consiste en un punto y coma. Para el resto de constructores, el
12cuerpo del constructor (constructor-body) consiste en un bloque (block) que especifica las instrucciones para
13inicializar una nueva instancia de la clase. Esto corresponde exactamente al bloque (block) de un método de
14instancia con un tipo de valor devuelto vo id (§10.5.8).
15Los constructores de instancia no se heredan. Por lo tanto, una clase sólo tiene los constructores de instancia que
16se declaran realmente en la clase. Si una clase no contiene ninguna declaración de constructor de instancia, se
17proporciona automáticamente un constructor de instancia predeterminado (§10.10.4).
18Los constructores de instancia se invocan mediante expresiones de creación de objetos (object-creation-
19expressions) (§7.5.10.1) y a través de inicializadores de constructor (constructor-initializers).

2010.10.1Inicializadores de constructor
21Todos los constructores de instancia (excepto aquellos para la clase ob jec t) incluyen implícitamente una
22invocación de otro constructor de instancia inmediatamente antes del cuerpo del constructor (constructor-body).
23El constructor que se invoca implícitamente está determinado por el inicializador de constructor (constructor-
24initializer):
25• Un inicializador de constructor de instancia de la forma base (lista de argumentos opcional o argument-
26 listopt) causa la invocación de un constructor de instancia desde la clase base directa. Este constructor se
27 selecciona utilizando la lista de argumentos (argument-list) y las reglas de resolución de sobrecargas de
28 §7.4.2. El conjunto de constructores de instancia candidatos está compuesto de todos los constructores de
29 instancia accesibles contenidos en la clase base directa, o del constructor predeterminado (§10.10.4), si en la
30 clase base directa no se declara ningún constructores de instancia. Si el conjunto está vacío o no es posible
31 identificar el mejor constructor de instancia, se produce un error en tiempo de compilación.
32• Un inicializador de constructor de instancia de la forma th i sargument-list
( opt) causa la invocación de un
33 constructor de instancia desde la propia clase. El constructor se selecciona utilizando la lista de argumentos
34 (argument-list) y las reglas de resolución de sobrecargas de §7.4.2. El conjunto de constructores de instancia
35 candidatos está formado por todos los constructores de instancia accesibles declarados en la clase. Si el
36 conjunto está vacío o no es posible identificar el mejor constructor de instancia, se produce un error en
37 tiempo de compilación. Si la declaración de un constructor de instancia incluye un inicializador de
38 constructor que invoca al propio constructor, se produce un error en tiempo de compilación.
39Si un constructor de instancia no tiene inicializador de constructor, se proporciona implícitamente uno de la
40forma base ( ). Por lo tanto, una declaración de constructor de instancia de la forma
41 C( . . . ) { . . . }
42equivale exactamente a
43 C( . . . ) : base ( ) { . . . }

609292 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


610 Capítulo 18 Código no seguro

1El ámbito de los parámetros dados en la lista de parámetros formales (formal-parameter-list) de una declaración
2de constructor de instancia incluye el inicializador de constructor de dicha declaración. Por lo tanto, un
3inicializador de constructor puede tener acceso a los parámetros del constructor. Por ejemplo:

4 class A

5 {

6 public A(int x, int y) {}


8 class B: A
7
9 {

10 public B(int x, int y): base(x + y, x - y) {}


12Un inicializador de constructor de instancia no puede tener acceso a la instancia que está siendo creada. Por ello,
13
11se produce un error en tiempo de compilación si se hace referencia a th i sen una expresión de argumento del
14inicializador del constructor, al igual que se produce un error en tiempo de compilación si una expresión de
15argumento hace referencia a cualquier miembro de instancia a través del nombre simple (simple-name).

1610.10.2Inicializadores de variables de instancia


17Cuando un constructor de instancia no tiene inicializador de constructor, o cuando tiene uno de la forma
18base ( . . ,. el
) constructor realiza implícitamente las inicializaciones especificadas por los inicializadores de
19variables (variable-initializers) de los campos de instancia declarados en su clase. Esto corresponde a una
20secuencia de asignaciones que se ejecutan inmediatamente al entrar en el constructor y antes de invocar
21implícitamente al constructor de la clase base directa. Los inicializadores de variable se ejecutan en el orden
22textual en que aparecen en la declaración de clase.

2310.10.3Ejecución de constructores
24Los inicializadores de variables se transforman en instrucciones de asignación, que se ejecutan antes de la
25invocación del constructor de instancia de la clase base. Tal ordenamiento garantiza que todos los campos de
26instancia son inicializados por sus inicializadores de variable antes de que se ejecute cualquier instrucción que
27tenga acceso a la instancia.
28Dado el ejemplo:
29 us ing Sys tem;
30 c lass A
31 {
32 pub l i c A( ) {
33 Pr in t F i e lds ( ) ;
34 }
35 pub l i c v i r tua l vo id Pr in t F i e lds ( ) {}
36 }
37 c lass B: A
38 {
39 i n t x = 1;
40 int y;
41 pub l i c B( ) {
42 y = - 1;
43 }

611Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 293


612Especificación del lenguaje C#

1 public override void PrintFields() {

2 Console.WriteLine("x = {0}, y = {1}", x, y);

3 }
5cuando se utiliza new B( ) para crear una instancia de B, el resultado que se produce es:
4
6 x = 1, y = 0
7El valor de x es 1 porque el inicializador de variable se ejecuta antes de que se invoque el constructor de
8instancia de la clase base. Sin embargo, el valor de y es 0 (el valor predeterminado para un tipo int) porque la
9asignación a y no se ejecuta hasta que el constructor de la clase base devuelve el control.
10Es útil considerar los inicializadores de variables de instancia y los inicializadores de constructor como
11instrucciones que se insertan automáticamente antes del cuerpo del constructor (constructor-body). En el
12ejemplo

13 using System;

14 using System.Collections;
15 class A

16 {

17
18 public A() {

19 count = 0;

20
21 public A(int n) {

22 count = n;

23 }
25 class B: A
24
26 {

27 double sqrt2 = Math.Sqrt(2.0);

28
30 public B(): this(100) {
29
31 items.Add("default");

32
33 public B(int n): base(n – 1) {

34 max = n;

35 }
37contiene varios inicializadores de variable e inicializadores de constructor de ambas formas (base y this). El
38
36código del ejemplo se corresponde con el código siguiente, donde cada comentario indica una instrucción que se
39inserta automáticamente (la sintaxis utilizada para invocar el constructor insertado automáticamente no es
40correcta, pero sirve para ilustrar el mecanismo).

41 using System.Collections;
42 class A

43 {

44

613294 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


614 Capítulo 18 Código no seguro

1 public A() {

2 x = 1; // Variable initializer

3 y = -1; // Variable initializer

4 object(); // Invoke object() constructor


75 public A(int n) {

86 x = 1; // Variable initializer

9 y = -1; // Variable initializer

10 object(); // Invoke object() constructor

11 count = n;
14 class B: A
12
15 {
13
16 double sqrt2;

17
19 public B(): this(100) {
18
20 B(100); // Invoke B(int) constructor

21 items.Add("default");
23 public B(int n): base(n – 1) {
22
24 sqrt2 = Math.Sqrt(2.0); // Variable initializer

25 items = new ArrayList(100); // Variable initializer

26 A(n – 1); // Invoke A(int) constructor

27 max = n;
3010.10.4Constructores predeterminados
28
31Si una clase no contiene ninguna declaración de constructores de instancia, se proporciona automáticamente un
32
29constructor de instancia predeterminado. El constructor predeterminado simplemente invoca el constructor sin
33parámetros de la clase base directa. Si la clase base directa no tiene un constructor de instancia sin parámetros
34accesible, se producirá un error en tiempo de compilación. Si la clase es abstracta, la accesibilidad declarada
35para el constructor predeterminado es protected. Si no, la accesibilidad declarada para el constructor
36predeterminado es public. Por lo tanto, el constructor predeterminado es siempre de la forma

37 protected C(): base() {}


38o bien

39 public C(): base() {}


40donde C es el nombre de la clase.
41En el siguiente ejemplo:

42 class Message

43 {

44 object sender;

45
46
615Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 295
616Especificación del lenguaje C#

1se proporciona un constructor predeterminado debido a que la clase no contiene ninguna declaración de
2constructor de instancia. Así, el ejemplo es exactamente equivalente a

3 class Message

4 {

5 object sender;
7 public Message(): base() {}
6
8 }
910.10.5Constructores Private
10Cuando una clase T sólo declara constructores de instancia privados, las clases externas al texto del programa de
11T no pueden ni derivarse ni crear directamente instancias de T. Por lo tanto, si una clase contiene sólo miembros
12estáticos y no se desea crear una instancia con él, puede evitarse tal instancia agregando un constructor de
13instancia privado que esté vacío. Por ejemplo:

14 public class Trig

15 {

16
17 public const double PI = 3.14159265358979323846;
18 public static double Sin(double x) {...}

19 public static double Cos(double x) {...}

20 public static double Tan(double x) {...}


22La clase Trig agrupa métodos y constantes relacionados, pero la intención no es crear una instancia de la misma.
23
21Por ello declara un único constructor de instancia privado vacío. Se debe declarar al menos un constructor de
24instancia para suprimir la generación automática de un constructor predeterminado.

2510.10.6Parámetros de constructor de instancia opcionales


26La forma this(...) de un inicializador de constructor se suele utilizar conjuntamente con sobrecarga para
27implementar parámetros de constructor de instancia opcionales. En el siguiente ejemplo:

28 class Text

29 {

30
31 public Text(int x, int y): this(x, y, null) {}
32 public Text(int x, int y, string s) {

33 // Actual constructor implementation

34 }
36los dos primeros constructores de instancia sólo proporcionan los valores predeterminados para los argumentos
37que faltan. Ambos usan un inicializador de constructor this(...) para invocar el tercer constructor de instancia,
35
38que es el que realmente inicializa la nueva instancia. El efecto son parámetros de constructor opcionales:

39 Text t1 = new Text(); // Same as Text(0, 0, null)

40 Text t2 = new Text(5, 10); // Same as Text(5, 10, null)

41

617296 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


618 Capítulo 18 Código no seguro

110.11Constructores static
2Un constructor estático es un miembro que implementa las acciones que se requieren para inicializar una clase.
3Los constructores estáticos se declaran mediante declaraciones de constructores estáticos (static-constructor-
4declarations):
5 static-constructor-declaration:
6 attributesopt static-constructor-modifiers identifier ( ) static-constructor-body
7 static-constructor-modifiers:
8 exte rnopt s ta t i c
9 static externopt
10 static-constructor-body:
11 block
12 ;
13Una declaración de constructor estático (static-constructor-declaration) puede incluir un conjunto de atributos
14(attributes) (§17) y un modificador ex te rn(§10.5.7).
15El identificador (identifier) de una declaración de constructor estático (static-constructor-declaration) debe
16nombrar la clase en la que se declara el constructor estático. Si se especifica cualquier otro nombre, se produce
17un error en tiempo de compilación.
18Cuando la declaración de un constructor incluye un modificador ex te rn, se dice que el constructor es un
19constructor estático externo. Debido a que la declaración de constructor estático no proporciona una
20implementación real, su cuerpo de constructor estático (static-constructor-body) consiste en un punto y coma.
21Para todas las demás declaraciones de constructor estático, el cuerpo del constructor estático (static-constructor-
22body) se compone de un bloque (block) que especifica las instrucciones que deben ejecutarse para poder
23inicializar la clase. Esto corresponde exactamente al cuerpo del método (method-body) estático con un tipo de
24valor devuelto vo id (§10.5.8).
25Los constructores estáticos no se heredan y no pueden ser llamados directamente.
26El constructor estático de una clase se ejecuta como mucho una vez en un dominio de aplicación dado. La
27ejecución de un constructor estático la desencadena el primero de los siguientes eventos que se produzcan en un
28dominio de aplicación.
29• Se crea una instancia de la clase.
30• Se hace referencia a cualquiera de los miembros estáticos de la clase.
31Si una clase contiene el método Main (§3.1) en el que comienza la ejecución, el constructor estático de esta
32clase se ejecuta antes de que se llame al método Main. Si una clase contiene cualquier campo estático con
33inicializadores, éstos se ejecutan en orden textual inmediatamente antes de ejecutar el constructor estático.
34En el ejemplo
35 us ing Sys tem;
36 c lass Tes t
37 {
38 s ta t i c vo id Main ( ) {
39 A.F ( ) ;
40 B .F ( ) ;
41 }
42

619Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 297


620Especificación del lenguaje C#

1 class A

2 {

3 static A() {

4 Console.WriteLine("Init A");

5 }

6 public static void F() {


10 class B
7
11 {
8
12 static B() {
9
13 Console.WriteLine("Init B");

14 }

15 public
19el resultado debe ser: static void F() {
16
20 Init A
17
21 A.F
18
22 Init B
24porque la ejecución de los constructores estáticos de A se desencadena al llamar a A.F, y la ejecución de los
25
23constructores estáticos de B se desencadena cuando se llama a B.F.
26Es posible construir dependencias circulares que permitan a los campos estáticos con inicializadores de variables
27ser observados en su estado de valor predeterminado.
28En el ejemplo

29 using System;
30 class A

31 {

32
33 static A() {

34 X = B.Y + 1;

35 }
37 class B
36
38 {

39
40 static B() {}
41 static void Main() {

42 Console.WriteLine("X = {0}, Y = {1}", A.X, B.Y);

43 }
45produce el resultado
44
46 X = 1, Y = 2

621298 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


622 Capítulo 18 Código no seguro

1Para ejecutar el método Main, el sistema ejecuta en primer lugar el inicializador de B.Y, antes que el constructor
2estático de la clase B. El inicializador de Y hace que se ejecute el constructor estático de A, porque se hace
3referencia al valor de A.X. Por su parte, el constructor estático de A continúa para calcular el valor de X y, para
4hacerlo, obtiene el valor predeterminado de Y, que es cero. Así, A.X se inicializa a 1. A continuación finaliza el
5proceso de ejecutar los inicializadores de campos estáticos de A y el constructor estático, volviendo al cálculo
6del valor inicial de Y, cuyo resultado es 2.

710.12Destructores
8Un destructor es un miembro que implementa las acciones necesarias para destruir una instancia de una clase.
9Un destructor se declara utilizando una declaración de destructor (destructor-declaration):
10 destructor-declaration:
11 attributesopt externopt ~ identifier ( ) destructor-body
12 destructor-body:
13 block
14 ;
15Una declaración de destructor (destructor-declaration) puede incluir un conjunto de atributos (attributes) (§17).
16El identificador (identifier) de un declaración de destructor (destructor-declaration) debe nombrar la clase en la
17que se declara el destructor. Si se especifica cualquier otro nombre, se produce un error en tiempo de
18compilación.
19Cuando una declaración de destructor incluye un modificador ex te rn, se dice que es un destructor externo.
20Debido a que la declaración de destructor externo no proporciona una implementación real, su cuerpo de
21destructor (destructor-body) consiste en un punto y coma. Para el resto de destructores, el cuerpo del destructor
22(destructor-body) consiste en un bloque (block) que especifica las instrucciones necesarias para destruir una
23instancia de la clase. Un cuerpo de destructor (destructor-body) corresponde exactamente al cuerpo del método
24(method-body) de un método de instancia con un tipo de valor devuelto vo id (§10.5.8).
25Los destructores no se heredan. Por lo tanto, una clase sólo tiene el destructor que se puede declarar en la propia
26clase.
27Como un destructor no puede tener parámetros, no puede ser sobrecargado; por lo tanto, una clase sólo puede
28tener como máximo un destructor.
29Los destructores se invocan automáticamente y no se pueden invocar explícitamente. Una instancia se convierte
30en candidata para destrucción cuando ya ninguna parte de código puede utilizarla. La ejecución del destructor de
31la instancia puede ocurrir en cualquier momento una vez que la instancia se convierta en candidata para
32destrucción. Cuando se destruye una instancia, se llama a los destructores de su cadena de herencia en orden, de
33la más derivada a la menos derivada. Un destructor puede ejecutarse en cualquier subproceso. Para leer una
34explicación más detallada de las reglas que controlan cómo y cuándo se ejecuta un destructor, vea la sección
35§3.9.
36El resultado del ejemplo
37 us ing Sys tem;
38 c lass A
39 {
40 ~A( ) {
41 Conso le .Wr i teL ine ( "A ' s des t ruc to r " ) ;
42 }
43

623Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 299


624Especificación del lenguaje C#

1 class B: A

2 {

3 ~B() {

4 Console.WriteLine("B's destructor");
75 class Test

86 {

9 static void Main() {

10 B b = new B();

11 b = null;

12
16es GC.Collect();

13
17 B’s destructor
14
18ya que en
19 A’s
unadestructor
cadena de herencia los destructores se llaman en orden, de la más derivada a la menos derivada.
15
20Los destructores se implementan reemplazando el método virtual F ina l i zeen Sys tem.Ob jec t. Los programas
21de C# no permiten reemplazar este método o llamarlo directamente (o a reemplazos del mismo). Por ejemplo, el
22programa

23 class A

24 {

25
26 public void F() {

27 this.Finalize(); // error

28 }
30contiene dos errores.
29

625300 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


626 Capítulo 18 Código no seguro

1El compilador se comporta como si este método, y sus reemplazos, no existieran. Por lo tanto, este programa:

2 class A

3 {

4 void Finalize() {} // permitted


6es válido, y el método mostrado oculta el método F ina l i ze
de Sys tem.Ob jec t.
57Para leer una explicación del comportamiento producido cuando se inicia una excepción desde un destructor,
8vea la sección §16.3.

627Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 301


628Especificación del lenguaje C#

1 11.Estructuras

2Las estructuras son similares a las clases en que pueden representar estructuras de datos que pueden contener
3miembros de datos y miembros de función. No obstante, a diferencia de las clases, las estructuras son tipos de
4valores y no requieren asignación del montón. Una variable de un tipo de estructura contiene directamente los
5datos de la estructura, mientras que una variable de un tipo de clase contiene una referencia a los datos, que se
6conocerá posteriormente como objeto.
7Las estructuras son particularmente útiles para estructuras de datos pequeñas que tienen semánticas de valor. Los
8números complejos, los puntos de un sistema de coordenadas o los pares de valores clave de un diccionario son
9buenos ejemplos de estructuras. La clave de estas estructuras de datos es que tienen pocos miembros de datos,
10que no necesitan utilizar herencia o identidad referencial y que pueden ser implementadas convenientemente
11utilizando semánticas de valor donde la asignación copia el valor en lugar de la referencia.
12Como se describe en §4.1.4, los tipos simples que proporciona C#, como i n t, doub le y bool, son en realidad
13tipos de estructuras. Como estos tipos predefinidos son estructuras, es posible también utilizar estructuras y
14sobrecarga de operadores para implementar nuevos tipos “primitivos” en el lenguaje C#. Al final de este
15capítulo (§11.4) se proporcionan dos ejemplos de estos tipos.

1611.1 Declaraciones de estructuras


17Una declaración de estructura (struct-declaration) es una declaración de tipo (type-declaration) (§9.5) que
18declara una nueva estructura.
19 struct-declaration:
20 attributesopt struct-modifiersopt struct identifier struct-interfacesopt struct-body ;opt
21Una declaración de estructura (struct-declaration) consiste en un conjunto de atributos (attributes) (§17)
22opcionales, seguidos de un conjunto de modificadores de estructura (struct-modifiers) (§11.1.1), de la palabra
23clave struct y un identificador (identifier) que da nombre a la estructura, de una especificación de interfaces de
24estructura (struct-interfaces) (§11.1.2) opcional, de un cuerpo de estructura (struct-body) (§11.1.3), y de un
25punto y coma opcional.

2611.1.1 Modificadores de estructuras


27Una declaración de estructura (struct-declaration) puede incluir opcionalmente una secuencia de modificadores
28de estructura:
29 struct-modifiers:
30 struct-modifier
31 struct-modifiers struct-modifier
32 struct-modifier:
33 new
34 public
35 protected
36 internal
37 private
38Cuando el mismo modificador aparece varias veces en una declaración de estructura, se produce un error en
39tiempo de compilación.

629302 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


630 Capítulo 18 Código no seguro

1Los modificadores de una declaración de estructura tienen el mismo significado que los de las declaraciones de
2clases (§10.1.1).

311.1.2 Interfaces Struct


4Una declaración de estructura puede incluir una especificación de interfaces de estructura (struct-interfaces), en
5cuyo caso se dice que la estructura implementa los tipos de interfaz dados.
6 struct-interfaces:
7 : interface-type-list
8Las implementaciones de interfaces se explican más detalladamente en §13.4.

911.1.3 Cuerpo de estructura


10El cuerpo de estructura (struct-body) de una estructura define los miembros de la estructura.
11 struct-body:
12 { struct-member-declarationsopt }

1311.2 Miembros de estructura


14Los miembros de una estructura se componen de los miembros que introducen las declaraciones de miembros de
15estructura (struct-member-declarations) y los miembros heredados del tipo Sys tem.Va lueType.
16 struct-member-declarations:
17 struct-member-declaration
18 struct-member-declarations struct-member-declaration
19 struct-member-declaration:
20 constant-declaration
21 field-declaration
22 method-declaration
23 property-declaration
24 event-declaration
25 indexer-declaration
26 operator-declaration
27 constructor-declaration
28 static-constructor-declaration
29 type-declaration
30Excepto por las diferencias indicadas en §11.3, las descripciones de miembros de clase proporcionadas en las
31secciones §10.2 a §10.11 también se aplican a los miembros de estructuras.

3211.3 Diferencias entre clase y estructura


33Las estructuras se diferencian de las clases de diferentes maneras:
34• Las estructuras son tipos de valor (§11.3.1).
35• Todos los tipos de estructura se heredan implícitamente de la clase Sys tem.Va lueType(§11.3.2).
36• La asignación a una variable de un tipo de estructura crea una copia (copy) del valor que se asigne (§11.3.3).
37• El valor predeterminado de una estructura es el valor producido al establecer todos los campos de tipos de
38 valor en su valor predeterminado, y todos los campos de tipos de referencia en nu l l(§11.3.4).

631Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 303


632Especificación del lenguaje C#

1• Las operaciones boxing y unboxing se utilizan para realizar la conversión entre un tipo struct y un tipo
2 ob jec t(§11.3.5).
3• El significado de th i ses diferente para las estructuras (§11.3.6).
4• Las declaraciones de campos de instancia para una estructura no pueden incluir inicializadores de variables
5 (§11.3.7).
6• Una estructura no puede declarar un constructor de instancia sin parámetros (§11.3.8).
7• No está permitido que una estructura declare un destructor (§11.3.9).

811.3.1 Semánticas de valor


9Las estructuras son tipos de valor (§4.1) y se dice que tienen semánticas de valor. Las clases, por el contrario,
10son tipos de referencia (§4.2) y se dice que tienen semánticas de referencia.
11Una variable de un tipo de estructura contiene directamente los datos de la estructura, mientras que una variable
12de un tipo de clase contiene una referencia a los datos, que se conocerá posteriormente como objeto. Cuando una
13estructura B contiene un campo de instancia A y A es un tipo de estructura, supone un error en tiempo de
14compilación que A dependa de B. Una estructura X tiene una dependencia directa con una estructura Y si X
15contiene un campo de instancia del tipo Y. Dada esta definición, el conjunto completo de estructuras de las
16cuales depende una estructura es el cierre transitivo de la relación de dependencia directa. Por ejemplo:
17 s t ruc t Node
18 {
19 i n t data ;
20 Node nex t ; / / e r ro r , Node d i rec t l y depends on i t se l f
21 }
22es un error porque Node contiene un campo de instancia de su propio tipo. Otro ejemplo
23 s t ruc t A { B b ; }
24 s t ruc t B { C c ; }
25 s t ruc t C { A a ; }
26es un error porque cada uno de los tipos A, B y C dependen entre sí.
27En el caso de las clases, es posible que dos variables hagan referencia al mismo objeto y, por tanto, que las
28operaciones en una variable afecten al objeto al que hace referencia la otra variable. En el caso de las
29estructuras, cada variable tiene su propia copia de los datos (exceptuando las variables de los parámetros ref y
30out), de manera que no posible que las operaciones de una afecten a la otra. Dado que las estructuras no son
31tipos de referencia, no es posible que los valores de un tipo de estructura sean null.
32Dada la declaración
33 s t ruc t Po in t
34 {
35 pub l i c i n t x , y ;
36 pub l i c Po in t ( i n t x , i n t y ) {
37 th i s . x = x ;
38 th i s . y = y ;
39 }
40
41el fragmento de código

633304 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


634 Capítulo 18 Código no seguro

1 Point a = new Point(10, 10);

2 Point b = a;

3 a.x = 100;
5devuelve como resultado el valor 10. La asignación de a a b crea una copia del valor y por tanto b no se ve
64afectada por la asignación a a.x. En cambio, si se hubiera declarado Point como clase, el resultado sería 100
7puesto que a y b harían referencia al mismo objeto.

811.3.2 Herencia
9Todos los tipos de estructura heredan implícitamente de la clase System.ValueType, la cual, a su vez, hereda
10de la clase object. Una declaración de estructura puede especificar una lista de interfaces implementadas, pero
11no es posible que especifique una clase base.
12Los tipos de estructura nunca son abstractos y son siempre sellados implícitamente. Por lo tanto, los
13modificadores abstract y sealed no están permitidos en una declaración de estructura.
14Debido a que las estructuras no permiten la herencia, la accesibilidad declarada de un miembro de estructura no
15puede ser protected o protected internal.
16Los miembros de función de una estructura no pueden ser abstract ni virtual, y sólo se permite el modificador
17override para reemplazar métodos heredados de System.ValueType.

1811.3.3 Asignación
19La asignación a una variable de un tipo de estructura crea una copia (copy) del valor que se asigne. Esto difiere
20de la asignación a una variable de un tipo de clase, que copia la referencia pero no el objeto identificado por la
21referencia.
22De forma similar a una asignación, cuando se pasa una estructura como parámetro de valor o se devuelve como
23resultado de un miembro de función, se crea una copia de la estructura. Una estructura se puede pasar por
24referencia a un miembro de función utilizando un parámetro ref u out.
25Cuando una propiedad o un indizador de una estructura es el destino de una asignación, la expresión de instancia
26asociada con el acceso a la propiedad o indizador debe estar clasificada como una variable. Si la expresión de
27instancia está clasificada como un valor, se produce un error de compilación. Esta categoría se explica con más
28detalle en la sección §7.13.1.

2911.3.4 Valores predeterminados


30Como se describe en la sección §5.2, cuando se crean algunos tipos de variables se inicializan automáticamente
31a sus valores predeterminados. Para variables de tipos de clase y otros tipos de referencia, el valor
32predeterminado es null. Sin embargo, como las estructuras son tipos de valor que no pueden ser null, el valor
33predeterminado de una estructura es el valor producido al establecer todos los campos de tipo de valor a sus
34valores predeterminados y todos los campos de tipo de referencia a null.
35Haciendo referencia a la estructura Point declarada anteriormente, en el ejemplo

36 Point[] a = new Point[100];


37se inicializa cada Point de la matriz al valor producido por establecer los campos x e y a cero.
38El valor predeterminado de una estructura corresponde al valor devuelto por el constructor predeterminado de la
39estructura (§4.1.1). A diferencia de una clase, una estructura no permite declarar un constructor de instancia sin
40parámetros. En su lugar, cada estructura tiene implícitamente un constructor de instancia sin parámetros que
41siempre devuelve el valor que resulta de establecer todos los campos de tipo de valor a sus valores
42predeterminados y todos los campos de tipo de referencia a null.

635Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 305


636Especificación del lenguaje C#

1Las estructuras deben diseñarse considerando que el estado de inicialización predeterminado sea un estado
2válido. En el siguiente ejemplo:

3 using System;
4 struct KeyValuePair

5 {

6 string key;
8 public KeyValuePair(string key, string value) {
7
9 if (key == null || value == null) throw new ArgumentException();

10 this.key = key;

11 this.value = value;
14el constructor de instancia definido por el usuario evita los valores nulos sólo donde es llamado explícitamente.
12En los casos en los que una variable KeyVa luePa i resté sujeta a una inicialización de valor predeterminado, los
15
16campos key y value serán nulos y la estructura debe estar preparada para controlar este estado.
13
1711.3.5 Boxing y Unboxing
18Un valor de un tipo de clase se puede convertir a un tipo object o a un tipo de interfaz que es implementada por
19la clase simplemente tratando la referencia como otro tipo en tiempo de compilación. Igualmente, un valor del
20tipo object o un valor de un tipo de interfaz se puede convertir de nuevo a un tipo de clase sin cambiar la
21referencia (pero en este caso es necesaria una comprobación de tipo en tiempo de ejecución).
22Dado que las estructuras no son tipos de referencia, estas operaciones se implementan de forma diferente para
23los tipos de estructura. Cuando se convierte un valor de un tipo de estructura a un tipo object o a un tipo de
24interfaz que es implementada por la estructura, tiene lugar una operación boxing. De la misma forma, cuando un
25valor de tipo object o un valor de tipo de interfaz se vuelve a convertir a un tipo de estructura, tiene lugar una
26operación unboxing. Una diferencia fundamental con las mismas operaciones en los tipos de clase es que las
27conversiones boxing y unboxing copian el valor de estructura en o fuera de la instancia a la que se ha aplicado la
28conversión boxing. Por lo tanto, después de una operación boxing o unboxing, los cambios realizados en la
29estructura a la que se ha aplicado la conversión unboxing no se reflejan en la estructura donde se ha aplicado la
30conversión boxing.
31Para obtener información detallada sobre boxing y unboxing, vea §4.3.

3211.3.6 Significado de This


33Dentro de un constructor de instancia o de un miembro de función de una clase, this está clasificado como un
34valor. Por lo tanto, mientras que this se puede utilizar para hacer referencia a la instancia para la que fue
35invocado el miembro de función, no es posible asignar a this en un miembro de función de una clase.
36Dentro de un constructor de instancia de una estructura, this corresponde a un parámetro out del tipo de
37estructura, y dentro de un miembro de función de instancia de una estructura, this corresponde a un parámetro
38ref del tipo de estructura. En ambos casos, this está clasificado como una variable, y es posible modificar toda
39la estructura para la que fue invocado el miembro de función mediante la asignación a this o pasándola como
40parámetros ref u out.

4111.3.7 Inicializadores de campo


42Como se describe en la sección §11.3.4, el valor predeterminado de una estructura es el valor que resulta de
43establecer todos los campos de tipo de valor a sus valores predeterminados y todos los campos de tipo de
44referencia a null. Por esta razón, una estructura no permite declaraciones de campos de instancia para incluir

637306 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


638 Capítulo 18 Código no seguro

1inicializadores de variables. Esta restricción sólo se aplica a los campos de instancia. Los campos estáticos de
2una estructura pueden incluir inicializadores de variable.

639Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 307


640Especificación del lenguaje C#

1En el ejemplo

2 struct Point

3 {

4 public int x = 1; // Error, initializer not permitted


75hay un error porque la declaración del campo de instancia incluye inicializadores de variables.
6811.3.8 Constructores
9A diferencia de una clase, una estructura no permite declarar un constructor de instancia sin parámetros. En su
10lugar, cada estructura tiene implícitamente un constructor de instancia sin parámetros que siempre devuelve el
11valor que resulta de establecer todos los campos de tipo de valor a sus valores predeterminados y todos los
12campos de tipo de referencia a null (§4.1.2). Una estructura puede declarar constructores de instancia con
13parámetros. Por ejemplo:

14 struct Point

15 {

16
17 public Point(int x, int y) {

18 this.x = x;

19 this.y = y;
22
20Dada la declaración anterior, las instrucciones
21
23 Point p1 = new Point();
24 Point p2 = new Point(0, 0);
25crean un Po in tcon x e y inicializados a cero.
26Un constructor de instancia de estructura no puede incluir un inicializador de constructor de la forma base(...).
27Si el constructor de instancia de la estructura no especifica un inicializador de constructor, la variable this
28corresponde a un parámetro out del tipo de estructura, y de forma similar a un parámetro out, this debe ser
29asignado de manera definitiva (§5.3) a cada ubicación a la que vuelva el constructor. Si el constructor de
30instancia de la estructura especifica un inicializador de constructor, la variable this corresponde a un parámetro
31ref del tipo de estructura, y de forma similar a un parámetro ref, this se considera definitivamente asignado en
32la entrada al cuerpo del constructor. Observe la implementación del constructor de instancia siguiente:

33 struct Point

34 {

35
36 public int X {

37 set { x = value; }

38
39 public int Y {

40 set { y = value; }

41

641308 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


642 Capítulo 18 Código no seguro

1 public Point(int x, int y) {

2 X = x; // error, this is not yet definitely assigned

3 Y = y; // error, this is not yet definitely assigned


64No puede llamarse a ninguna función de miembro de instancia (incluyendo los descriptores de acceso
7establecidos para las propiedades X e Y) hasta que todos los campos de la estructura que está construyéndose se
58hayan asignado definitivamente. Observe, no obstante, que si Point fuera una clase en lugar de una estructura,
9estaría permitida la implementación del constructor de instancia.

1011.3.9 Destructores
11No está permitido que una estructura declare un destructor.

1211.3.10Constructores Static
13Los constructores estáticos (static) para estructuras siguen la mayoría de las mismas reglas que las clases. La
14ejecución de un constructor estático para una estructura la desencadena el primero de los siguientes eventos que
15se produzcan en un dominio de aplicación:
16• Se hace referencia a un miembro de instancia de la estructura.
17• Se hace referencia a un miembro estático de la estructura.
18• Se llama a un constructor explícitamente declarado de la estructura.
19La creación de valores predeterminados (§11.3.4) de tipos de estructura no desencadena el constructor estático.
20(Un ejemplo de esto es el valor inicial de los elementos de una matriz.)

2111.4 Ejemplos de estructuras


22A continuación se muestran dos ejemplos significativos de la utilización de tipos struct para crear tipos que
23puedan utilizarse de manera similar a los tipos integrados del lenguaje, pero con semánticas modificadas.

2411.4.1 Tipo entero de base de datos


25La estructura DBInt siguiente implementa un tipo entero que puede representar el conjunto completo de valores
26del tipo int, además de un estado adicional que indica un valor desconocido. Un tipo de estas características se
27utiliza normalmente en bases de datos.

28 using System;
29 public struct DBInt

30 {

31
32 public static readonly DBInt Null = new DBInt();
33 // When the defined field is true, this DBInt represents a known value

34 // which is stored in the value field. When the defined field is false,

35
36 int value;

37 bool defined;
38 // Private instance constructor. Creates a DBInt with a known value.

643Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 309


644Especificación del lenguaje C#

1 DBInt(int value) {

2 this.value = value;

3 this.defined = true;
5 // The IsNull property is true if this DBInt represents an unknown value.
4
6 public bool IsNull { get { return !defined; } }
7 // The Value property is the known value of this DBInt, or 0 if this

8 // DBInt represents an unknown value.


9 public int Value { get { return value; } }
10 // Implicit conversion from int to DBInt.
11 public static implicit operator DBInt(int x) {

12 return new DBInt(x);

13
14 // Explicit conversion from DBInt to int. Throws an exception if the

15 // given DBInt represents an unknown value.


16 public static explicit operator int(DBInt x) {

17 if (!x.defined) throw new InvalidOperationException();

18 return x.value;
20 public static DBInt operator +(DBInt x) {
19
21 return x;

22
23 public static DBInt operator -(DBInt x) {

24 return x.defined ? -x.value : Null;

25
26 public static DBInt operator +(DBInt x, DBInt y) {

27 return x.defined && y.defined? x.value + y.value: Null;

28
29 public static DBInt operator -(DBInt x, DBInt y) {

30 return x.defined && y.defined? x.value - y.value: Null;

31
32 public static DBInt operator *(DBInt x, DBInt y) {

33 return x.defined && y.defined? x.value * y.value: Null;

34
35 public static DBInt operator /(DBInt x, DBInt y) {

36 return x.defined && y.defined? x.value / y.value: Null;

37
38 public static DBInt operator %(DBInt x, DBInt y) {

39 return x.defined && y.defined? x.value % y.value: Null;

40
41 public static DBBool operator ==(DBInt x, DBInt y) {

42 return x.defined && y.defined? x.value == y.value: DBBool.Null;

43
44 public static DBBool operator !=(DBInt x, DBInt y) {

45 return x.defined && y.defined? x.value != y.value: DBBool.Null;

46
645310 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
646 Capítulo 18 Código no seguro

1 public static DBBool operator >(DBInt x, DBInt y) {

2 return x.defined && y.defined? x.value > y.value: DBBool.Null;

34 public static DBBool operator <(DBInt x, DBInt y) {

5 return x.defined && y.defined? x.value < y.value: DBBool.Null;

67 public static DBBool operator >=(DBInt x, DBInt y) {

8 return x.defined && y.defined? x.value >= y.value: DBBool.Null;

109 public static DBBool operator <=(DBInt x, DBInt y) {

11 return x.defined && y.defined? x.value <= y.value: DBBool.Null;

12
13 public override bool Equals(object obj) {

14 if (!(obj is DBInt)) return false;

15 DBInt x = (DBInt)obj;

16
18 public override int GetHashCode() {
17
19 return value;

20
21 public override string ToString() {

22 return defined? value.ToString(): “DBInt.Null”;

23 }
25
24 11.4.2 Tipo booleano de base de datos
26La estructura DBBoo l siguiente implementa un tipo lógico de tres valores. Los valores posibles de este tipo son
27DBBoo l . True, DBBool.False y DBBool.Null, donde el miembro Null indica un valor desconocido. Estos tipos
28lógicos de tres valores se utilizan con frecuencia en bases de datos.

29 using System;
30 public struct DBBool

31 {

32
33 public static readonly DBBool Null = new DBBool(0);

34 public static readonly DBBool False = new DBBool(-1);

35
36 // Private field that stores –1, 0, 1 for False, Null, True.
37 sbyte value;
38 // Private instance constructor. The value parameter must be –1, 0, or 1.
39 DBBool(int value) {

40 this.value = (sbyte)value;

41
42 // Properties to examine the value of a DBBool. Return true if this

43 // DBBool has the given value, false otherwise.


44 public bool IsNull { get { return value == 0; } }
45 public bool IsFalse { get { return value < 0; } }

647Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 311


648Especificación del lenguaje C#

1 public bool IsTrue { get { return value > 0; } }


2 // Implicit conversion from bool to DBBool. Maps true to DBBool.True and

3 // false to DBBool.False.
4 public static implicit operator DBBool(bool x) {

5 return x? True: False;

67 // Explicit conversion from DBBool to bool. Throws an exception if the

8 // given DBBool is Null, otherwise returns true or false.


9 public static explicit operator bool(DBBool x) {

10 if (x.value == 0) throw new InvalidOperationException();

11 return x.value > 0;


13 // Equality operator. Returns Null if either operand is Null, otherwise
12
14 // returns True or False.
15 public static DBBool operator ==(DBBool x, DBBool y) {

16 if (x.value == 0 || y.value == 0) return Null;

17 return x.value == y.value? True: False;


19 // Inequality operator. Returns Null if either operand is Null, otherwise
18
20 // returns True or False.
21 public static DBBool operator !=(DBBool x, DBBool y) {

22 if (x.value == 0 || y.value == 0) return Null;

23 return x.value != y.value? True: False;


25 // Logical negation operator. Returns True if the operand is False, Null
24
26 // if the operand is Null, or False if the operand is True.
27 public static DBBool operator !(DBBool x) {

28 return new DBBool(-x.value);

29
30 // Logical AND operator. Returns False if either operand is False,

31 // otherwise Null if either operand is Null, otherwise True.


32 public static DBBool operator &(DBBool x, DBBool y) {

33 return new DBBool(x.value < y.value? x.value: y.value);

34
35 // Logical OR operator. Returns True if either operand is True, otherwise

36 // Null if either operand is Null, otherwise False.


37 public static DBBool operator |(DBBool x, DBBool y) {

38 return new DBBool(x.value > y.value? x.value: y.value);

39
40 // Definitely true operator. Returns true if the operand is True, false

41 // otherwise.
42 public static bool operator true(DBBool x) {

43 return x.value > 0;

44
45 // Definitely false operator. Returns true if the operand is False, false

46 // otherwise.

649312 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


650 Capítulo 18 Código no seguro

1 public static bool operator false(DBBool x) {

2 return x.value < 0;

34 public override bool Equals(object obj) {

5 if (!(obj is DBBool)) return false;

6 return value == ((DBBool)obj).value;


8 public override int GetHashCode() {
7
9 return value;

10
11 public override string ToString() {

12 if (value > 0) return "DBBool.True";

13 if (value < 0) return "DBBool.False";

14 return "DBBool.Null";

15
16

651Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 313


652Especificación del lenguaje C#

1 12.Matrices

2Una matriz es una estructura de datos que contiene una serie de variables a las que se obtiene acceso a través de
3índices calculados. Las variables contenidas en una matriz, también conocidas como elementos de la matriz, son
4todas del mismo tipo, denominado tipo de elemento de la matriz.
5Una matriz tiene un rango que determina el número de índices asociados a cada elemento de la matriz. El rango
6de una matriz también se conoce como número de dimensiones de la matriz. Una matriz con un rango de uno se
7denomina matriz unidimensional. Una matriz con un rango mayor que uno se denomina matriz
8multidimensional. Las matrices multidimensionales de un determinado tamaño se suelen denominar matrices
9bidimensionales, tridimensionales, etc.
10Cada dimensión de una matriz tiene una longitud asociada, que es un número entero mayor o igual a cero. Las
11longitudes de dimensión no forman parte del tipo de matriz, sino que se establecen cuando se crea una instancia
12del tipo de matriz en tiempo de ejecución. La longitud de una dimensión determina el intervalo válido de índices
13para esa dimensión: para una dimensión de longitud N, los índices pueden tomar valores de cero a N – 1
14inclusive. El número total de elementos en una matriz es el producto de las longitudes de cada dimensión de la
15matriz. Si alguna de las dimensiones de una matriz tiene una longitud cero, se dice que la matriz está vacía.
16Los elementos de una matriz pueden ser de cualquier tipo, incluido un tipo de matriz.

1712.1 Tipos de matriz


18Un tipo de matriz se escribe como non-array-type seguido de uno o más especificadores de rango (rank-
19specifers):
20 array-type:
21 non-array-type rank-specifiers
22 non-array-type:
23 type
24 rank-specifiers:
25 rank-specifier
26 rank-specifiers rank-specifier
27 rank-specifier:
28 [ dim-separatorsopt ]
29 dim-separators:
30 ,
31 dim-separators ,
32Un tipo no de matriz (non-array-type) es cualquier tipo que no es en sí mismo un tipo de matriz (array-type).
33El rango de un tipo de matriz viene dado por el especificador de rango (rank-specifier) situado en el extremo
34izquierdo del tipo de matriz (array-type): así, un especificador de rango indica que la matriz posee un rango de
35uno más el número de símbolos "," del especificador de rango.
36El tipo de elemento de un tipo de matriz es el tipo resultante de eliminar el especificador de rango (rank-
37specifier) del extremo izquierdo:
38• Un tipo de matriz de la forma T[R] es una matriz de rango R y un tipo de elemento T que no sea de matriz.

653314 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


654 Capítulo 18 Código no seguro

1• Un tipo de matriz de la forma T[R] [R1]...[RN] es una matriz de rango R y un tipo de elemento T[R1]...[RN]
2 que no sea de matriz.
3Lo que sucede es que los especificadores de rango (rank-specifiers) se leen de izquierda a derecha, antes del tipo
4de elemento final que no es de matriz. El tipo int[][,,][,] es una matriz unidimensional que contiene matrices
5tridimensionales de matrices bidimensionales de tipo int.
6En tiempo de ejecución, el valor de un tipo de matriz puede ser null o una referencia a una instancia de dicho
7tipo.

812.1.1 Tipo System.Array


9El tipo Sys tem.Ar rayes el tipo base abstracto de todos los tipos de matrices. Se produce una conversión de
10referencia implícita (§6.1.4) entre cualquier tipo de matriz y un tipo Sys tem.Ar ray, así como una conversión de
11referencia explícita (§6.2.3) de System.Array a cualquier tipo de matriz. Observe que System.Array no es en
12sí mismo un tipo de matriz (array-type). Al contrario, es un tipo de clase (class-type) del que se derivan todos
13los tipos de matriz (array-types).
14En tiempo de ejecución, un valor de tipo System.Array puede ser null o una referencia a una instancia de
15cualquier tipo de matriz.

1612.2 Creación de matrices


17Las instancias de matriz se crean mediante expresiones de creación de matriz (array-creation-expressions)
18(§7.5.10.2) o mediante declaraciones de campo o variable local que incluyen un inicializador de matriz (array-
19initializer) (§12.6).
20Cuando se crea una instancia de matriz, se establecen el rango y longitud de cada dimensión y a continuación
21permanecen constante por toda la duración de la instancia. Es decir, no es posible cambiar el rango de una
22instancia de matriz existente ni cambiar el tamaño de sus dimensiones.
23Una instancia de matriz es siempre de tipo de matriz. El tipo System.Array es un tipo abstracto del que no se
24pueden crear instancias.
25Los elementos de matrices creadas mediante expresiones de creación de matrices (array-creation-expressions)
26se inicializan siempre con sus valores predeterminados (§5.2).

2712.3 Acceso a los elementos de matriz


28El acceso a los elementos de una matriz se obtiene mediante expresiones de acceso a elementos (element-access)
29(§7.5.6.1) de la forma A[I1, I2, ..., IN], donde A es una expresión de un tipo de matriz y cada IX es una expresión
30de tipo int, uint, long, ulong, o de un tipo que puede convertirse de manera implícita a uno o varios de estos
31tipos. El resultado de un acceso a elemento de matriz es una variable, en concreto el elemento de matriz
32seleccionado por los índices.
33Los elementos de una matriz se pueden enumerar mediante una instrucción foreach (§8.8.4).

3412.4 Miembros de matriz


35Cada tipo de matriz hereda los miembros declarados por el tipo System.Array.

3612.5 Covarianza de matrices


37Por cada dos tipos de referencia (reference-types) A y B, si existe una conversión de referencia implícita (§6.1.4)
38o explícita (§6.2.3) entre A y B, también tiene lugar la misma conversión de referencia entre el tipo de matriz
39A[R] y el tipo de matriz B[R], donde R es cualquier especificador de rango (rank-specifier), si bien es el mismo
40para ambos tipos de matriz. Esta relación se conoce como covarianza matricial. La covarianza matricial

655Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 315


656Especificación del lenguaje C#

1significa, en concreto, que un valor de un tipo de matriz A[R] puede ser en realidad una referencia a una
2instancia de un tipo de matriz B[R], siempre que exista una conversión de referencia implícita entre B y A.
3Debido a la covarianza de matrices, las asignaciones a elementos de matrices de tipo de referencia incluyen una
4comprobación en tiempo de ejecución que garantiza que el valor que se asigna al elemento de matriz es
5realmente de un tipo permitido (§7.13.1). Por ejemplo:
6 c lass Tes t
7 {
8 s ta t i c vo id F i l l ( ob jec t [ ] ar ray , i n t i ndex , i n t count , ob jec t va lue ) {
9 f o r ( i n t i = i ndex ; i < i ndex + count ; i++) ar ray [ i ] = va lue ;
10
11 s ta t i c vo id Main ( ) {
12 s t r i ng [ ] s t r i ngs = new s t r i ng [100] ;
13 F i l l ( s t r i ngs , 0 , 100 , "Unde f i ned" ) ;
14 F i l l ( s t r i ngs , 0 , 10 , nu l l ) ;
15 F i l l ( s t r i ngs , 90 , 10 , 0) ;
16 }
18La asignación a ar ray [ ien ] el método F i l incluye
l implícitamente una comprobación en tiempo de ejecución,
17que garantiza que el objeto al que hace referencia value es null o bien una instancia de un tipo compatible con
19
20el tipo de elemento real de la matriz array. En Main, las dos primeras llamadas a Fill se ejecutan con éxito, pero
21la tercera llamada provoca una excepción System.ArrayTypeMismatchException al ejecutarse la primera
22asignación a array[i]. La excepción se produce porque no se puede almacenar un valor int boxed en una matriz
23string.
24La covarianza matricial no abarca las matrices de tipos de valor (value-types). Por ejemplo, no hay ninguna
25conversión que permita tratar un tipo int[] como object[].

2612.6 Inicializadores de matrices


27Los inicializadores de matrices se pueden especificar en declaraciones de campo (§10.4), declaraciones de
28variable local (§8.5.1) y expresiones de creación de matrices (§7.5.10.2):
29 array-initializer:
30 { variable-initializer-listopt }
31 { variable-initializer-list , }
32 variable-initializer-list:
33 variable-initializer
34 variable-initializer-list , variable-initializer
35 variable-initializer:
36 expression
37 array-initializer
38Un inicializador de matriz se compone de una secuencia de inicializadores de variable, que figuran entre los
39símbolos “{” y “}” y separados por “,”. Cada inicializador de variable es una expresión o, en el caso de una
40matriz multidimensional, un inicializador de matriz anidado.
41El contexto en que se utiliza un inicializador de matriz determina el tipo de la matriz que se inicializa. En una
42expresión de creación de matriz, el tipo de matriz precede inmediatamente al inicializador. En una declaración
43de campo o de variable, el tipo de matriz es el tipo del campo o variable que se está declarando. Cuando se
44utiliza un inicializador de matriz en una declaración de campo o de variable, como:
45 i n t [ ] a = {0 , 2 , 4 , 6 , 8} ;

657316 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


658 Capítulo 18 Código no seguro

1equivale a la siguiente expresión de creación de matriz:

2 int[] a = new int[] {0, 2, 4, 6, 8};


3Para una matriz unidimensional, el inicializador de matriz debe estar compuesto de una secuencia de
4expresiones que sean compatibles en materia de asignación con el tipo de elemento de la matriz. Las expresiones
5inicializan los elementos de matriz por orden creciente, comenzando a partir del elemento en el índice cero. El
6número de expresiones del inicializador de matriz determina la longitud de la instancia de matriz que se está
7creando. Por ejemplo, el inicializador de matriz anterior crea una instancia i n t [ de
] longitud 5 y a continuación
8inicializa la instancia con los siguientes valores:

9 a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;


10Para una matriz multidimensional, el inicializador de matriz debe tener tantos niveles de anidamiento como
11dimensiones tenga la matriz. El nivel de anidamiento superior se corresponde con la dimensión situada en el
12extremo izquierdo y el nivel de anidamiento inferior se corresponde con la dimensión situada en el extremo
13derecho. La longitud de cada una de las dimensiones de la matriz está determinada por el número de elementos
14del nivel de anidamiento correspondiente en el inicializador de la matriz. Por cada inicializador de matriz
15anidado, el número de elementos debe ser igual al de los demás inicializadores de matriz que están situados en el
16mismo nivel. En el ejemplo:

17 int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
18se crea una matriz bidimensional con una longitud de cinco para la dimensión del extremo izquierdo y una
19longitud de dos para la dimensión del extremo derecho:

20 int[,] b = new int[5, 2];


21y a continuación se inicializa la instancia de la matriz con los valores siguientes:

22 b[0, 0] = 0; b[0, 1] = 1;

23 b[1, 0] = 2; b[1, 1] = 3;

24 b[2, 0] = 4; b[2, 1] = 5;
27Cuando una expresión de creación de matriz incluye tanto longitudes de dimensión explícitas como un
25
28inicializador de matriz, las longitudes deben ser expresiones constantes, y el número de elementos en cada nivel
26
29de anidamiento debe coincidir con la longitud de dimensión correspondiente. A continuación se describen
30algunos ejemplos:

31 int i = 3;

32 int[] x = new int[3] {0, 1, 2}; // OK

33 int[] y = new int[i] {0, 1, 2}; // Error, i not a constant


35Aquí, el inicializador de y es un error de compilación porque la expresión de longitud de dimensión no es una
36
34constante, y el inicializador de z es un error de compilación porque la longitud y el número de elementos del
37inicializador no coinciden.

659Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 317


660Especificación del lenguaje C#

1 13.Interfaces

2Una interfaz define un contrato. Una clase o estructura que implementa una interfaz debe adherirse a su
3contrato. Una interfaz puede derivarse de varias interfaces base, y una clase o estructura puede implementar
4varias interfaces.
5Las interfaces pueden contener métodos, propiedades, eventos e indizadores. La propia interfaz no proporciona
6implementaciones para los miembros que define. La interfaz sólo especifica los miembros que las clases o
7estructuras deben proporcionar, y que implementan la interfaz.

813.1 Declaraciones de interfaz


9Una declaración de interfaz (interface-declaration) es una declaración de tipo (type-declaration) (§9.5) que
10declara un nuevo tipo de interfaz.
11 interface-declaration:
12 attributesopt interface-modifiersopt i n te r faceidentifier interface-baseopt interface-body ;opt
13Una declaración de interfaz (interface-declaration) consiste en un conjunto de atributos (attributes) (§17)
14opcionales, seguidos de un conjunto de modificadores de interfaz (interface-modifiers) (§13.1.1), de la palabra
15clave i n te r face
y de un identificador (identifier) que da nombre a la interfaz, de una especificación opcional de
16base de interfaz (interface-base) (§13.1.2), de un cuerpo de interfaz (interface-body) (§13.1.3) y de un punto y
17coma opcional.

1813.1.1 Modificadores de interfaz


19Una declaración de interfaz (interface-declaration) puede incluir opcionalmente una secuencia de modificadores
20de interfaz:
21 interface-modifiers:
22 interface-modifier
23 interface-modifiers interface-modifier
24 interface-modifier:
25 new
26 pub l i c
27 pro tec ted
28 i n te rna l
29 private
30Cuando el mismo modificador aparece varias veces en una declaración de interfaz, se produce un error en
31tiempo de compilación.
32El modificador new sólo se permite en interfaces definidas dentro de una clase. Especifica que la interfaz oculta
33un miembro heredado con el mismo nombre, como se describe en §10.2.2.
34Los modificadores public, protected, internal y private controlan la accesibilidad a la interfaz. Dependiendo
35del contexto en que se produzca la declaración de interfaz, puede que sólo se permitan algunos de estos
36modificadores (§3.5.1).

661318 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


662 Capítulo 18 Código no seguro

113.1.2 Interfaces base


2Una interfaz puede no heredar de ninguna interfaz o heredar de varias, que se denominan interfaces base
3explícitas de la interfaz. Cuando una interfaz tiene una o más interfaces base explícitas, el identificador de
4interfaz, en la declaración, viene seguido de un carácter de dos puntos y una lista de identificadores de interfaz
5base separados por comas.
6 interface-base:
7 : interface-type-list
8Las interfaces base explícitas de una interfaz deben ser por lo menos tan accesibles como la propia interfaz
9(§3.5.4). Por ejemplo, supone un error en tiempo de compilación especificar una interfaz pr i va teo i n te rnaen
l
10la interfaz base (interface-base) de una interfaz public.
11Igualmente, se produce un error en tiempo de compilación cuando una interfaz hereda directa o indirectamente
12de sí misma.
13Las interfaces base de una interfaz son las interfaces base explícitas y sus interfaces base. En otras palabras, el
14conjunto de interfaces base se compone de la terminación transitiva completa de las interfaces base explícitas,
15sus interfaces base explícitas, etc. Una interfaz hereda a todos los miembros de sus interfaces base. En el
16siguiente ejemplo:
17 i n te r face I Con t ro l
18 {
19 vo id Pa in t ( ) ;
20 }
21 i n te r face I Tex tBox : I Con t ro l
22 {
23 vo id Set Tex t ( s t r i ng t ex t ) ;
24 }
25 i n te r face I L i s tBox : I Con t ro l
26 {
27 vo id Set I tems(s t r i ng [ ] i t ems) ;
28 }
29 i n te r face I ComboBox : I Tex tBox , I L i s tBox {}
30las interfaces base de I ComboBox son I Con t ro, lITextBox e IListBox.
31Es decir, la interfaz IComboBox anterior hereda los miembros SetText y SetItems, así como Paint.
32Una clase o estructura que implementa una interfaz también implementa implícitamente todas las interfaces base
33de dicha interfaz.

3413.1.3 Cuerpo de interfaz


35El cuerpo de una interfaz (interface-body) define sus miembros respectivos.
36 interface-body:
37 { interface-member-declarationsopt }

3813.2 Miembros de interfaz


39Los miembros de una interfaz son los miembros heredados de las interfaces base y los miembros declarados por
40la propia interfaz.
41 interface-member-declarations:
42 interface-member-declaration
43 interface-member-declarations interface-member-declaration

663Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 319


664Especificación del lenguaje C#

1 interface-member-declaration:
2 interface-method-declaration
3 interface-property-declaration
4 interface-event-declaration
5 interface-indexer-declaration
6Una declaración de interfaz puede declarar cero o varios miembros. Los miembros de una interfaz pueden ser
7métodos, propiedades, eventos o indizadores. Una interfaz no puede contener constantes, campos, operadores,
8constructores de instancia, destructores ni tipos, ni tampoco puede una interfaz contener miembros estáticos de
9ningún tipo.
10Todos los miembros de interfaz tienen implícitamente acceso público. Supone un error en tiempo de
11compilación que las declaraciones de miembros de interfaz contengan modificadores. En particular, los
12miembros de interfaces no pueden declararse con los modificadores abs t rac,tpub l i c, protected, internal,
13private, virtual, override o static.
14En el ejemplo

15 public delegate void StringListEvent(IStringList sender);


16 public interface IStringList

17 {

18
19 int Count { get; }
20 event StringListEvent Changed;
21 string this[int index] { get; set; }

22 } una interfaz que contiene cada uno de los tipos posibles de miembro: un método, una propiedad, un
23se declara
24evento y un indizador.
25Una declaración de interfaz (interface-declaration) crea un nuevo espacio de declaración (§3.3), y las
26declaraciones de miembro de interfaz (interface-member-declarations) contenidas inmediatamente en la
27declaración de interfaz agregan nuevos miembros al espacio de declaración. Las siguientes reglas se aplican a
28las declaraciones de miembros de interfaz (interface-member-declarations):
29• El nombre de un método debe ser diferente del de cualquier otra propiedad o evento declarados en la misma
30 interfaz. Además, la firma (§3.6) de un método debe ser distinta de las firmas de todos los demás métodos
31 declarados en la misma interfaz, y dos métodos declarados en la misma interfaz no pueden tener firmas que
32 sólo se diferencien por ref y out.
33• El nombre de una propiedad o evento debe ser diferente de los nombres de todos los demás miembros
34 declarados en la misma interfaz.
35• La firma de un indizador debe ser diferente de las firmas de los demás indizadores declarados en la misma
36 interfaz.
37Los miembros heredados de una interfaz no forman parte específicamente del espacio de declaración de la
38interfaz. De esta forma, una interfaz puede declarar un miembro con el mismo nombre o firma que un miembro
39heredado. Cuando esto ocurre, se dice que el miembro de interfaz derivado oculta el miembro de interfaz base.
40Ocultar un miembro heredado no se considera un error, pero hace que el compilador emita una advertencia. Para
41evitar la advertencia, la declaración del miembro de interfaz heredado debe incluir el modificador new para
42indicar que el miembro derivado está destinado a ocultar el miembro base. Este tema se explica con más detalle
43en la sección §3.7.1.2.

665320 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


666 Capítulo 18 Código no seguro

1Si se incluye un modificador new en una declaración que no oculta un miembro heredado, se emite una
2advertencia a tal efecto. Esta advertencia se evita quitando el modificador new .
3Tenga en cuenta que los miembros de la clase ob jec tno son, estrictamente hablando, miembros de ninguna
4interfaz (§13.2). Sin embargo, los miembros de la clase ob jec testán disponibles a través de la búsqueda de
5miembros en cualquier tipo de interfaz (§7.3).

613.2.1 Métodos de interfaz


7Los métodos de interfaz se declaran mediante declaraciones de métodos de interfaz (interface-method-
8declarations):
9 interface-method-declaration:
10 attributesopt new opt return-type identifier ( formal-parameter-listopt ) ;
11Los atributos (attributes), el tipo de valor devuelto (return-type), el identificador (identifier) y la lista de
12parámetros formales (formal-parameter-list) de una declaración de método de interfaz pueden tener el mismo
13significado que los de una declaración de método perteneciente a una clase (§10.5). No se permite que una
14declaración de método de interfaz especifique un cuerpo del método, y la declaración por tanto siempre termina
15con un punto y coma.

1613.2.2 Propiedades de interfaz


17Las propiedades de interfaz se declaran mediante declaraciones de propiedad de interfaz (interface-property-
18declaration):
19 interface-property-declaration:
20 attributesopt newopt type identifier { interface-accessors }
21 interface-accessors:
22 attributesopt get ;
23 attributesopt set ;
24 attributesopt get ; attributesopt set ;
25 attributesopt set ; attributesopt get ;
26Los atributos (attributes), el tipo (type) y el identificador (identifier) de una declaración de propiedad de interfaz
27pueden tener el mismo significado que los de una declaración de propiedad perteneciente a una clase (§10.6).
28Los descriptores de acceso correspondientes a una declaración de propiedad de interfaz son los descriptores de
29acceso de una declaración de propiedad de clase (§10.6.2), salvo que el cuerpo del descriptor de acceso siempre
30debe ser un punto y coma. Así, el propósito de los descriptores de acceso es indicar si la propiedad es de lectura
31y escritura, de sólo lectura o de sólo escritura.

3213.2.3 Eventos de interfaz


33Los eventos de interfaz se declaran mediante declaraciones de evento de interfaz (interface-event-declaration):
34 interface-event-declaration:
35 attributesopt newopt event type identifier ;
36Los atributos (attributes), el tipo (type) y el identificador (identifier) de una declaración de evento de interfaz
37pueden tener el mismo significado que los de una declaración de evento perteneciente a una clase (§10.7).

3813.2.4 Indizadores de interfaz


39Los indizadores de interfaz se declaran mediante declaraciones de indizador de interfaz (interface-indexer-
40declarations):

667Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 321


668Especificación del lenguaje C#

1 interface-indexer-declaration:
2 attributesopt new opt type th i s [ formal-parameter-list ] { interface-accessors }
3Los atributos (attributes), el tipo (type) y la lista de parámetros formales (formal-parameter-list) de una
4declaración de indizador de interfaz pueden tener el mismo significado que los de una declaración de indizador
5perteneciente a una clase (§10.8).
6Los descriptores de acceso correspondientes a una declaración de indizador de interfaz son los descriptores de
7acceso de una declaración de indizador de clase (§10.8), salvo que el cuerpo del descriptor de acceso siempre
8debe ser un punto y coma. Así, el propósito de los descriptores de acceso es indicar si el indizador es de lectura
9y escritura, de sólo lectura o de sólo escritura.

1013.2.5 Acceso a miembros de interfaz


11El acceso a los miembros de interfaz tiene lugar mediante las expresiones de acceso a miembros (§7.5.4) y
12expresiones de acceso a indizadores (§7.5.6.2) de la forma I.M e I[A], donde I es un tipo de interfaz, M es un
13método, propiedad o evento de dicho tipo de interfaz y A es una lista de argumentos del indizador.
14Para interfaces que son estrictamente de herencia simple (todas las interfaces de la cadena de herencia tienen
15exactamente cero o una sola interfaz base directa), los efectos de las reglas de búsqueda de miembros (§7.3),
16llamadas a métodos (§7.5.5.1) y acceso a indizadores (§7.5.6.2) son exactamente iguales que las aplicables a
17clases y estructuras: más miembros derivados ocultan menos miembros derivados con el mismo nombre o firma.
18Sin embargo, para interfaces de herencia múltiple, pueden existir ambigüedades cuando dos o más interfaces
19base no relacionadas entre sí declaran miembros con el mismo nombre o firma. Esta sección muestra varios
20ejemplos de tales situaciones. En todos los casos, pueden utilizarse las conversiones implícitas para resolver las
21ambigüedades.
22En el siguiente ejemplo:

23 interface IList

24 {

25 int Count { get; set; }


27 interface ICounter
26
28 {

29 void Count(int i);


31 interface IListCounter: IList, ICounter {}
30
32 class C

33 {

34 void Test(IListCounter x) {

35 x.Count(1); // Error

36 x.Count = 1; // Error

37
41las dos primeras ((IList)x).Count = 1; en tiempo
instrucciones causan errores // de
Ok, invokes ya
compilación, IList.Count.set
que la búsqueda de miembros (§7.3)
42
38 de Count en IListCounter es ambigua. Como muestra el ejemplo, la ambigüedad se resuelve convirtiendo x en
43el tipo de interfaz base apropiado. Tales conversiones no tienen costo de ejecución; simplemente consisten en
39ver la instancia como un tipo menos derivado en tiempo de compilación.
44
40En el siguiente ejemplo:
45

669322 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


670 Capítulo 18 Código no seguro

1 interface IInteger

2 {

3 void Add(int i);


5 interface IDouble
4
6 {

7 void Add(double d);


9 interface INumber: IInteger, IDouble {}
8
10 class C

11 {

12 void Test(INumber n) {

13 n.Add(1); // Invokes IInteger.Add

14 n.Add(1.0); // Only IDouble.Add is applicable

15
19la llamada n .Add(1((IInteger)n).Add(1);
)selecciona I I n tege r .Addal // Only
aplicar las IInteger.Add is de
reglas de resolución a sobrecargas
candidatede §7.4.2. De
20
16manera similar, la llamada n.Add(1.0) selecciona IDouble.Add. Cuando se insertan conversiones explícitas,
21sólo hay un método candidato y por ello no hay ambigüedad.
17
22En el siguiente ejemplo:
18
23 i n te r face I Base
24 {
25 vo id F ( i n t i ) ;
26 }
27 i n te r face I Le f t : I Base
28 {
29 new vo id F ( i n t i ) ;
30 }
31 i n te r face I R igh t : I Base
32 {
33 vo id G( ) ;
34 }
35 i n te r face IDe r i ved : I Le f t , I R igh t {}
36 c lass A
37 {
38 vo id Tes t ( IDe r i ved d) {
39 d .F (1 ) ; / / I nvokes I Le f t . F
40 ( ( IBase )d ) . F (1 ) ; / / I nvokes I Base .F
41 ( ( I Le f t )d ) . F (1 ) ; / / I nvokes I Le f t . F
42 ( ( IR igh t )d ) . F (1 ) ; / / I nvokes I Base .F
43el miembro I Base .Festá oculto por el miembro I Le f t . FLa llamada d.F(1) selecciona ILeft.F, aunque IBase.F
45
46no parece quedar oculta en la ruta de acceso que conduce a IRight.
44
47La regla intuitiva para la ocultación en interfaces de herencia múltiple es sencilla: si un miembros está oculto en
48cualquier ruta de acceso, queda oculto en todas las rutas de acceso. Debido a que la ruta de acceso desde

671Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 323


672Especificación del lenguaje C#

1IDe r i vedhasta I Le f te IBase oculta IBase.F, el miembro también queda oculto en la ruta de acceso desde
2IDerived hasta IRight e IBase.

313.3 Nombres completos de miembros de interfaz


4A menudo se conoce un miembro de interfaz por su nombre completo. El nombre completo de un miembro de
5interfaz se compone del nombre de la interfaz en que se declara el miembro, seguido de un punto y del nombre
6del miembro. El nombre completo de un miembro hace referencia a la interfaz donde se declara. Por ejemplo,
7dadas las declaraciones

8 interface IControl

9 {

10 void Paint();
12 interface ITextBox: IControl
11
13 {

14 void SetText(string text);


16el nombre completo de Paint es IControl.Paint y el de SetText es ITextBox.SetText.
15
17En el ejemplo anterior, no es posible hacer referencia a Paint como ITextBox.Paint.
18Cuando una interfaz forma parte de un espacio de nombres, el nombre completo de un miembro de interfaz
19incluye el espacio de nombres. Por ejemplo:

20 namespace System

21 {

22 public interface ICloneable

23 {

24
27Aquí, el nombreobject
completoClone();
del método Clone es System.ICloneable.Clone.
25
2813.4 Implementaciones de interfaces
26
29Las interfaces pueden implementarse mediante clases y estructuras. Para indicar que una clase o estructura
30implementa una interfaz, el identificador de la interfaz se incluye en la lista de clases base de la clase o
31estructura. Por ejemplo:

32 interface ICloneable

33 {

34 object Clone();
36 interface IComparable
35
37 {

38 int CompareTo(object other);


40 class ListEntry: ICloneable, IComparable
39
41 {

42
43 public int CompareTo(object other) {...}

44 }

673324 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


674 Capítulo 18 Código no seguro

1Una clase o estructura que implementa una interfaz también implementa implícitamente todas las interfaces base
2de dicha interfaz. Esto es cierto aunque la clase o estructura no muestre explícitamente todas las interfaces base
3de la lista de clases base. Por ejemplo:

4 interface IControl

5 {

6 void Paint();
8 interface ITextBox: IControl
7
9 {

10 void SetText(string text);


12 class TextBox: ITextBox
11
13 {

14
15 public void SetText(string text) {...}

16
17Aquí, la }clase Tex tBoximplementa I Con t ro el ITextBox.

1813.4.1 Implementaciones de miembro de interfaz explícitas


19Para los fines de implementar interfaces, una clase o estructura puede declarar implementaciones explícitas de
20miembros de interfaz. Una implementación explícita de miembro de interfaz es una declaración de método,
21propiedad, evento o indizador que hace referencia a un nombre completo de miembro de interfaz. Por ejemplo:

22 interface ICloneable

23 {

24 object Clone();
26 interface IComparable
25
27 {

28 int CompareTo(object other);


30 class ListEntry: ICloneable, IComparable
29
31 {

32
33 int IComparable.CompareTo(object other) {...}

34Aquí, ICloneable.Clone
35 } e IComparable.CompareTo son implementaciones explícitas de miembros de
36interfaz.
37En algunos casos, el nombre de un miembro de interfaz puede no ser apropiado para la clase que lo implementa,
38en cuyo caso puede implementarse con la implementación explícita de miembros de interfaz. Es probable que
39una clase que implemente una abstracción de archivo, por ejemplo, implemente una función miembro Close que
40tenga el efecto de liberar el recurso de archivo e implemente el método Dispose de la interfaz IDisposable
41mediante la implementación explícita de miembros de interfaz:
42 interface IDisposable
43 {
44 void Dispose();
45 }

675Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 325


676Especificación del lenguaje C#

1 class MyFile: IDisposable


2 {
3 void IDisposable.Dispose() {
4 Close();
5 }
6 public void Close() {
7 // Do what's necessary to close the file
8 System.GC.SuppressFinalize(this);
9 }
10 }
11No es posible obtener acceso a una implementación explícita de miembros de interfaz utilizando el nombre
12completo en una llamada a un método, instrucción de acceso a propiedad o acceso a indizador. Sólo se puede
13obtener acceso a una implementación explícita de miembros de interfaz a través de una instancia de interfaz, en
14cuyo caso se hace referencia a ella por el nombre del miembro.
15Si en una implementación explícita de miembros de interfaz se incluyen modificadores de acceso o
16modificadores abs t rac,tv i r tua, loverride o static, se producirán errores en tiempo de compilación.

677326 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


678 Capítulo 18 Código no seguro

1Las implementaciones explícitas de miembros de interfaz tienen diferentes características de acceso respecto a
2otros miembros. Dado que este tipo de implementaciones nunca son accesibles por su nombre completo en una
3llamada a un método o una instrucción de acceso a una propiedad, son en cierto sentido privadas. Sin embargo,
4debido a que se puede obtener acceso a ellas a través de una instancia de propiedad, también son en otro sentido
5públicas.
6Las implementaciones explícitas de miembros de interfaz tienen dos principales objetivos:
7• Puesto que no se puede obtener acceso a ellas por medio de instancias de clase o estructura, permiten excluir
8 las implementaciones de interfaz de la interfaz pública de una clase o estructura. Esto resulta de especial
9 utilidad cuando una clase o estructura implementa una interfaz interna que no es de interés para ningún
10 consumidor de dicha clase o estructura.
11• Las implementaciones explícitas de miembros de interfaz permiten la eliminación de la ambigüedad en los
12 miembros de interfaz que tienen la misma firma. Sin ellas, una clase o estructura no podría contener
13 diferentes implementaciones de los miembros de interfaz de idéntica firma y tipo de valor devuelto;
14 tampoco podría contener ninguna implementación de los miembros de interfaz de igual firma pero distinto
15 tipo de valor devuelto.
16Para que una implementación explícita de miembros de interfaz sea válida, la clase o estructura debe dar nombre
17a una interfaz en su clase base que contenga un miembro cuyo nombre completo, tipo y tipos de parámetro
18coincidan exactamente con los de la implementación explícita de miembros de interfaz. Así, en la clase siguiente

19 class Shape: ICloneable

20 {

21
22 int IComparable.CompareTo(object other) {...} // invalid

23 }
24la declaración de I Comparab le .Compare Toproduce un error en tiempo de compilación, porque I Comparab le
25no aparece en la lista de clases base de Shape y no es una interfaz base de ICloneable. Igualmente, en las
26declaraciones

27 class Shape: ICloneable

28 {

29 object ICloneable.Clone() {...}


31 class Ellipse: Shape
30
32 {

33 object ICloneable.Clone() {...} // invalid


35la declaración de ICloneable.Clone en Ellipse supone un error en tiempo de compilación, ya que ICloneable
36
34no aparece expresamente citada en la lista de clases base de Ellipse.
37El nombre completo de un miembro de interfaz debe hacer referencia a la interfaz donde se declara. Así, en las
38declaraciones

39 interface IControl

40 {

41 void Paint();
43 interface ITextBox: IControl
42
44 {

45 void SetText(string text);

46
679Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 327
680Especificación del lenguaje C#

1 class TextBox: ITextBox

2 {

34 void ITextBox.SetText(string text) {...}

56la implementación
} explícita de miembro de interfaz de Pa in tdebe escribirse como I Con t ro l . Pa .in t

713.4.2 Asignación de interfaces


8Una clase o estructura debe proporcionar implementaciones de todos los miembros de las interfaces enumeradas
9en su lista de clases base. El proceso de localizar implementaciones de miembros de interfaz en una clase o
10estructura de implementación se conoce como asignación de interfaz.
11La asignación de interfaz para una clase o estructura C localiza una implementación para cada uno de los
12miembros de todas las interfaces especificadas en la lista de clases base de C. La implementación de un
13miembro de interfaz I.M concreto, donde I es la interfaz donde se declara el miembro M, se determina al
14examinar todas las clases o estructuras S, comenzando desde C y repitiendo el paso para cada clase base
15sucesiva de C, hasta encontrar una coincidencia:
16• Si S contiene una declaración de una implementación explícita de miembros de interfaz que coincide con I y
17 M, este miembro es la implementación de I.M.
18• Por otro lado, si S contiene una declaración de un miembro público no estático que coincide con M, este
19 miembro es la implementación de I.M.
20Se produce un error en tiempo de compilación si no es posible localizar implementaciones de todos los
21miembros de cada interfaz especificada en la lista de clases base de C. Observe que los miembros de una
22interfaz incluyen aquellos miembros heredados de sus interfaces base.
23A efectos de la asignación de interfaz, un miembro de clase A coincide con un miembro de interfaz B cuando:
24• A y B son métodos, y el nombre, tipo y listas de parámetros formales de A y B son idénticos.

25• A y B son propiedades, el nombre y tipo de A y B son idénticos y A posee los mismos descriptores de acceso
26 que B (se permite que A tenga descriptores de acceso adicionales si no es una implementación explícita de
27 miembros de interfaz).
28• A y B son eventos, y el nombre y tipo de A y B son idénticos.

29• A y B son indizadores, el tipo y listas de parámetros formales de A y B son idénticos y A posee los mismos
30 descriptores de acceso que B (se permite que A tenga descriptores de acceso adicionales si no es una
31 implementación explícita de miembros de interfaz).
32Algunas de las principales implicaciones del algoritmo de asignación de interfaz son:
33• Las implementaciones explícitas de miembros de interfaz tienen prioridad sobre los demás miembros de una
34 misma clase o estructura a la hora de determinar el miembro de clase o estructura que implementa a un
35 miembro de interfaz.
36• Ni los miembros no públicos ni los no estáticos participan en la asignación de interfaz.
37En el siguiente ejemplo:

38 interface ICloneable

39 {

40 object Clone();

41
681328 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
682 Capítulo 18 Código no seguro

1 class C: ICloneable

2 {

34 public object Clone() {...}

56el miembro
} I C l oneab le .C lonede C se convierte en la implementación de Clone en ICloneable, porque las
7implementaciones explícitas de miembros de interfaz tienen prioridad sobre los demás miembros.
8Si una clase o estructura implementa dos o más interfaces que contienen un miembro con el mismo nombre, tipo
9y tipos de parámetros, es posible asignar cada uno de los miembros de interfaz a un único miembro de clase o
10estructura. Por ejemplo:

11 interface IControl

12 {

13 void Paint();
15 interface IForm
14
16 {

17 void Paint();
19 class Page: IControl, IForm
18
20 {

21 public void Paint() {...}


23Aquí, los métodos Paint de IControl e IForm se asignan al método Paint en Page. Naturalmente, también es
24
22posible trabajar con diferentes implementaciones explícitas de miembros de interfaz para los dos métodos.
25Si una clase o estructura implementa una interfaz que contiene miembros ocultos, necesariamente deberán
26implementarse algunos miembros por medio de la implementación explícita de miembros de interfaz. Por
27ejemplo:

28 interface IBase

29 {

30 int P { get; }
32 interface IDerived: IBase
31
33 {

34 new int P();


36Una implementación de esta interfaz requeriría al menos una implementación explícita de miembros de interfaz,
37y tomaría una de las siguientes formas:
35
38 class C: IDerived

39 {

40
41 int IDerived.P() {...}

42 }
43 class C: IDerived

44 {

45
46 int IDerived.P() {...}

47 }

683Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 329


684Especificación del lenguaje C#

1 class C: IDerived

2 {

34 public int P() {...}

5 }

685330 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


686 Capítulo 18 Código no seguro

1Cuando una clase implementa varias interfaces con la misma interfaz base, sólo puede haber una
2implementación de la interfaz base. En el siguiente ejemplo:

3 interface IControl

4 {

5 void Paint();
7 interface ITextBox: IControl
6
8 {

9 void SetText(string text);


11 interface IListBox: IControl
10
12 {

13 void SetItems(string[] items);


15 class ComboBox: IControl, ITextBox, IListBox
14
16 {

17
18 void ITextBox.SetText(string text) {...}
19 void IListBox.SetItems(string[] items) {...}

20 }
21no es posible que haya implementaciones independientes para el miembro de interfaz I Con t ro que
l figura en la
22lista de clases base, el miembro I Con t ro heredado
l por ITextBox y el miembro IControl heredado por IListBox.
23De hecho, no existe una identidad separada para estas interfaces. En realidad, las implementaciones de ITextBox
24e IListBox comparten la misma implementación de IControl, y se considera que ComboBox únicamente
25implementa tres interfaces: IControl, ITextBox e IListBox.
26Los miembros de una clase base participan en la asignación de interfaz. En el siguiente ejemplo:

27 interface Interface1

28 {

29 void F();
31 class Class1
30
32 {

33
34 public void G() {}

35 }
36 class Class2: Class1, Interface1

37 {

38 new public void G() {}


40el método F de Class1 se utiliza en la implementación de Interface1 en Class2.
39
4113.4.3 Herencia de implementación de interfaces
42Una clase hereda todas las implementaciones de interfaz proporcionadas por sus clases base.
43Sin reimplementar explícitamente una interfaz, una clase derivada no puede alterar de ningún modo las
44asignaciones de interfaz que hereda de sus clases base. Por ejemplo, en las declaraciones

687Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 331


688Especificación del lenguaje C#

1 interface IControl

2 {

3 void Paint();
5 class Control: IControl
4
6 {

7 public void Paint() {...}


9 class TextBox: Control
8
10 {

11 new public void Paint() {...}


13el método Pa in tde Tex tBoxoculta el método Paint de Control, pero no altera la asignación de Control.Paint a
14IControl.Paint, y las llamadas a Paint a través de instancias de clase y de interfaz tendrán los efectos siguientes:
12
15 Control c = new Control();

16 TextBox t = new TextBox();

17 IControl ic = c;

18 IControl it = t;

19 c.Paint(); // invokes Control.Paint();


23Sin embargo, cuando se asigna un método de interfaz a un método virtual de una clase, es posible que las clases
20
24derivadas reemplacen el método virtual y alteren la implementación de la interfaz. Por ejemplo, al rescribir las
25
21declaraciones anteriores de esta manera
22
26 interface IControl

27 {

28 void Paint();
30 class Control: IControl
29
31 {

32 public virtual void Paint() {...}


34 class TextBox: Control
33
35 {

36 public override void Paint() {...}


38se observarán los siguientes efectos
37
39 Control c = new Control();

40 TextBox t = new TextBox();

41 IControl ic = c;

42 IControl it = t;

43 c.Paint(); // invokes Control.Paint();


47Dado que las implementaciones explícitas de miembros de interfaz no pueden declararse como virtuales, no es
44
48posible reemplazarlas. Sin embargo, es perfectamente válido que una implementación explícita de miembros de
45
46
689332 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
690 Capítulo 18 Código no seguro

1interfaz llame a otro método, y se puede declarar dicho método como virtual para permitir que las clases
2derivadas lo reemplacen. Por ejemplo:

3 interface IControl

4 {

5 void Paint();
7 class Control: IControl
6
8 {

109 protected virtual void PaintControl() {...}

11 }
12 class TextBox: Control

13 {

14 protected override void PaintControl() {...}


16Aquí, las clases derivadas de Cont ro lpueden especializar la implementación de I Con t ro l . Pa in
reemplazando
t
17el método PaintControl.
15

1813.4.4 Reimplementación de interfaces


19Una clase que hereda una implementación de interfaz puede reimplementar la interfaz incluyéndola en la lista
20de clases base.
21Una reimplementación de una interfaz sigue exactamente las mismas reglas de asignación de interfaz que la
22implementación inicial de una interfaz. Así, la asignación de interfaz heredada no tiene efecto alguno en la
23asignación de interfaz especificada para la reimplementación de la interfaz. Por ejemplo, en las declaraciones

24 interface IControl

25 {

26 void Paint();
28 class Control: IControl
27
29 {

30 void IControl.Paint() {...}


32 class MyControl: Control, IControl
31
33 {

34 public void Paint() {}


36el hecho de que Control asigne IControl.Paint a Control.IControl.Paint no afecta a la reimplementación en
37MyControl, que asigna IControl.Paint a MyControl.Paint.
35
38Las declaraciones heredadas de miembros públicos y las implementaciones explícitas heredadas de miembros de
39interfaz participan en el proceso de asignación de interfaz para las interfaces implementadas. Por ejemplo:

40 interface IMethods

41 {

42 void F();

43 void G();

44 void H();

45
691Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 333
46
692Especificación del lenguaje C#

1 class Base: IMethods

2 {

3 void IMethods.F() {}

4 void IMethods.G() {}

5 public void H() {}


8 class Derived: Base, IMethods
6
9 {
7
10 public void F() {}
13
11Aquí, la implementación de IMe thods en Der ivedasigna los métodos de interfaz a Derived.F,
14Base.IMethods.G, Derived.IMethods.H y Base.I.
12
15Cuando una clase implementa una interfaz, también implementa implícitamente todas las interfaces base de
16dicha interfaz. De igual forma, una reimplementación de una interfaz también es implícitamente una
17reimplementación de todas sus interfaces base. Por ejemplo:

18 interface IBase

19 {

20 void F();
22 interface IDerived: IBase
21
23 {

24 void G();
26 class C: IDerived
25
27 {

28
29 void IDerived.G() {...}

30 }
31 class D: C, IDerived

32 {

33
34 public void G() {...}

35
36Aquí, la }reimplementación de IDerived también reimplementa IBase, asignando IBase.F a D.F.

3713.4.5 Interfaces y clases abstractas


38Al igual que una clase no abstracta, una clase abstracta debe proporcionar implementaciones de todos los
39miembros de las interfaces que figuran en su lista de clases base. Sin embargo, una clase abstracta puede asignar
40métodos de interfaz a métodos abstractos. Por ejemplo:

41 interface IMethods

42 {

43 void F();

44
45

693334 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


694 Capítulo 18 Código no seguro

1 abstract class C: IMethods

2 {

3 public abstract void F();


64Aquí, la implementación de IMe thods asigna F y G a métodos abstractos, que pueden reemplazarse en clases no
7abstractas que se deriven de C.
5
8Debe observarse que las implementaciones explícitas de miembros de interfaz no pueden ser abstractas, pero sí
9pueden llamar a métodos abstractos. Por ejemplo:

10 interface IMethods

11 {

12 void F();

13
15 abstract class C: IMethods
14
16 {

17
18 void IMethods.G() { GG(); }
19 protected abstract void FF();
20 protected abstract void GG();

21Aquí, las} clases no abstractas que se derivan de C deberían reemplazar FF y GG, proporcionando de esa forma la
22
23propia implementación de IMethods.

695Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 335


696Especificación del lenguaje C#

1 14.Enumeraciones

2Un tipo enum es un tipo de valor distintivo (§4.1) que declara un conjunto de constantes con nombre.
3En el ejemplo

4 enum Color

5 {

6 Red,

7 Green,
10declara un tipo enum denominado Co lo rcon los miembros Red , Green y Blue.
8
11914.1 Declaraciones de enumeración
12Una declaración de enumeración declara un nuevo tipo enum. Una declaración de enumeración comienza con la
13palabra clave enum y define su nombre, su tipo de acceso, su tipo subyacente y sus miembros.
14 enum-declaration:
15 attributesopt enum-modifiersopt enum identifier enum-baseopt enum-body ;opt
16 enum-base:
17 : integral-type
18 enum-body:
19 { enum-member-declarationsopt }
20 { enum-member-declarations , }
21Cada tipo enum tiene un tipo integral correspondiente denominado tipo subyacente del tipo de enumeración.
22Este tipo subyacente debe poder representar todos los valores de enumerador definidos en la enumeración. Una
23declaración de enumeración puede declarar explícitamente un tipo subyacente byte, sbyte, short, ushort, int,
24uint, long o ulong. Observe que no se puede utilizar char como tipo subyacente. Una declaración de
25enumeración que no declara explícitamente un tipo subyacente tiene int como tipo subyacente.
26En el ejemplo

27 enum Color: long

28 {

29 Red,

30 Green,
33declara una enumeración con el tipo subyacente long. Un programador podría utilizar un tipo subyacente long,
31como en el ejemplo, para habilitar el uso de valores que estén en el intervalo de long, pero no en el de int, o
34
35
32para preservar esta opción para el futuro.

697336 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


698 Capítulo 18 Código no seguro

114.2 Modificadores de enumeración


2Una declaración de enumeración (enum-declaration) puede incluir opcionalmente una secuencia de
3modificadores de enumeración:
4 enum-modifiers:
5 enum-modifier
6 enum-modifiers enum-modifier
7 enum-modifier:
8 new
9 pub l i c
10 pro tec ted
11 i n te rna l
12 private
13Cuando el mismo modificador aparece varias veces en una declaración de enumeración, se produce un error en
14tiempo de compilación.
15Los modificadores de una declaración de enumeración tienen el mismo significado que los de las declaraciones
16de clases (§10.1.1). Observe, sin embargo, que los modificadores abstract y sealed no están permitidos en una
17declaración de enumeración. Las enumeraciones no pueden ser abstractas y no permiten derivación.

1814.3 Miembros de enumeración


19El cuerpo de una declaración de tipo enum define cero o varios miembros de enumeración, que son las
20constantes con nombre del tipo enum. No puede haber dos miembros de enumeración con el mismo nombre.
21 enum-member-declarations:
22 enum-member-declaration
23 enum-member-declarations , enum-member-declaration
24 enum-member-declaration:
25 attributesopt identifier
26 attributesopt identifier = constant-expression
27Cada miembro de enumeración tiene un valor asociado constante. El tipo de este valor es el tipo subyacente de
28la enumeración contenedora. El valor constante de cada miembro de enumeración debe estar comprendido en el
29intervalo del tipo subyacente de la enumeración. En el ejemplo

30 enum Color: uint

31 {

32 Red = -1,

33 Green = -2,
36se produce un error en tiempo de compilación, porque los valores constantes -1, -2 y –3 no están en el intervalo
34
37del tipo integral subyacente uint.
35Varios miembros de enumeración pueden compartir el mismo valor asociado. En el ejemplo
38

39 enum Color

40 {

41 Red,

42
43
699Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 337
700Especificación del lenguaje C#

1 Max = Blue

23muestra }una enumeración en la que dos de sus miembros (B lue y Max ) tienen el mismo valor asociado.
4El valor asociado de un miembro de enumeración se asigna bien implícita o explícitamente. Si la declaración del
5miembro de enumeración tiene un inicializador de expresión constante (constant-expression), el valor de dicha
6expresión, convertido de manera implícita al tipo subyacente de la enumeración, es el valor asociado del
7miembro de enumeración. Si la declaración del miembro de enumeración no tiene inicializador, su valor
8asociado se establece de forma implícita, de la siguiente forma:
9• Si el miembro de enumeración es el primero de los declarados en el tipo enum, su valor asociado es cero.
10• En cualquier otro caso, el valor asociado del miembro de enumeración se obtiene al aumentar en uno el
11 valor asociado del miembro de enumeración precedente en el código. Este valor aumentado debe estar
12 incluido en el intervalo de valores que puede representar el tipo subyacente, en caso contrario, se produce un
13 error de compilación.
14En el ejemplo
15 us ing Sys tem;
16 enum Co lo r
17 {
18 Red ,
19 Green = 10 ,
20 B lue
22
21 c lass Tes t
23 {
24 s ta t i c vo id Main ( ) {
25 Conso le .Wr i teL ine (S t r i ngFromCo lo r (Co lo r .Red) ) ;
26 Conso le .Wr i teL ine (S t r i ngFromCo lo r (Co lo r .G reen) ) ;
27 Conso le .Wr i teL ine (S t r i ngFromCo lo r (Co lo r .B lue ) ) ;
29
28 s ta t i c s t r i ng St r i ngFromCo lo r (Co lo r c ) {
30 sw i t ch ( c ) {
31 case Co lo r .Red :
32 re tu rn St r i ng . Fo rmat ( "Red = {0}" , ( i n t ) c ) ;
33 case Co lo r .G reen :
34 re tu rn St r i ng . Fo rmat ( "Green = {0}" , ( i n t ) c ) ;
35 case Co lo r .B lue :
36 re tu rn St r i ng . Fo rmat ( "B lue = {0}" , ( i n t ) c ) ;
37 de fau l t :
38 re tu rn " I nva l i d co lo r " ;
39 }
40 }
41se imprimen los nombres de los miembros de enumeración y sus valores asociados. El resultado es:
42
43 Red = 0
44 Green = 10
45 B lue = 11
46por las siguientes razones:

701338 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


702 Capítulo 18 Código no seguro

1• al miembro de enumeración Red se le asigna automáticamente el valor cero (puesto que no tiene
2 inicializador y es el primer miembro de enumeración);
3• al miembro de enumeración Green se le asigna explícitamente el valor 10 ;
4• y al miembro de enumeración B lue se le asigna automáticamente el valor del miembro que le precede en el
5 código más uno.
6El valor asociado de un miembro de enumeración no puede, directa ni indirectamente, usar el valor de su propio
7miembro de enumeración asociado. Aparte de esta restricción de circularidad, los inicializadores de miembros
8de enumeración pueden hacer referencia con libertad a otros inicializadores de miembros de enumeración, sin
9importar su posición en el código. Dentro de un inicializador de miembro de enumeración, los valores de los
10demás miembros de enumeración siempre se tratan como si tuvieran el tipo de su tipo subyacente, de manera
11que no son necesarias las conversiones de tipos al hacer referencia a otros miembros de enumeración.
12En el ejemplo

13 enum Circular

14 {

15 A = B,
18
16se produce un error en tiempo de compilación, porque las declaraciones de A y B son circulares. Es decir, que A
19depende de B explícitamente y B depende de A implícitamente.
17
20Los miembros de enumeración se denominan y se establece su ámbito de manera exactamente análoga a los
21campos pertenecientes a clases. El ámbito de un miembro de enumeración es el cuerpo de su tipo enum
22contenedor. Dentro de ese ámbito, se puede hacer referencia a los miembros de enumeración por su nombre
23simple. Desde cualquier otra ubicación del código, debe calificarse el nombre de un miembro de enumeración
24con el nombre de su tipo enum. Los miembros de enumeración no tienen ninguna accesibilidad declarada: un
25miembro de enumeración es accesible si el tipo enum que lo contiene es accesible.

2614.4 Tipo System.Enum


27El tipo System.Enum es la clase base abstracta de todos los tipos enum (es distinta y diferente del tipo
28subyacente del tipo enum), y los miembros heredados de System.Enum están disponibles en cualquier tipo
29enum. Existe una conversión boxing (§4.3.1) de cada tipo enum a System.Enum y también existe una
30conversión unboxing (§4.3.2) de System.Enum a cualquier tipo enum.
31Observe que Sys tem.Enum no es en sí mismo un tipo enum (enum-type). Al contrario, es un tipo de clase
32(class-type) del que se derivan todos los tipos enum (enum-types). El tipo Sys tem.Enum hereda del tipo
33System.ValueType (§4.1.1), el cual, a su vez, hereda del tipo object. En tiempo de ejecución, un valor de tipo
34System.Enum puede ser null o una referencia a un valor boxed de cualquier tipo enum.

3514.5 Valores y operaciones de enumeración


36Cada tipo enum define un tipo distintivo; es necesaria una conversión explícita de enumeración (§6.2.2) para
37convertir un tipo enum a un tipo integral, o para convertir dos tipos enum. El conjunto de valores que puede
38tomar un tipo enum no está limitado por sus miembros de enumeración. En concreto, cualquier valor del tipo
39subyacente de una enumeración puede convertirse al tipo enum, y es un valor aceptado y distintivo de dicho tipo
40enum.
41Los miembros de enumeración tienen el tipo de su tipo enum contenedor (salvo dentro de otros inicializadores
42de miembros de enumeración: vea la sección §14.3). El valor de un miembro de enumeración declarado en el
43tipo enum E con un valor v asociado es (E)v.

703Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 339


704Especificación del lenguaje C#

1Los operadores siguientes se pueden utilizar en los valores de tipos de enumeración: == , !=, <, >, <=,
2>= (§7.9.5), binario + (§7.7.4), binario - (§7.7.5), ^, &, | (§7.10.2), ~ (§7.6.4), ++, -- (§7.5.9 y §7.6.5), y
3sizeof (§18.5.4).
4Cada tipo enum automáticamente deriva de la clase Sys tem.Enum (que, a su vez, deriva de
5Sys tem.Va lueTypey object). Así, los métodos heredados y propiedades de esta clase se pueden utilizar en los
6valores de un tipo de enumeración.

705340 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


706 Capítulo 18 Código no seguro

1 15.Delegados

2Los delegados habilitan escenarios de programación que otros lenguajes, como C++, Pascal y Modula, han
3resuelto mediante punteros a función. A diferencia de los punteros a función de C++, los delegados están
4completamente orientados a objetos y, a diferencia de los punteros a funciones miembro de C++, los delegados
5encapsulan a la vez una instancia de objeto y un método.
6Una declaración de delegado define una clase que se deriva de la clase Sys tem.De legate. Una instancia de
7delegado encapsula una lista de llamadas, que consiste en una lista de uno o varios métodos, a cada uno de los
8que se hace referencia como una entidad a la que se puede llamar. Para métodos de instancia, una entidad a la
9que se puede llamar consta de una instancia y un método de la instancia. Para los métodos estáticos, una entidad
10a la que se puede llamar está formada solamente por un método. Invocar a una instancia delegada con un
11conjunto adecuado de argumentos, hace que se invoque a cada una de las entidades del delegado a las que se
12puede llamar, con el conjunto de argumentos dado.
13Una propiedad interesante y útil de una instancia de delegado es que no necesita conocer las clases de los
14métodos a los que encapsula; lo único que importa es que los métodos sean compatibles (§15.1) con el tipo del
15delegado. Esto hace que los delegados sean perfectos para una invocación “anónima”.

1615.1 Declaraciones de delegados


17Una declaración de delegado (delegate-declaration) es una declaración de tipo (type-declaration) (§9.5) que
18declara un nuevo tipo delegado.
19 delegate-declaration:
20 attributesopt delegate-modifiersopt de legate return-type identifier
21 ( formal-parameter-listopt ) ;
22 delegate-modifiers:
23 delegate-modifier
24 delegate-modifiers delegate-modifier
25 delegate-modifier:
26 new
27 public
28 protected
29 internal
30 private
31Cuando el mismo modificador aparece varias veces en una declaración de delegado, se produce un error en
32tiempo de compilación.
33El modificador new sólo está permitido en delegados declarados dentro de otro tipo, en cuyo caso, éste
34especifica que tal delegado oculta un miembro heredado con el mismo nombre, como se describe en la sección
35§10.2.2.
36Los modificadores public, protected, internal y private controlan el acceso del tipo delegado. Dependiendo
37del contexto en el que ocurra la declaración de delegado, algunos de estos modificadores pueden no estar
38permitidos (§3.5.1).
39El nombre del tipo delegado es el identificador (identifier).

707Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 341


708Especificación del lenguaje C#

1La lista opcional de parámetros formales (formal-parameter-list) especifica los parámetros del delegado, y el
2tipo de valor devuelto (return-type) corresponde al delegado. Un método y un tipo delegado son compatibles si
3se cumplen las dos condiciones siguientes:
4• Tienen el mismo número de parámetros, con los mismos tipos, en el mismo orden y los mismos
5 modificadores de parámetro.
6• Sus tipos de valor devuelto son iguales.
7Los tipos delegados en C# son equivalentes en su denominación, no estructuralmente. En concreto, dos tipos
8delegados diferentes con la misma lista de parámetros y el mismo tipo de valor devuelto se consideran
9diferentes. Sin embargo, las instancias de dos tipos delegados distintos pero estructuralmente equivalentes se
10pueden considerar iguales (§7.9.8).
11Por ejemplo:

12 delegate int D1(int i, double d);


13 class A

14 {

15 public static int M1(int a, double b) {...}


17 class B
16
18 {

19
20 public static int M1(int f, double g) {...}
21 public static void M2(int k, double l) {...}
22 public static int M3(int g) {...}
23 public static void M4(int g) {...}

24
25Los tipos} delegados D1 y D2 son compatibles con los métodos A.M1 y B.M1, ya que poseen el mismo tipo de
26valor devuelto y la misma lista de parámetros; sin embargo, son tipos diferentes, por lo cual no son
27intercambiables entre sí. Los tipos delegados D1 y D2 son incompatibles con los métodos B.M2, B.M3 y B.M4,
28ya que poseen diferentes tipos de valor devuelto o diferentes listas de parámetros.
29La única forma de declarar un tipo delegado es a través de una declaración de delegado (delegate-declaration).
30Un tipo delegado es un tipo de clase derivada de Sys tem.De legate. Los tipos delegados son sea ledde manera
31implícita, por lo que no está permitido derivar ningún tipo de un tipo delegado. Tampoco se permite derivar un
32tipo de clase no delegado a partir de System.Delegate. Observe que System.Delegate no es en sí mismo un
33tipo delegado; es un tipo de clase del cual se derivan todos los tipos delegados.
34C# proporciona una sintaxis especial para la creación y llamada de instancias de delegado. Salvo para la
35creación de instancias, cualquier operación que pueda aplicarse a una clase o instancia de clase también puede
36aplicarse a una clase o instancia de delegado, respectivamente. En particular, es posible obtener acceso a los
37miembros del tipo System.Delegate a través de la sintaxis habitual de acceso a miembros.
38El conjunto de métodos encapsulados por una instancia de delegado se denomina lista de llamadas. Cuando se
39crea una instancia de delegado (§15.2) a partir de un único método, encapsula el método y su lista de llamadas
40contiene una sola entrada. Sin embargo, cuando se combinan dos instancias de delegado no null, sus listas de
41llamadas se concatenan, por orden de operando izquierdo a operando derecho, para formar una nueva lista de
42llamadas, que contiene dos o más entradas.

709342 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


710 Capítulo 18 Código no seguro

1Los delegados se combinan utilizando los operadores binarios + (§7.7.4) y += (§7.13.2). Se puede quitar un
2delegado de una combinación de delegados utilizando los operadores binarios - (§7.7.5) y -= (§7.13.2). Los
3delegados se pueden comparar para comprobar si hay igualdad (§7.9.8).
4El ejemplo siguiente muestra la creación de instancias de un número de delegados y sus correspondientes listas
5de llamadas:

6 delegate void D(int x);


7 class C

8 {

109 public static void M2(int i) {...}


11 }
12 class Test

13 {

14 static void Main() {

15 D cd1 = new D(C.M1); // M1

16 D cd2 = new D(C.M2); // M2

17 D cd3 = cd1 + cd2; // M1 + M2


21 }
18Al crear instancias de cd1 y cd2, cada una de ellas encapsula un método. Cuando se crea una instancia de cd3,
22
23
19tiene una lista de llamadas de dos métodos, M1 y M2, por ese orden. La lista de llamadas de cd4 contiene M1,
24M2 y M1, por ese orden. Por último, la lista de llamadas de cd5 contiene M1, M2, M1, M1 y M2, por ese orden.
25
20Para ver más ejemplos de combinación (y eliminación) de delegados, vea la sección §15.3.

2615.2 Creación de instancias de delegados


27Una instancia de un delegado se crea mediante una expresión de creación de delegado (delegate-creation-
28expression) (§7.5.10.3). La instancia de delegado recién creada hace referencia a alguno de los siguientes:
29• El método estático a que se hace referencia en la expresión de creación de delegado (delegate-creation-
30 expression), o bien
31• El objeto de destino (que no puede ser nu l )l y el método de instancia a que se hace referencia en la
32 expresión de creación de delegado (delegate-creation-expression), o bien
33• Otro delegado.
34Por ejemplo:
35 de legate vo id D( in t x ) ;
36 c lass C
37 {
38 pub l i c s ta t i c vo id M1( in t i ) { . . . }
39 pub l i c vo id M2( in t i ) { . . . }
40

711Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 343


712Especificación del lenguaje C#

1 class Test

2 {

3 static void Main() {

4 D cd1 = new D(C.M1); // static method

5 C t = new C();

6 D cd2 = new D(t.M2); // instance method

7
8
9

713344 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


714 Capítulo 18 Código no seguro

1Una vez creadas las instancias de delegado, éstas siempre hacen referencia al mismo objeto de destino y método.
2Recuerde que, cuando se combinan dos delegados, o se quita uno de ellos en el otro, el resultado es un nuevo
3delegado con su propia lista de llamadas; las listas de llamadas de los delegados combinados o eliminados
4permanecen sin cambios.

515.3 Invocación de delegados


6C++ proporciona una sintaxis especial para invocar un delegado. Cuando se invoca una instancia de delegado no
7null cuya lista de llamadas contiene un sola entrada, llama a ese método con los mismos argumentos que recibió,
8y devuelve el mismo valor que el método a que se hace referencia. (Vea la sección §7.5.5.2 para obtener
9información detallada sobre la invocación de delegados.) Si se produce una excepción durante la llamada a un
10delegado y no se captura dentro del método invocado, la búsqueda de una cláusula de excepción catch continúa
11en el método que llamó al delegado, como si ese método hubiera llamado directamente al método al que hacía
12referencia el delegado.
13La invocación de una instancia de delegado cuya lista de llamadas contiene varias entradas continúa llamando a
14cada uno de los métodos de la lista, de forma sincrónica, por orden. A cada uno de los métodos así llamados se
15le pasa el mismo conjunto de argumentos pasados a la instancia de delegado. Si tal invocación de delegado
16incluye parámetros de referencia (§10.5.1.2), cada invocación de método incluirá una referencia a la misma
17variable; los cambios en dicha variable que realice un método de la lista de llamadas serán visibles para todos
18los métodos situados a continuación en la lista. Si la invocación de delegado incluye parámetros de salida o un
19valor devuelto, su valor final vendrá dado por la invocación del último delegado de la lista.
20Si se produce una excepción durante la llamada a dicho delegado y no se captura dentro del método invocado, la
21búsqueda de una cláusula de excepción catch continúa en el método que llamó al delegado, y no se invoca
22ninguno de los métodos situados a continuación en la lista de llamadas.
23Intentar invocar una instancia de delegado cuyo valor es null provoca una excepción del tipo
24Sys tem.Nu l lRe fe renceExcept i.on
25En el siguiente ejemplo se muestra cómo crear instancias, combinar, quitar e invocar delegados:

26 using System;
27 delegate void D(int x);
28 class C

29 {

30 public static void M1(int i) {

31
33 public static void M2(int i) {
32
34 Console.WriteLine("C.M2: " + i);

35
36 public void M3(int i) {

37 Console.WriteLine("C.M3: " + i);

38 }
40 class Test
39
41 {

42 static void Main() {

43
45 D cd2 = new D(C.M2);
44
46 cd2(-2); // call M2

715Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 345


716Especificación del lenguaje C#

1 D cd3 = cd1 + cd2;

2 cd3(10); // call M1 then M2


3 cd3 += cd1;

4 cd3(20); // call M1, M2, then M1


5 C c = new C();

6 D cd4 = new D(c.M3);

7 cd3 += cd4;
9 cd3 -= cd1; // remove last M1
8
10 cd3(40); // call M1, M2, then M3
11 cd3 -= cd4;

12 cd3(50); // call M1 then M2


13 cd3 -= cd2;

14 cd3(60); // call M1
15 cd3 -= cd2; // impossible removal is benign

16 cd3(60); // call M1
17 cd3 -= cd1; // invocation list is empty so cd3 is null
18 // cd3(70); // System.NullReferenceException thrown
19 cd3 -= cd1; // impossible removal is benign

20 }
22
21Tal como se muestra en la instrucción cd3 += cd1;, un delegado puede aparecer varias veces en una lista de
23llamadas. En este caso, se le llama una vez por cada aparición. En una lista de llamadas como esta, cuando se
24quita el delegado, la última aparición en la lista es la que se elimina realmente.
25Inmediatamente antes de la ejecución de la instrucción final, cd3 - = cd1; el delegado cd3 hace referencia a una
26lista de llamadas vacía. Intentar quitar un delegado de una lista vacía (o quitar un delegado no existente de una
27lista que no está vacía) no es ningún error.
28El resultado producido es el siguiente:

29 C.M1: -1

30 C.M2: -2

31 C.M1: 10

32 C.M2: 10

33 C.M1: 20

34 C.M2: 20

35 C.M1: 20

36 C.M1: 30

37 C.M2: 30

38 C.M1: 30

39 C.M3: 30

40
41
717346 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
42
43
718 Capítulo 18 Código no seguro

1 16.Excepciones

2Las excepciones de C# proporcionan una forma estructurada, uniforme y con seguridad de tipos de controlar
3situaciones de error del sistema y de las aplicaciones. El mecanismo de excepciones de C# es bastante similar al
4de C++, pero con unas cuantas diferencias importantes:
5• En C#, todas las excepciones deben estar representadas por una instancia de un tipo de clase derivado de
6 Sys tem.Except i on . En C++, cualquier valor de cualquier tipo puede utilizarse para representar una
7 excepción.
8• En C#, puede utilizarse un bloque finally (§8.10) para crear código de terminación que se ejecute en
9 condiciones normales y excepcionales. Ese tipo de código es difícil de escribir en C++ sin duplicar código.
10• En C#, las excepciones del nivel de sistema, como desbordamiento, división por cero o eliminaciones de
11 referencia nulas, tienen clases de excepción bien definidas y están al mismo nivel que los escenarios de error
12 de aplicaciones.

1316.1 Causas de excepciones


14Una excepción se puede iniciar de dos formas diferentes.
15• Una instrucción th row (§8.9.5) inicia una excepción de manera inmediata e incondicional. El control nunca
16 llega a la instrucción que sigue inmediatamente a th row.
17• Algunas situaciones excepcionales que pueden surgir durante el procesamiento de instrucciones y
18 expresiones de C# provocan una excepción en ciertas circunstancias cuando no es posible completar
19 normalmente una operación. Por ejemplo, una operación de división por cero (§7.7.2) inicia una excepción
20 Sys tem.D iv ideByZeroExcept i on si el denominador es cero. Vea la §16.4 para obtener una lista de las
21 diferentes excepciones que pueden producirse de esta forma.

2216.2 Clase System.Exception


23La clase Sys tem.Except i ones el tipo base de todas las excepciones. Esta clase tiene unas cuantas propiedades
24a resaltar que comparten todas las excepciones:
25• Message es una propiedad de sólo lectura de tipo s t r i ngque contiene una descripción legible por el
26 hombre de la causa que provocó la excepción.
27• es una propiedad de sólo lectura de tipo Except i on. Si su valor no es null, se refiere a la
I nne rExcept i on
28 excepción que causó la excepción actual, es decir, que la excepción actual se inició en un bloque catch que
29 controlaba la excepción InnerException. En cualquier otro caso, su valor es null, lo que indica que esta
30 excepción no fue causada por otra excepción. El número de objetos de excepción encadenados de esta forma
31 puede ser arbitrario.
32El valor de estas propiedades puede especificarse en las llamadas al constructores de instancia de
33System.Exception.

719Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 347


720Especificación del lenguaje C#

116.3 Cómo controlar excepciones


2Las excepciones se controlan mediante una excepción t r y(§8.10).
3Cuando se produce una excepción, el sistema busca la cláusula ca tch más próxima que controla la excepción,
4según lo determina el tipo de la excepción en tiempo de ejecución. En primer lugar, se busca una instrucción t r y
5contenedora en el método actual, y se consideran las cláusulas catch asociadas de la instrucción try según su
6ordenación. Si ese primer paso falla, se busca el método que llamó al método actual en una instrucción try que
7contenga el punto de la llamada al método actual. Esta búsqueda continúa hasta encontrar una cláusula catch
8que puede controlar la excepción actual, denominando una clase de excepción que sea de la misma clase o clase
9base del tipo en tiempo de ejecución de la excepción iniciada. Una cláusula catch que no denomina ninguna
10clase de excepción puede controlar cualquier excepción.
11Una vez hallada una cláusula catch coincidente, el sistema se prepara para transferir el control a la primera
12instrucción de la cláusula catch. Antes de comenzar la ejecución de la cláusula catch, el sistema ejecuta, por
13orden, todas las cláusulas finally asociadas a instrucciones try cuyo nivel de anidamiento sea mayor que el de la
14que capturó la excepción.
15De no encontrarse ninguna cláusula catch coincidente, se produce una de estas dos circunstancias:
16• Si la búsqueda de una cláusula catch coincidente llega hasta un constructor estático (§10.11) o un
17 inicializador de campo estático, se inicia una excepción System.TypeInitializationException en el punto
18 donde se desencadenó la llamada al constructor estático. La excepción interna de
19 System.TypeInitializationException contiene la excepción iniciada en un principio.
20• Si la búsqueda de cláusulas catch coincidentes llega hasta el código que inició el subproceso, termina la
21 ejecución del mismo. El impacto de esta terminación se define según la implementación.
22Las excepciones que se producen durante la ejecución de un destructor merecen especial atención. Si se produce
23una excepción durante la ejecución de un destructor y la excepción no es capturada, se termina la ejecución de
24dicho destructor y se llama al destructor de la clase base (si existe). Si no hay clase base (como ocurre con el
25tipo object) o no hay un destructor de clase base, se descarta la excepción.

2616.4 Clases de excepción comunes


27A continuación se muestran las excepciones iniciadas por algunas operaciones de C#.
28

721348 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


722 Capítulo 18 Código no seguro

System.ArithmeticException Clase base de las excepciones producidas durante


operaciones aritméticas, como
Sys tem.D iv ideByZeroExcept i on y
Sys tem.Over f l owExcept i on.
System.ArrayTypeMismatchException Se inicia cuando una operación de almacenamiento
en una matriz falla porque el tipo real del elemento
almacenado es incompatible con el tipo de la
matriz.
System.DivideByZeroException Se inicia cuando se produce un intento de dividir
un valor integral por cero.
System.IndexOutOfRangeException Se inicia cuando se produce un intento de indizar
una matriz por medio de un índice menor que cero
o situado fuera de los límites de la matriz.
System.InvalidCastException Se inicia cuando una conversión explícita de un
tipo o interfaz base a un tipo derivado da un error
en tiempo de ejecución.
System.NullReferenceException Se inicia cuando se utiliza una referencia nu l lde
manera que hace obligatorio el objeto al que se
hace referencia.
System.OutOfMemoryException Se inicia cuando falla un intento de asignar
memoria a través de new .
System.OverflowException Se inicia cuando una operación aritmética en un
contexto checked produce un desbordamiento.
System.StackOverflowException Se inicia cuando se agota la pila de excepciones
debido a la existencia de demasiadas llamadas de
método pendientes; suele indicar un nivel de
recursividad muy profundo o ilimitado.
System.TypeInitializationException Se inicia cuando un constructor estático inicia una
excepción sin que haya cláusulas ca tch para
capturarla.
29

723Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 349


724Especificación del lenguaje C#

1 17.Atributos

2Gran parte del lenguaje C# permite al programador especificar información declarativa acerca de las entidades
3que se definen en el programa. Por ejemplo, la accesibilidad de un método en una clase se especifica al
4decorarlo con los modificadores de método (method-modifiers) pub l i c, pro tec ted, internal y private.
5C# permite a los programadores inventar nuevas clases de información declarativa, denominadas atributos. Los
6programadores pueden entonces asociar atributos a varias entidades de programa y recuperar la información de
7atributos en un entorno en tiempo de ejecución. Por ejemplo, un marco de trabajo podría definir un atributo
8He lpAt t r i bu te
que se pueda colocar en ciertos elementos del programa, como clases y métodos, para
9proporcionar una asignación o correspondencia entre dichos elementos y su documentación.
10Los atributos se definen mediante la declaración de clases de atributo (§17.1), que pueden contener parámetros
11posicionales y con nombre (§17.1.2). Los atributos se asocian a entidades de un programa de C# mediante las
12especificaciones de atributos (§17.2), y pueden recuperarse en tiempo de ejecución como instancias de atributo
13(§17.3).

1417.1 Clases de atributo


15Una clase de atributo es una clase que se deriva de la clase abstracta Sys tem.At t r i bu ,teya sea directa o
16indirectamente. La declaración de una clase de atributo define un nuevo tipo de atributo que se puede insertar en
17una declaración. Por convención, las clases de atributos se denominan con el sufijo At t r i bu te. Los usos de un
18atributo pueden incluir u omitir este sufijo.

1917.1.1 Uso de los atributos


20El atributo At t r i bu teUsage
(§17.4.1) se utiliza para describir cómo se puede utilizar una clase de atributo.
21At t r i bu teUsageposee un parámetro posicional (§17.1.2) que habilita a una clase de atributo para especificar
22los tipos de declaraciones en que se puede utilizar. En el ejemplo
23 us ing Sys tem;
24 [A t t r i bu teUsage(At t r i bu te Ta rge ts .C lass | At t r i bu te Ta rge ts . I n te r face ) ]
25 pub l i c c lass S imp leAt t r i bu te : At t r i bu te
26 {
27 ...
28
29se define una clase de atributo denominada SimpleAttribute que se puede colocar en declaraciones de clase
30(class-declarations) y en declaraciones de interfaz (interface-declarations). En el ejemplo
31 [S imp le ] c lass C lass1 { . . . }
32 [S imp le ] i n te r face I n te r face1 { . . . }
33muestra varios usos del atributo S imp le. Si bien el atributo se define con el nombre S imp leAt t r i bu ,tecuando
34se utiliza puede omitirse el sufijo Attribute, dando como resultado el nombre corto Simple. Así, el ejemplo
35anterior es semánticamente equivalente al que se muestra a continuación:
36 [S imp leAt t r i bu te ] c lass C lass1 { . . . }
37 [S imp leAt t r i bu te ] i n te r face I n te r face1 { . . . }

725350 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


726 Capítulo 18 Código no seguro

1At t r i bu teUsagetiene un parámetro con nombre (§17.1.2) denominado A l l owMu l t i p ,leque indica si puede
2especificarse el atributo más de una vez para una entidad dada. Si AllowMultiple para una clase de atributo es
3true, se trata de una clase de atributo de uso múltiple y se puede especificar más de una vez en una entidad. Si
4AllowMultiple para una clase de atributo es false o no está especificado, se trata de una clase de atributo de uso
5único y se puede especificar como mucho una vez en una entidad.
6En el ejemplo

7 using System;
8 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]

9 public class AuthorAttribute: Attribute

10 {
12 public AuthorAttribute(string name) {
11
13 this.name = name;

14
15 public string Name {

16 get { return name; }

17 }
19define una clase de atributo de uso múltiple de nombre AuthorAttribute. En el ejemplo
18
20 [Author("Brian Kernighan"), Author("Dennis Ritchie")]

21 class Class1

22 {
25
23muestra una declaración de clase con dos usos del atributo Author.
26
24AttributeUsage tiene otro parámetro con nombre denominado Inherited, que especifica si el atributo, cuando
27se especifica en una clase base, también es heredado por las clases que se derivan de esta clase base. Si
28Inherited para una clase de atributo es true, se hereda dicho atributo. Si Inherited para una clase de atributo es
29false, dicho atributo no se hereda. Si no se especifica, su valor predeterminado es true.
30Una clase de atributo X sin atributo AttributeUsage asociado, como en

31 using System;
32 class X: Attribute {...}
33equivale a lo siguiente:

34 using System;
35 [AttributeUsage(

36 AttributeTargets.All,

37 AllowMultiple = false,

38 Inherited = true)
4117.1.2 Parámetros posicionales y con nombre
39
42Las clases de atributo pueden tener parámetros posicionales y parámetros con nombre. Cada constructor de
40
43instancia público de una clase de atributo define una secuencia válida de parámetros posicionales para esa clase

727Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 351


728Especificación del lenguaje C#

1de atributo. Cada campo o propiedad de lectura y escritura pública y no estática de una clase de atributo define
2un parámetro con nombre para la clase de atributo.

729352 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


730 Capítulo 18 Código no seguro

1En el ejemplo

2 using System;
3 [AttributeUsage(AttributeTargets.Class)]

4 public class HelpAttribute: Attribute

5 {

6 public HelpAttribute(string url) { // Positional parameter


97 public string Topic { // Named parameter

108 get {...}

11 set {...}
13 public string Url {
12
14 get {...}

15 }
17se define una clase de atributo denominada He lpAt t r i bu te que posee un parámetro posicional, ur ly un
18
16 parámetro con nombre, Topic . Si bien es no estática y pública, la propiedad Url no define un parámetro con
19nombre porque no es de lectura y escritura.
20Esta clase de atributo podría utilizarse de la manera siguiente:

21 [Help("http://www.mycompany.com/.../Class1.htm")]

22 class Class1

23 {

24
26 [Help("http://www.mycompany.com/.../Misc.htm", Topic = "Class2")]
25
27 class Class2

28 {

29
3117.1.3 Tipos de parámetros de atributos
30Los tipos de los parámetros posicionales y con nombre de una clase de atributo se limitan a los tipos de
32
33parámetros de atributos, que son:
34• Uno de los tipos siguientes: boo l, byte, char, double, float, int, long, sbyte, short, string, uint, ulong,
35 ushort.
36• El tipo object.
37• El tipo System.Type.
38• Un tipo enum, con la condición de que éste y los tipos en los que esté anidado (si los hubiera) tengan acceso
39 público (§17.2).
40• Matrices unidimensionales de los tipos anteriores.

4117.2 Especificación de atributos


42La especificación de atributos es la aplicación de un atributo previamente definido a una declaración. Un
43atributo es una parte de información declarativa adicional que se especifica en una declaración. Los atributos

731Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 353


732Especificación del lenguaje C#

1pueden especificarse en un ámbito global (para especificar atributos en el ensamblado o módulo que los
2contienen) y para declaraciones de tipo (type-declarations) (§9.5), declaraciones de miembros de clase (class-
3member-declarations) (§10.2), declaraciones de miembros de interfaz (interface-member-declarations) (§13.2),
4declaraciones de miembros de estructura (struct-member-declarations) (§11.2), declaraciones de miembros de
5enumeración (enum-member-declarations) (§14.3), declaraciones de descriptores de acceso (accessor-
6declarations) (§10.6.2), declaraciones de descriptores de acceso a eventos (event-accessor-declarations)
7(§10.7.1) y listas de parámetros formales (formal-parameter-lists) (§10.5.1).
8Los atributos se especifican en secciones de atributos. Una sección de atributos se compone de un par de
9corchetes, que encierran una lista separada por comas de uno o varios atributos. Ni el orden de los atributos en la
10lista, ni el de las secciones adjuntas a la misma entidad de programa son significativos. Por ejemplo, las
11especificaciones de atributos [A ] [B,][B ] [A,][A, B] y [B, A] son equivalentes.
12 global-attributes:
13 global-attribute-sections
14 global-attribute-sections:
15 global-attribute-section
16 global-attribute-sections global-attribute-section
17 global-attribute-section:
18 [ global-attribute-target-specifier attribute-list ]
19 [ global-attribute-target-specifier attribute-list , ]
20 global-attribute-target-specifier:
21 global-attribute-target :
22 global-attribute-target:
23 assembly
24 module
25 attributes:
26 attribute-sections
27 attribute-sections:
28 attribute-section
29 attribute-sections attribute-section
30 attribute-section:
31 [ attribute-target-specifieropt attribute-list ]
32 [ attribute-target-specifieropt attribute-list , ]
33 attribute-target-specifier:
34 attribute-target :
35 attribute-target:
36 field
37 event
38 method
39 param
40 property
41 return
42 type
43 attribute-list:
44 attribute
45 attribute-list , attribute

733354 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


734 Capítulo 18 Código no seguro

1 attribute:
2 attribute-name attribute-argumentsopt
3 attribute-name:
4 type-name
5 attribute-arguments:
6 ( positional-argument-listopt )
7 ( positional-argument-list , named-argument-list )
8 ( named-argument-list )
9 positional-argument-list:
10 positional-argument
11 positional-argument-list , positional-argument
12 positional-argument:
13 attribute-argument-expression
14 named-argument-list:
15 named-argument
16 named-argument-list , named-argument
17 named-argument:
18 identifier = attribute-argument-expression
19 attribute-argument-expression:
20 expression
21Un atributo se compone de un nombre de atributo (attribute-name) y una lista opcional de argumentos
22posicionales y con nombre. Los argumentos posicionales (si los hay) preceden a los argumentos con nombre. Un
23argumento posicional se compone de una expresión de argumentos de atributo (attribute-argument-expression).
24Un argumento con nombre consta de un nombre seguido por el signo igual, seguido de una expresión de
25argumentos de atributo (attribute-argument-expression) que, en conjunto, están restringidos por las mismas
26reglas que la asignación simple. El orden de los argumentos con nombre no es significativo.
27El nombre de atributo (attribute-name) identifica una clase de atributo. Si la forma del nombre de atributo
28(attribute-name) es un nombre de tipo (type-name), este nombre debe hacer referencia a una clase de atributo.
29En caso contrario, se producirá un error en tiempo de compilación. En el ejemplo
30 c lass C lass1 {}
31 [C lass1 ] c lass C lass2 {} / / Er ro r
32se produce un error en tiempo de compilación, porque se intenta utilizar Class1 como clase de atributo a pesar
33de no serlo.
34Algunos contextos permiten especificar un atributo en más de un destino. Un programa puede especificar
35explícitamente el destino incluyendo un especificador de destino de atributo (attribute-target-specifier). Cuando
36se coloca un atributo en el nivel global, es necesario un especificador de destino de atributo global (global-
37attribute-target-specifier). En todas las demás ubicaciones, se aplica un valor predeterminado razonable, pero es
38posible utilizar un especificador de destino de atributo (attribute-target-specifier) para afirmar o reemplazar el
39valor predeterminado en ciertos casos ambiguos (o sólo para afirmar el valor predeterminado, en casos en que
40no haya ambigüedad). De esta forma, pueden omitirse habitualmente los especificadores de destino de atributo
41(attribute-target-specifiers). Los contextos potencialmente ambiguos se resuelven de esta forma:
42• Un atributo especificado en el ámbito global se puede aplicar tanto al ensamblado de destino como al
43 módulo de destino. No existe un valor predeterminado para este contexto, por lo que siempre se requiere un
44 especificador de destino de atributo (attribute-target-specifier). La presencia del especificador de destino de
45 atributo (attribute-target-specifier) a continuación de assemb ly indica que el atributo se aplica al

735Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 355


736Especificación del lenguaje C#

1 ensamblado de destino; la presencia del mismo especificador a continuación de modu le indica que el
2 atributo se aplica al módulo de destino.

737356 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


738 Capítulo 18 Código no seguro

1• Un atributo especificado en una declaración de delegado se puede aplicar al delegado que se está declarando
2 o a su valor devuelto. En ausencia del especificador de destino de atributo (attribute-target-specifier), el
3 atributo se aplica al delegado. La presencia del especificador de destino de atributo (attribute-target-
4 specifier) a continuación de t ype indica que el atributo se aplica al delegado; la presencia del mismo
5 especificador a continuación de re tu rnindica que el atributo se aplica al valor devuelto.
6• Un atributo especificado en una declaración de método se puede aplicar al método que se está declarando o
7 a su valor devuelto. En ausencia del especificador de destino de atributo (attribute-target-specifier), el
8 atributo se aplica al método. La presencia del especificador de destino de atributo (attribute-target-specifier)
9 a continuación de method indica que el atributo se aplica al método; la presencia del mismo especificador a
10 continuación de re tu rnindica que el atributo se aplica al valor devuelto.
11• Un atributo especificado en una declaración de operador se puede aplicar al operador que se está declarando
12 o a su valor devuelto. En ausencia del especificador de destino de atributo (attribute-target-specifier), el
13 atributo se aplica al operador. La presencia del especificador de destino de atributo (attribute-target-
14 specifier) a continuación de method indica que el atributo se aplica al operador; la presencia del mismo
15 especificador a continuación de re tu rnindica que el atributo se aplica al valor devuelto.
16• Un atributo especificado en una declaración de evento que omite los descriptores de acceso de eventos se
17 puede aplicar al evento que se está declarando, al campo asociado (si el evento no es abstracto) o a los
18 métodos add y remove. En ausencia del especificador de destino de atributo (attribute-target-specifier), el
19 atributo se aplica al evento. La presencia del especificador de destino de atributo (attribute-target-specifier)
20 a continuación de event indica que el atributo se aplica al evento; la presencia del especificador a
21 continuación de f i e l dindica que el atributo se aplica al campo; y la presencia del mismo especificador a
22 continuación de method indica que el atributo se aplica a los métodos.
23• Un atributo especificado en una declaración de descriptor de acceso get para una propiedad o una
24 declaración de indizador se puede aplicar al método asociado o a su valor devuelto. En ausencia del
25 especificador de destino de atributo (attribute-target-specifier), el atributo se aplica al método. La presencia
26 del especificador de destino de atributo (attribute-target-specifier) a continuación de method indica que el
27 atributo se aplica al método; la presencia del mismo especificador a continuación de return indica que el
28 atributo se aplica al valor devuelto.
29• Un atributo especificado en una declaración de descriptor de acceso set para una propiedad o una
30 declaración de indizador se puede aplicar al método asociado o a su parámetro implícito solo. En ausencia
31 del especificador de destino de atributo (attribute-target-specifier), el atributo se aplica al método. La
32 presencia del especificador de destino de atributo (attribute-target-specifier) method indica que el atributo
33 se aplica al método; la presencia del especificador de destino de atributo param indica que el atributo se
34 aplica al parámetro; y la presencia del especificador de destino de atributo return indica que el atributo se
35 aplica al valor devuelto.
36• Un atributo especificado en una declaración de descriptor de acceso add o remove para una declaración de
37 evento se aplica al método asociado o a su parámetro. En ausencia del especificador de destino de atributo
38 (attribute-target-specifier), el atributo se aplica al método. La presencia del especificador de destino de
39 atributo (attribute-target-specifier) method indica que el atributo se aplica al método; la presencia del
40 especificador de destino de atributo param indica que el atributo se aplica al parámetro; y la presencia del
41 especificador de destino de atributo return indica que el atributo se aplica al valor devuelto.
42En otros contextos se permite la inclusión de un especificador de destino de atributo (attribute-target-specifier),
43pero no es necesaria. Por ejemplo, una declaración de clase puede incluir u omitir el especificador type:
44 [ t ype : Author ( "B r i an Kern ighan" ) ]
45 c lass C lass1 {}

739Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 357


740Especificación del lenguaje C#

1 [Author("Dennis Ritchie")]

23Supone un
class
error Class2 {}un especificador de destino de atributo (attribute-target-specifier) no válido. Por
especificar
4ejemplo, el especificador param no se puede utilizar en una declaración de clase:
5 [pa ram: Author ( "B r i an Kern ighan" ) ] / / Er ro r
6 c lass C lass1 {}
7Por convención, las clases de atributos se denominan con el sufijo At t r i bu te
. Un nombre de atributo (attribute-
8name) de la forma de nombre de tipo (type-name) puede incluir u omitir este sufijo. Si se encuentra una clase de
9atributo tanto con este sufijo como sin él, existe una ambigüedad y se produce un error de tiempo de
10compilación. Si el nombre de atributo (attribute-name) se escribe de manera que su identificador (identifier)
11situado más a la derecha es un identificador literal (§2.4.2), sólo se corresponderá con un atributo sin sufijo, lo
12que supondrá una ambigüedad que se debe resolver. En el ejemplo
13 us ing Sys tem;
14 [A t t r i bu teUsage(At t r i bu te Ta rge ts .A l l ) ]
15 pub l i c c lass X: At t r i bu te
16 {}
17 [A t t r i bu teUsage(At t r i bu te Ta rge ts .A l l ) ]
18 pub l i c c lass XAt t r i bu te : At t r i bu te
19 {}
20 [X ] / / Er ro r : amb igu i ty
21 c lass C lass1 {}
22 [XAt t r i bu te ] / / Re fe rs to XAt t r i bu te
23 c lass C lass2 {}
24 [@X] / / Re fe rs to X
25 c lass C lass3 {}
26 [@XAt t r i bu te ] / / Re fe rs to XAt t r i bu te
27 c lass C lass4 {}
28se muestran dos clases de atributos denominadas X y XAt t r i bu te . El atributo [X] es ambiguo, puesto que podría
29referirse tanto a X como a XAttribute. El uso de un identificador literal permite especificar la intención exacta
30en estos casos poco frecuentes. El atributo [XAttribute] no es ambiguo (aunque lo sería si hubiera una clase de
31atributo denominada XAttributeAttribute). Si se quita la declaración de la clase X, ambos atributos hacen
32referencia a la clase de atributo Xattribute, de la manera siguiente:
33 us ing Sys tem;
34 [A t t r i bu teUsage(At t r i bu te Ta rge ts .A l l ) ]
35 pub l i c c lass XAt t r i bu te : At t r i bu te
36 {}
37 [X ] / / Re fe rs to XAt t r i bu te
38 c lass C lass1 {}
39 [XAt t r i bu te ] / / Re fe rs to XAt t r i bu te
40 c lass C lass2 {}
41 [@X] / / Er ro r : no at t r i bu te named "X"
42 c lass C lass3 {}
43Supone un error en tiempo de compilación utilizar un atributo de uso único más de una vez en la misma entidad.
44En el ejemplo
45 us ing Sys tem;

741358 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


742 Capítulo 18 Código no seguro

1 [AttributeUsage(AttributeTargets.Class)]

2 public class HelpStringAttribute: Attribute

3 {
5 public HelpStringAttribute(string value) {
4
6 this.value = value;

78 public string Value {

9 get {...}

10 }
12 [HelpString("Description of Class1")]
11
13 [HelpString("Another description of Class1")]
15
14se produce un error en tiempo de compilación, porque intenta utilizar He lpS t r i ng
, una clase de atributo de uso
16único, más de una vez en la declaración de C lass1.
17Una expresión E es una expresión de argumentos de atributo (attribute-argument-expression) si todas las
18instrucciones siguientes son verdaderas:
19• El tipo de E es un tipo de parámetro de atributo (§17.1.3).
20• En tiempo de compilación, el valor de E se puede calcular a través de:
21 o Un valor constante.
22 o Un objeto Sys tem. Type.
23 o Una matriz unidimensional de expresiones de argumento de atributo (attribute-argument-expressions).
24Por ejemplo:
25 us ing Sys tem;
26 [A t t r i bu teUsage(At t r i bu te Ta rge ts .C lass ) ]
27 pub l i c c lass Tes tAt t r i bu te : At t r i bu te
28 {
29 pub l i c i n t P1 {
30 get { . . . }
31 se t { . . . }
33
32 pub l i c Type P2 {
34 get { . . . }
35 se t { . . . }
36 }
37 pub l i c ob jec t P3 {
38 get { . . . }
39 se t { . . . }
40 }
41
42 [ Tes t (P1 = 1234 , P3 = new i n t [ ] {1 , 3 , 5} , P2 = t ypeo f ( f l oa t ) ) ]
43 c lass MyC lass {}

743Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 359


744Especificación del lenguaje C#

117.3 Instancias de atributo


2Una instancia de atributo es una instancia que representa un atributo en tiempo de ejecución. Un atributo se
3define mediante una clase de atributo, argumentos posicionales y argumentos con nombre. Una instancia de
4atributo es una instancia de la clase de atributo que se inicializa con los argumentos posicionales y con nombre.
5La recuperación de la instancia de atributo comprende el procesamiento en tiempo de compilación y ejecución,
6tal como se describe en las siguientes secciones:

717.3.1 Compilación de un atributo


8La compilación de un atributo (attribute) con clase de atributo T, una lista de argumentos posicionales
9(positional-argument-list) P y una lista de argumentos con nombre (named-argument-list) N se compone de los
10siguientes pasos:
11• Para compilar una expresión de creación de objeto de la forma new T(P), se siguen los pasos del
12 procesamiento en tiempo de compilación Estos pasos pueden producir un error en tiempo de compilación o
13 determinar un constructor de instancia C en T, que se puede invocar en tiempo de ejecución.
14• Si C no tiene acceso público, se produce un error en tiempo de compilación.
15• Para cada argumento con nombre Arg de N:
16 o Name será el identificador (identifier) del argumento con nombre (named-argument) Arg.
17 o Name debe identificar a un campo o propiedad público, de lectura y escritura y no estático en T. Si T no
18 tiene tal campo o propiedad, se producirá un error en tiempo de compilación.
19• Mantenga la siguiente información para la creación de instancias del atributo en tiempo de ejecución: la
20 clase de atributo T, el constructores de instancia C de T, la lista de argumentos posicionales (positional-
21 argument-list) P y la lista de argumentos con nombre (named-argument-list) N.

2217.3.2 Recuperación en tiempo de ejecución de una instancia de atributo


23La compilación de un atributo (attribute) da como resultado una clase de atributo T, un constructor de instancia
24C en T, una lista de argumentos posicionales (positional-argument-list) P y una lista de argumentos con nombre
25(named-argument-list) N. Dada esta información, se puede recuperar una instancia de atributo en tiempo de
26ejecución siguiendo estos pasos:
27• Se siguen los pasos de procesamiento en tiempo de ejecución para ejecutar una expresión de creación de
28 objeto (object-creation-expression) de la forma new T(P), utilizando el constructor de instancia C tal como
29 se determina en tiempo de compilación. Estos pasos dan como resultado una excepción, o producen una
30 instancia O de T.
31• Para cada argumento con nombre (named-argument) Arg de N, con el orden:
32 o Name será el identificador (identifier) del argumento con nombre (named-argument) Arg. Si Name no
33 identifica a un campo o propiedad de lectura y escritura, público y no estático en O, se inicia una
34 excepción.
35 o Value será el resultado de evaluar la expresión de argumentos de atributo (attribute-argument-
36 expression) de Arg.
37 o Si Name identifica a un campo en O, establezca este campo en Value.
38 o En cualquier otro caso, Name identifica a una propiedad en O. Establezca esta propiedad en Value.
39 o El resultado es O, una instancia de la clase de atributo T inicializada con la lista de argumentos
40 posicionales (positional-argument-list) P y la lista de argumentos con nombre (named-argument-list) N.

745360 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


746 Capítulo 18 Código no seguro

117.4 Atributos reservados


2Un pequeño número de atributos afectan al lenguaje de alguna forma. Estos atributos incluyen:
3• Sys tem.At t r i bu teUsageAt t r i bu(§17.4.1),
te que se utiliza para describir las formas en que puede usarse
4 una clase de atributo.

747Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 361


748Especificación del lenguaje C#

1• Sys tem.D iagnos t i c s .Cond i t i ona lA t t r i(§17.4.2),


bu te que se utiliza para definir métodos condicionales.
2• Sys tem.Obso le teAt t r i bu (§17.4.3),
te que se utiliza para marcar un miembro como obsoleto.

317.4.1 Atributo AttributeUsage


4El atributo At t r i bu teUsage
se utiliza para describir la manera en que se puede utilizar la clase de atributo.
5Una clase decorada con el atributo At t r i bu teUsage
debe derivarse de Sys tem.At t r i bu ,tedirecta o
6indirectamente. En caso contrario, se producirá un error en tiempo de compilación.

7 namespace System

8 {

9 [AttributeUsage(AttributeTargets.Class)]

10 public class AttributeUsageAttribute: Attribute


13
11 public virtual bool AllowMultiple { get {...} set {...} }
14 public virtual bool Inherited { get {...} set {...} }
12
15 public virtual AttributeTargets ValidOn { get {...} }

16 }
17 public enum AttributeTargets

18 {

19 Assembly = 0x0001,

20 Module = 0x0002,

21 Class = 0x0004,

22 Struct = 0x0008,

23 Enum = 0x0010,

24 Constructor = 0x0020,

25 Method = 0x0040,

26 Property = 0x0080,
33 All = Assembly | Module | Class | Struct | Enum | Constructor |
27
34 Method | Property | Field | Event | Interface | Parameter |
28
35 Delegate | ReturnValue
29
36
38
3017.4.2 Atributo Conditional
37El atributo Cond i t i onapermite
39 l la definición de métodos condicionales y clases de atributo condicional.
31
40 namespace Sys tem.D iagnos t i c s
32
41 {
42 [A t t r i bu teUsage(At t r i bu te Ta rge ts .Method | At t r i bu te Ta rge ts .C lass ,
43 A l l owMu l t i p le = t rue ) ]
44 pub l i c c lass Cond i t i ona lA t t r i bu te : At t r i bu te
45 {
46
749362 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
750 Capítulo 18 Código no seguro

1 public string ConditionString { get {...} }

2 }

3417.4.2.1 Métodos condicionales


5Un método decorado con el atributo Cond i t i onaes l un método condicional. El atributo Cond i t i onaindica
l una
6condición al comprobar un símbolo de compilación condicional. Las llamadas a un método condicional se
7incluyen u omiten dependiendo de si está definido este símbolo en el punto de la llamada. Si el símbolo se
8define, la llamada se incluye; en caso contrario, la llamada (incluida la evaluación de los parámetros de la
9llamada) se omite.
10Un método condicional está sujeto a las siguientes restricciones:
11• El método condicional debe ser un método perteneciente a una declaración de clase (class-declaration) o de
12 estructura (struct-declaration). Si se especifica el atributo Cond i t i onaen
l un método de declaración de
13 interfaz, se produce un error en tiempo de compilación.
14• El método condicional debe tener un tipo de valor devuelto vo id.
15• El método condicional no debe estar marcado con el modificador ove r r i de. Sin embargo, un método
16 condicional puede marcarse con el modificador v i r tua. lLos reemplazos de dicho método son condicionales
17 de forma implícita, y no se deben marcar explícitamente con un atributo Conditional.
18• El método condicional no debe ser una implementación de un método de interfaz. En caso contrario, se
19 producirá un error en tiempo de compilación.
20Asimismo, se produce un error en tiempo de compilación si se utiliza un método condicional en una expresión
21de creación de delegado (delegate-creation-expression). En el ejemplo
22 #def ine DEBUG
23 us ing Sys tem;
24 us ing Sys tem.D iagnos t i c s ;
25 c lass C lass1
26 {
27 [Cond i t i ona l ( "DEBUG" ) ]
28 pub l i c s ta t i c vo id M() {
29 Conso le .Wr i teL ine ( "Executed C lass1 .M" ) ;
30 }
32
31 c lass C lass2
33 {
34 pub l i c s ta t i c vo id Tes t ( ) {
35 C lass1 .M( ) ;
36 }
38
37se declara Class1.M como método condicional. El método Test de Class2 llama a este método. Dado que se
39define el símbolo de compilación condicional DEBUG, si se llama a Class2.Test éste llamará a M. Si no se
40define el símbolo DEBUG, Class2.Test no llamará a Class1.M.
41Es importante observar que la inclusión o exclusión de una llamada a un método condicional está controlada por
42los símbolos de compilación condicional existentes en el lugar de la llamada. En el siguiente ejemplo:
43 Archivo c lass1 . cs
:
44 us ing Sys tem.D iagnos t i c s ;

751Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 363


752Especificación del lenguaje C#

1 class Class1

2 {

3 [Conditional("DEBUG")]

4 public static void F() {

58 Console.WriteLine("Executed
Archivo c lass2 :
. cs Class1.F");

69 #def ine DEBUG


107 c lass C lass2
11 {
12 pub l i c s ta t i c vo id G( ) {
13 C lass1 .F ( ) ; / / F i s ca l l ed
14 }
16
15 Archivo c lass3 . cs
:
17 #unde f DEBUG
18 c lass C lass3
19 {
20 pub l i c s ta t i c vo id H( ) {
21 C lass1 .F ( ) ; / / F i s not ca l l ed
22 }
24
23 las clases C lass2 y C lass3contienen llamadas al método condicional Class1.F, que será o no condicional, en
25función de si DEBUG está definido o no. Dado que este símbolo está definido en el contexto de Class2 pero no
26de Class3, la llamada a F en Class2 se mantiene, mientras que se omite la llamada a F en Class3.
27El uso de métodos condicionales en una cadena de herencia puede resultar confuso. Las llamadas realizadas a un
28método condicional mediante base, de la forma base.M, están sujetas a las reglas normales de llamadas a
29métodos condicionales. En el siguiente ejemplo:
30 Archivo c lass1 . cs
:
31 us ing Sys tem;
32 us ing Sys tem.D iagnos t i c s ;
33 c lass C lass1
34 {
35 [Cond i t i ona l ( "DEBUG" ) ]
36 pub l i c v i r tua l vo id M() {
37 Conso le .Wr i teL ine ( "C lass1 .M execu ted" ) ;
38 }
40 Archivo c lass2 . cs
:
39
41 us ing Sys tem;

753364 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


754 Capítulo 18 Código no seguro

1 class Class2: Class1

2 {

3 public override void M() {

4 Console.WriteLine("Class2.M executed");

58 base.M();
Archivo c lass3 :
. cs // base.M is not called!

69 #def ine DEBUG


107 us ing Sys tem;
11 c lass C lass3
12 {
13 pub l i c s ta t i c vo id Tes t ( ) {
14 C lass2 c = new C lass2 ( ) ;
15 c .M( ) ; / / M i s ca l l ed
16 }
18C lass2incluye una llamada al método M definido en su clase base. Esta llamada se omite porque el método
17base es condicional debido a la presencia del símbolo DEBUG, que no está definido. Así, el método sólo escribe
19
20en la consola “Class2.M executed”. Un uso juicioso de las declaraciones pp (pp-declarations) puede eliminar
21tales problemas.

2217.4.2.2 Clases de atributo condicional


23Una clase de atributo (§17.1) decorada con uno o más atributos Conditional es una clase de atributo
24condicional. Una clase de atributo condicional se asocia, por lo tanto, con los símbolos de compilación
25condicional declarados en sus atributos Conditional. Este ejemplo:
26 us ing Sys tem;
27 us ing Sys tem.D iagnos t i c s ;
28 [Cond i t i ona l ( "ALPHA" ) ]
29 [Cond i t i ona l ( "BETA" ) ]
30
31declara TestAttribute como una clase de atributo condicional asociado a los símbolos de compilaciones
32condicionales ALPHA y BETA.
33Las especificaciones de atributos (§17.2) de un atributo condicional se incluyen si uno o más de sus símbolos de
34compilación condicional asociado se define en el lugar de la especificación; de lo contrario, se omite la
35especificación de atributos.
36Es importante observar que la inclusión o exclusión de la especificación de un atributo de una clase de atributo
37condicional está controlada por los símbolos de compilación condicional existentes en el lugar de la
38especificación. En el siguiente ejemplo:
39 Archivo test.cs:
40 us ing Sys tem;
41 us ing Sys tem.D iagnos t i c s ;
42 [Cond i t i ona l ( “DEBUG” ) ]
43 pub l i c c lass Tes tAt t r i bu te : At t r i bu te {}
44 Archivo class1.cs:

755Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 365


756Especificación del lenguaje C#

1 #define DEBUG
2 [Test] // TestAttribute is specified
3 class Class1 {}
4 Archivo c lass2 . cs
:
5 #unde f DEBUG
6 [ Tes t ] / / Tes tAt t r i bu te i s not spec i f i ed
7 c lass C lass2 {}
8las clases C lass1y C lass2están decoradas con el atributo Test, que es condicional en función de si DEBUG
9está o no definido. Dado que este símbolo está definido en el contexto de Class1 pero no de Class2, se incluye
10la especificación del atributo Test de Class1, mientras que se omite la especificación del atributo Test en
11Class2.
12

757366 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


758 Capítulo 18 Código no seguro

117.4.3 Atributo Obsolete


2El atributo Obso le tese utiliza para marcar tipos y miembros de tipos que ya no se deberían utilizar.

3 namespace System

4 {

5 [AttributeUsage(

6 AttributeTargets.Class |

7 AttributeTargets.Struct |

8 AttributeTargets.Enum |

9 AttributeTargets.Interface |

10 AttributeTargets.Delegate |

11 AttributeTargets.Method |

12 AttributeTargets.Constructor |

13 AttributeTargets.Property |
21 public ObsoleteAttribute(string message) {...}
14
22 public ObsoleteAttribute(string message, bool error) {...}
15
23 public string Message { get {...} }
16
24 public bool IsError { get {...} }
17
25 }
18Si un programa utiliza un tipo o miembro decorado con el atributo Obso le te, el compilador emite una
27
26
28advertencia o genera un error. En concreto, si no se proporciona ningún parámetro de error, o si se proporciona y
19
29su valor es f a l se, el compilador emite una advertencia. Si se especifica un parámetro de error y su valor es true,
30
20el compilador genera un error.
31En el siguiente ejemplo:

32 [Obsolete("This class is obsolete; use class B instead")]

33 class A

34 {

35
37 class B
36
38 {

39 public void F() {}


41 class Test
40
42 {

43 static void Main() {

44 A a = new A(); // Warning

45 a.F();

46
47Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
759 367
760Especificación del lenguaje C#

1la clase A está decorada con el atributo Obso le te. Cada uso de A en Main da como resultado una advertencia
2que incluye el mensaje especificado en el código anterior, “This class is obsolete; use class B instead.”

317.5 Atributos para interoperabilidad


4Note: Esta sección sólo se aplica a la implementación de C# de Microsoft .NET.

517.5.1 Interoperabilidad con componentes COM y Win32


6El motor de tiempo de ejecución de .NET proporciona un gran número de atributos que permiten que los
7programas de C# interactúen con componentes escritos mediante bibliotecas .dll COM y Win32. Por ejemplo, el
8atributo DllImport puede utilizarse en un método static extern para indicar que la implementación del método
9se encuentra en una biblioteca DLL Win32. Estos atributos se encuentran en el espacio de nombres
10System.Runtime.InteropServices; se puede encontrar información detallada referente a estos atributos en la
11documentación del motor de tiempo de ejecución .NET.

1217.5.2 Interoperabilidad con otros lenguajes .NET

1317.5.2.1 Atributo IndexerName


14Los indizadores se implementan en .NET utilizando propiedades indizadas y tienen un nombre en los metadatos
15.NET. Si no hay ningún atributo IndexerName presente para un indizador, se utiliza de forma predeterminada
16el nombre Item. El atributo IndexerName permite al programador reemplazar este valor predeterminado y
17especificar un nombre distinto.

18 namespace System.Runtime.CompilerServices.CSharp

19 {

20 [AttributeUsage(AttributeTargets.Property)]

21 public class IndexerNameAttribute: Attribute


24
22 public string Value { get {...} }

25
23 }

26

761368 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


762 Capítulo 18 Código no seguro

1 18.Código no seguro

2El lenguaje C# básico, tal como se define en los capítulos precedentes, difiere notablemente de C y C++ en su
3omisión de los punteros como un tipo de datos. C# proporciona en su lugar referencias, así como la capacidad de
4crear objetos administrados por un recolector de elementos no utilizados. Este diseño, unido a otras
5características, hace de C# un lenguaje mucho más seguro que C o C++. En el lenguaje básico de C# no es
6posible usar una variable no inicializada, un puntero “pendiente” o una expresión que indice una matriz más allá
7de sus límites. De esta manera se eliminan categorías enteras de errores que suelen afectar a los programas
8desarrollados en C y C++.
9Si bien casi todas las construcciones de tipos de puntero en C o C++ poseen un tipo de referencia equivalente en
10C#, hay situaciones donde el acceso a tipos de puntero se convierte en una necesidad. Por ejemplo, al desear
11interactuar con el sistema operativo subyacente, obtener acceso a un dispositivo asignado a memoria o
12implementar un algoritmo sensible al tiempo puede no lograrse nuestro objetivo sin disponer de acceso a
13punteros. Para solucionar esta necesidad, C# proporciona la capacidad de crear código no seguro.
14En el código no seguro es posible declarar y operar con punteros, realizar conversiones de punteros a tipos
15integrales, tomar las direcciones de variables, etc. En cierto sentido, escribir código no seguro se parece mucho a
16escribir código C dentro de un programa en C#.
17El código no seguro es, en realidad, una característica “segura” desde el punto de vista tanto de programadores
18como de usuarios. El código no seguro debe estar marcado con claridad mediante el modificador unsa fe, de
19manera que los programadores no puedan usar características no seguras por accidente, y el motor de ejecución
20garantice que no se pueda ejecutar el código no seguro en un entorno que no sea de confianza.

2118.1 Contextos no seguros


22Las características no seguras de C# están disponibles sólo en contextos no seguros (unsafe contexts). Se
23introduce un contexto no seguro incluyendo un modificador unsa fe en la declaración de un tipo o miembro, o
24utilizando una instrucción unsafe (unsafe-statement).
25• Una declaración de una clase, estructura, interfaz o delegado puede incluir un modificador unsa fe, en cuyo
26 caso toda la extensión textual de la declaración (incluido el cuerpo de la clase, estructura o interfaz) se
27 considera como contexto no seguro.
28• Una declaración de un campo, método, propiedad, evento, indizador, operador, constructor de instancia,
29 constructor estático o destructor puede incluir el modificador unsa fe, en cuyo caso toda la extensión textual
30 de dicha declaración de miembro se considera como contexto no seguro.
31• Una instrucción unsafe (unsafe-statement) habilita el uso de un contexto no seguro dentro de un bloque
32 (block). Toda la extensión textual del bloque (block) asociado se considera como contexto no seguro.
33Las extensiones de gramática asociadas se muestran a continuación. Para mayor brevedad, se utilizan puntos
34suspensivos (...) para representar elementos que aparecen explicados en capítulos anteriores.
35 class-modifier:
36 ...
37 unsa fe

763Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 369


764Especificación del lenguaje C#

1 struct-modifier:
2 ...
3 unsa fe
4 interface-modifier:
5 ...
6 unsa fe
7 delegate-modifier:
8 ...
9 unsa fe
10 field-modifier:
11 ...
12 unsa fe
13 method-modifier:
14 ...
15 unsa fe
16 property-modifier:
17 ...
18 unsa fe
19 event-modifier:
20 ...
21 unsa fe
22 indexer-modifier:
23 ...
24 unsa fe
25 operator-modifier:
26 ...
27 unsa fe
28 constructor-modifier:
29 ...
30 unsa fe
31 destructor-declaration:
32 attributesopt ex te rnopt unsa feopt ~ identifier ( ) destructor-body
33 attributesopt unsafeopt externopt ~ identifier ( ) destructor-body
34 static-constructor-modifiers:
35 externopt unsafeopt static
36 unsafeopt externopt static
37 externopt static unsafeopt
38 unsafeopt static externopt
39 static externopt unsafeopt
40 static unsafeopt externopt
41 embedded-statement:
42 ...
43 unsafe-statement
44 unsafe-statement:
45 unsafe block

765370 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


766 Capítulo 18 Código no seguro

1En el siguiente ejemplo:

2 public unsafe struct Node

3 {

4 public int Value;

5 public Node* Left;


8el modificador unsa fe especificado en la declaración de estructura hace que toda la extensión textual de la
69declaración sea un contexto no seguro. Así, es posible declarar los campos Le f ty Right como tipo de puntero.
107El ejemplo anterior también se puede escribir de la siguiente forma:

11 public struct Node

12 {

13 public int Value;

14 public unsafe Node* Left;


17Aquí, los modificadores unsafe de las declaraciones de campo convierten a éstas en contextos no seguros.
15
18Aparte de establecer un contexto no seguro, que permite así el uso de tipos de puntero, el modificador unsafe
16no tiene ningún efecto en un tipo o miembro. En el siguiente ejemplo:
19

20 public class A

21 {

22 public unsafe virtual void F() {

23 char* p;

24 ...
27 public class B: A
25
28 {
26
29 public override void F() {

30 base.F();

31el modificador unsa


34 ... fe del método F en A simplemente hace que la extensión textual de F se convierta en un
35contexto no seguro en el cual pueden utilizarse las características no seguras del lenguaje. En el reemplazo de F
32
36en B, no hay necesidad de volver a especificar el modificador unsafe, a menos que el propio método F de B
37requiera el acceso a características no seguras.
33
38Esta situación es ligeramente diferente cuando un tipo de puntero forma parte de la firma del método.
39 pub l i c unsa fe c lass A
40 {
41 pub l i c v i r tua l vo id F (char* p) { . . . }
42 }
43 pub l i c c lass B: A
44 {
45 pub l i c unsa fe ove r r i de vo id F (char* p) { . . . }
46 }

767Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 371


768Especificación del lenguaje C#

1Aquí, debido a que la firma de F incluye un tipo de puntero, sólo se puede escribir en un contexto no seguro. Sin
2embargo, el contexto no seguro se puede introducir convirtiendo toda la clase en no segura, como en el caso de
3A, o al incluir un modificador unsafe en la declaración de método, como en el caso de B.

418.2 Tipos de punteros


5En un contexto no seguro, un tipo (type) (§4) puede ser un tipo de puntero (pointer-type), así como un tipo de
6valor (value-type) o un tipo de referencia (reference-type). Sin embargo, un tipo de puntero (pointer-type)
7también puede utilizarse en una expresión t ypeo f(§7.5.11) fuera de un contexto no seguro ya que dicho uso no
8es no seguro.
9 type:
10 value-type
11 reference-type
12 pointer-type
13Un tipo de puntero (pointer-type) se escribe como tipo no administrado (unmanaged-type) o como la palabra
14clave vo id seguida de un símbolo (token) *:
15 pointer-type:
16 unmanaged-type *
17 void *
18 unmanaged-type:
19 type
20El tipo especificado antes del asterisco en un tipo de puntero se denomina tipo referente del tipo de puntero.
21Representa el tipo de la variable a la que señala un valor del tipo de puntero.
22Al contrario que las referencias (valores de tipos de referencia), los punteros no están sometidos al seguimiento
23del recolector de elementos no utilizados, el cual no conoce los punteros ni los datos a los que estos apuntan. Por
24este motivo, un puntero no puede señalar a una referencia o una estructura que contenga referencias, y el tipo
25referente de un puntero debe ser un tipo no administrado (unmanaged-type).
26Un tipo no administrado (unmanaged-type) es cualquier tipo que no sea un tipo de referencia (reference-type) y
27no contenga campos de tipo de referencia en ningún nivel de anidamiento. En otras palabras, un tipo no
28administrado (unmanaged-type) es uno de los siguientes:
29• sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal o bool.

30• Cualquier tipo enum (enum-type).


31• Cualquier tipo de puntero (pointer-type).
32• Cualquier tipo de estructura (struct-type) definido por el usuario que contenga únicamente campos de tipos
33 no administrados (unmanaged-types).
34La regla intuitiva para mezclar punteros y referencias es que los referentes de las referencias (objetos) pueden
35contener punteros, pero los referentes de punteros no pueden contener referencias.
36En la tabla siguiente se dan algunos ejemplos de tipos de punteros:
37

769372 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


770 Capítulo 18 Código no seguro

Ejemplo Descripción
byte* Puntero a byte
char* Puntero a char
int** Puntero a un puntero a i n t
int*[] Matriz unidimensional de punteros a i n t
void* Puntero a un tipo desconocido
38
39Para una implementación dada, todos los tipos de puntero deben tener el mismo tamaño y representación.
40A diferencia de C y C++, cuando se declaran múltiples punteros en una misma declaración, en C# el carácter *
41se escribe junto al tipo subyacente únicamente, no como puntuación de prefijo en cada nombre de puntero. Por
42ejemplo:
43 int* pi , pj ; / / NOT as i n t *p i , *p j ;
44El valor de un puntero de tipo T* representa la dirección (address) de una variable de tipo T. El operador de
45direccionamiento indirecto de puntero * (§18.5.1) puede usarse para obtener acceso a esta variable. Por ejemplo
46dada una variable P de tipo int*, la expresión *P denota la variable int hallada en la dirección contenida en P.
47Al igual que una referencia de objeto, un puntero puede ser null. Aplicar el operador de direccionamiento
48indirecto a un puntero null da como resultado un comportamiento definido según la implementación. Un
49puntero con el valor null se representa por medio de todos los bits cero.
50El tipo void* representa un puntero a un tipo desconocido. Dado que el tipo referente es desconocido, el
51operador de direccionamiento indirecto no se puede aplicar a un puntero de tipo void*, ni se pueden realizar
52operaciones aritméticas en este tipo de puntero. Sin embargo, un puntero de tipo void* se puede convertir a
53cualquier otro tipo de puntero (y viceversa).
54Los tipos de puntero forman una categoría independiente de tipos. A diferencia de los tipos de referencia y los
55tipos de valor, los tipos de puntero no se derivan de object y no existen conversiones entre tipos de puntero y
56object. En particular, las operaciones boxing y unboxing (§4.3) no se permiten en punteros. Sin embargo, se
57permiten las conversiones entre diferentes tipos de punteros y entre tipos de punteros y tipos integrales. Este
58proceso se describe en §18.4.
59Puede utilizarse un tipo de puntero (pointer-type) como tipo de un campo volátil (§10.4.3).
60Si bien pueden pasarse punteros como parámetros ref u out, hacerlo puede causar un comportamiento
61indefinido, ya que un puntero puede señalar a una variable local que ya no existe cuando el método llamado
62devuelve un valor, o el objeto fijo al que apuntaba ya no es fijo. Por ejemplo:
63 us ing Sys tem;
64 c lass Tes t
65 {
66 s ta t i c i n t va lue = 20 ;
67 unsa fe s ta t i c vo id F (ou t i n t * p i1 , re f i n t * p i2 ) {
68 i n t i = 10 ;
69 p i1 = &i ;
70 f i xed ( i n t * p j = &va lue ) {
71 // ...
72 p i2 = p j ;
73 }
74 }

771Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 373


772Especificación del lenguaje C#

1 static void Main() {

2 int i = 10;

3 unsafe {

46 F(out px1, ref px2);


57 Console.WriteLine("*px1 = {0}, *px2 = {1}",

8 *px1, *px2); // undefined behavior

9 }
12
10Un método puede devolver un valor de un tipo, y ese tipo puede ser un puntero. Por ejemplo, dado un puntero a
13una secuencia contigua de valores i n t, el número de elementos de la secuencia y otro valor i n t, el método
11
14siguiente devuelve la dirección del valor indicado en la secuencia, si se encuentra una coincidencia; en cualquier
15otro caso devuelve null:

16 unsafe static int* Find(int* pi, int size, int value) {

17 for (int i = 0; i < size; ++i) {

18 if (*pi == value)

19 return pi;

20 ++pi;
24En un contexto no seguro, hay varias construcciones disponibles para operar con punteros:
21
25• Puede utilizarse el operador * para realizar direccionamiento indirecto de punteros (§18.5.1).
22
26• Puede utilizarse el operador -> para obtener acceso a un miembro de una estructura a través de un puntero
23
27 (§18.5.2).
28• Puede utilizarse el operador [] para indizar un puntero (§18.5.3).
29• Puede utilizarse el operador & para obtener la dirección de una variable (§18.5.4).
30• Pueden utilizarse los operadores ++ y -- para incrementar y disminuir punteros (§18.5.5).
31• Se pueden utilizar los operadores + y - para realizar aritmética de punteros (§18.5.6).
32• Pueden utilizarse los operadores ==, !=, <, >, <= y => para comparar punteros (§18.5.7).
33• El operador stackalloc puede utilizarse para asignar memoria de la pila de llamadas (§18.7).
34• Puede utilizarse la instrucción fixed para fijar temporalmente una variable y obtener su dirección (§18.6).

3518.3 Variables fijas y móviles


36El operador de dirección (§18.5.4) y la instrucción fixed (§18.6) dividen las variables en dos categorías:
37variables fijas y variables móviles.
38Las variables fijas se encuentran en ubicaciones de almacenamiento que no se ven afectadas por el
39funcionamiento del recolector de elementos no utilizados. Los ejemplos de variables fijas incluyen variables
40locales, parámetros de valor y variables creados mediante punteros de eliminación de referencias. Las variables
41móviles, por otro lado, se encuentran en ubicaciones de almacenamiento sujetas a la reubicación o eliminación
42por parte del recolector de elementos no utilizados. Los ejemplos de variables móviles incluyen campos de
43objetos y elementos de matrices.

773374 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


774 Capítulo 18 Código no seguro

1El operador & (§18.5.4) permite obtener la dirección de una variable fija sin restricciones. Sin embargo, debido
2a que una variable móvil está sujeta a la reubicación o eliminación por el recolector de elementos no utilizados,
3sólo se puede obtener su dirección mediante una instrucción f i xed(§18.6), y la dirección es válida sólo durante
4el ciclo de duración de esa instrucción fixed.
5En términos precisos, una variable fija es una de las siguientes:
6• Una variable resultante de un nombre simple (simple-name) (§7.5.2) que hace referencia a una variable local
7 o un parámetro de valor.
8• Una variable resultante de un acceso a miembro (member-access) (§7.5.4) de la forma V. ,I donde V es una
9 variable fija de tipo de estructura (struct-type).
10• Una variable resultante de una expresión de direccionamiento indirecto de puntero (pointer-indirection-
11 expression) (§18.5.1) de la forma *P, un acceso a miembro de puntero (pointer-member-access) (§18.5.2) de
12 la forma P - >Io un acceso a elemento de puntero (pointer-element-access) (§18.5.3) de la forma P[E].
13Todas las demás variables se clasifican como variables móviles.
14Observe que un campo estático se clasifica también como variable móvil. Observe, igualmente, que un
15parámetro ref o out se clasifica como variable móvil, aun cuando el argumento dado para ese parámetro sea una
16variable fija. Por último, debe señalarse que una variable producida mediante la eliminación de referencia a un
17puntero siempre se clasifica como una variable fija.

1818.4 Conversiones de puntero


19En un contexto no seguro, el conjunto de conversiones implícitas disponibles (§6.1) se extiende hasta incluir las
20siguientes conversiones implícitas de puntero:
21• De cualquier tipo de puntero (pointer-type) al tipo void*.
22• Del tipo null a cualquier tipo de puntero (pointer-type).
23En un contexto no seguro, el conjunto de conversiones explícitas disponibles (§6.2) se extiende hasta incluir las
24siguientes conversiones explícitas de puntero:
25• De cualquier tipo de puntero (pointer-type) a cualquier otro tipo de puntero.
26• De sbyte, byte, short, ushort, int, uint, long o ulong a cualquier tipo de puntero (pointer-type).
27• De cualquier tipo de puntero (pointer-type) a sbyte, byte, short, ushort, int, uint, long o ulong.
28Por último, en un contexto no seguro, el conjunto de conversiones implícitas estándar (§6.3.1) incluye la
29siguiente conversión de puntero:
30• De cualquier tipo de puntero (pointer-type) al tipo void*.
31Las conversiones entre dos tipos de puntero nunca afectan al valor real del puntero. En otras palabras, una
32conversión de un tipo de puntero a otro no tiene efecto en la dirección subyacente dada por el puntero.
33Cuando se convierte un tipo de puntero a otro, si el puntero resultante no está correctamente alineado para el
34tipo a que se apunta, el comportamiento será indefinido si se elimina la referencia al resultado. En general, el
35concepto “correctamente alineado” es transitivo: si un puntero a un tipo A está correctamente alineado para un
36puntero a un tipo B que, a su vez, está correctamente alineado para un puntero a un tipo C, entonces un puntero a
37un tipo A está correctamente alineado para un puntero a un tipo C.
38Considérese el siguiente caso en el que se obtiene acceso a una variable de un tipo determinado mediante un
39puntero a otro tipo:

775Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 375


776Especificación del lenguaje C#

1 char c = 'A';

2 char* pc = &c;

3 void* pv = pc;

4 int* pi = (int*)pv;
7Cuando se convierte un tipo de puntero a un puntero a byte, el resultado apunta al byte de dirección inferior de
58la variable. Los sucesivos incrementos del resultado, hasta alcanzar el tamaño de la variable, dan como resultado
96punteros a los bytes restantes de dicha variable. Por ejemplo, el método siguiente muestra cada uno de los ocho
10bytes de un tipo double como un valor hexadecimal:

777376 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


778 Capítulo 18 Código no seguro

1 using System;
2 class Test

3 {

4 unsafe static void Main() {

5 double d = 123.456e23;

6 unsafe {

7 byte* pb = (byte*)&d;

8 for (int i = 0; i < sizeof(double); ++i)


149Por supuesto, los resultados producidos dependen del carácter big-endian o little endian.
15
10Las asignaciones entre punteros y enteros se definen según su implementación. Sin embargo, en arquitecturas de
16CPU de 32 y 64 bits, con un espacio de direcciones lineal, las conversiones de punteros a o desde tipos
11
17integrales tienen lugar exactamente igual que las conversiones de valores u in to u long, respectivamente, a o
18
12desde dichos tipos integrales.
13
1918.5 Punteros en expresiones
20En un contexto no seguro, una expresión puede dar como resultado un tipo de puntero, pero fuera de ese
21contexto supone un error en tiempo de compilación que una expresión sea del tipo de puntero. En términos más
22precisos, fuera de un contexto no seguro se produce un error en tiempo de compilación si cualquier nombre
23simple (simple-name) (§7.5.2), acceso a miembro (member-access) (§7.5.4), expresión de invocación
24(invocation-expression) (§7.5.5) o acceso a elemento (element-access) (§7.5.6) es de un tipo de puntero.
25En un contexto no seguro, una expresión primaria sin creación de matriz (primary-no-array-creation-
26expression) (§7.5) y una expresión unaria (unary-expression) (§7.6) permiten las siguientes construcciones
27adicionales:
28 primary-no-array-creation-expression:
29 ...
30 pointer-member-access
31 pointer-element-access
32 sizeof-expression
33 unary-expression:
34 ...
35 pointer-indirection-expression
36 addressof-expression
37Estas construcciones se describen en las siguientes secciones. La precedencia y asociatividad de los operadores
38no seguros está implicada en la gramática del lenguaje.

3918.5.1 Direccionamiento indirecto de punteros


40Una expresión de direccionamiento indirecto de punteros (pointer-indirection-expression) se compone de un
41asterisco (*) seguido de una expresión unaria (unary-expression).
42 pointer-indirection-expression:
43 * unary-expression
44El operador unario * indica el direccionamiento indirecto de punteros (pointer indirection) y se utiliza para
45obtener la variable a la que apunta un puntero. El resultado de evaluar *P, donde P es una expresión de un tipo

779Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 377


780Especificación del lenguaje C#

1de puntero T*, es una variable de tipo T. Se produce un error en tiempo de compilación al aplicar el operador
2unario * a una expresión de tipo void* o a una expresión que no sea de tipo de puntero.
3El efecto de aplicar el operador unario * a un puntero null se define por su implementación. En particular, no
4hay garantía de que esta operación inicie una excepción System.NullReferenceException.
5Si se asignó un valor no válido al puntero, el comportamiento del operador unario * es indefinido. Entre los
6valores no válidos para eliminar la referencia a un puntero desde un operador unario * se encuentran una
7dirección incorrectamente alineada para el tipo al que se apunta (vea el ejemplo de la §18.4) y la dirección de
8una variable después de finalizar su vida útil.
9A los efectos del análisis definitivo de asignaciones, una variable producida al evaluar una expresión de la forma
10*P se considera inicialmente asignada (§5.3.1).

1118.5.2 Acceso a miembros de puntero


12Un acceso a miembros de puntero (pointer-member-access) se compone de una expresión primaria (primary-
13expression), seguida de un símbolo “- >”, que a su vez viene seguido de un identificador (identifier).
14 pointer-member-access:
15 primary-expression -> identifier
16En un acceso a miembro de puntero de la forma P->I, P debe ser una expresión de un tipo de puntero diferente
17de void*, e I debe denotar un miembro accesible del tipo al que apunta P.
18Un acceso a miembro de puntero de la forma P->I se evalúa exactamente como (*P).I. Para obtener una
19descripción del operador de direccionamiento indirecto de puntero (*), vea la sección §18.5.1. Para obtener una
20descripción del operador de acceso a miembros (.), vea la sección §7.5.4.
21En el siguiente ejemplo:
22 us ing Sys tem;
23 s t ruc t Po in t
24 {
25 pub l i c i n t x ;
26 pub l i c i n t y ;
27 pub l i c ove r r i de s t r i ng ToS t r i ng ( ) {
28 re tu rn " ( " + x + " , " + y + " ) " ;
29 }
30 }
31 c lass Tes t
32 {
33 s ta t i c vo id Main ( ) {
34 Po in t po in t ;
35 unsa fe {
36 Po in t * p = &po in t ;
37 p ->x = 10 ;
38 p ->y = 20 ;
39 Conso le .Wr i teL ine (p - >ToSt r i ng ( ) ) ;
40
43el operador -> se} utiliza para obtener acceso a campos e invocar un método de una estructura a través de un
41puntero. Dado que la operación P->I es precisamente equivalente a (*P).I, podría haberse escrito igualmente el
44
45
42método Main:

781378 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


782 Capítulo 18 Código no seguro

1 class Test

2 {

3 static void Main() {

4 Point point;

5 unsafe {

6 Point* p = &point;

7 (*p).x = 10;

8
1318.5.3 Acceso a elementos de puntero
149Un acceso a elementos de puntero (pointer-element-access) se compone de una expresión primaria sin creación
15
10de matriz seguida de una expresión (primary-no-array-creation-expression) delimitada por “[” y “]”.
16 pointer-element-access:
11
17 primary-no-array-creation-expression [ expression ]
12
18En un acceso a elemento de puntero de la forma P[E ], P debe ser una expresión de un tipo de puntero diferente
19de void*, y E debe ser una expresión de un tipo que pueda convertirse implícitamente a int, uint, long o ulong.
20Un acceso a elemento de puntero de la forma P[E] se evalúa exactamente como *(P + E). Para obtener una
21descripción del operador de direccionamiento indirecto de puntero (*), vea la sección §18.5.1. Para obtener una
22descripción del operador de suma de punteros (+), vea la sección §18.5.6.
23En el siguiente ejemplo:
24 c lass Tes t
25 {
26 s ta t i c vo id Main ( ) {
27 unsa fe {
28 char* p = s tacka l l oc char [256] ;
29 f o r ( i n t i = 0 ; i < 256 ; i++) p[ i ] = ( cha r ) i ;
30 }
31se utiliza un acceso a elemento de puntero para inicializar el búfer de caracteres de un bucle for. Dado que la
33
34
32operación P[E] es precisamente equivalente a *(P + E), el ejemplo podría haberse escrito igualmente de esta
35forma:
36 c lass Tes t
37 {
38 s ta t i c vo id Main ( ) {
39 unsa fe {
40 char* p = s tacka l l oc char [256] ;
41 f o r ( i n t i = 0 ; i < 256 ; i++) * (p + i ) = ( cha r ) i ;
42 }
43El operador de acceso a elementos de puntero no comprueba errores de tipo "fuera de límite", por otro lado, el
45
46comportamiento al obtener acceso a un elemento fuera de límites no está definido. Ocurre lo mismo en C y C++.
44

783Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 379


784Especificación del lenguaje C#

118.5.4 Operador de dirección


2Una expresión de dirección (addressof-expression) se compone de un símbolo de Y comercial (&) seguido de
3una expresión unaria (unary-expression).
4 addressof-expression:
5 & unary-expression
6Dada una expresión E que es del tipo T y se clasifica como una variable fija (§18.3), la construcción &E calcula
7la dirección de la variable proporcionada por E. El tipo del resultado es T* y se clasifica como un valor. Se
8produce un error en tiempo de compilación si no se clasifica E como una variable, si se clasifica como una
9variable local de sólo lectura o si denota una variable móvil. En este último caso, se puede usar una instrucción
10fija (§18.6) para “fijar” temporalmente la variable antes de obtener su dirección. Como se afirmaba en la sección
11§7.5.4, fuera de un constructor de instancia o constructor estático de una clase o estructura que define un campo
12readonly, dicho campo se considera un valor, no una variable. Como tal, no se puede capturar su dirección.
13Igualmente, no se puede tomar la dirección de una constante.
14El operador & no requiere que esté asignado su argumento definitivamente, pero después de una operación &, la
15variable a la que se aplica el operador se considera definitivamente asignada en la ruta de ejecución donde tiene
16lugar la operación. Es responsabilidad del programador asegurarse de que se produce una correcta inicialización
17de la variable en esta situación.
18En el siguiente ejemplo:
19 us ing Sys tem;
20 c lass Tes t
21 {
22 s ta t i c vo id Main ( ) {
23 int i ;
24 unsa fe {
25 i n t * p = &i ;
26 *p = 123 ;
27 }
28 Conso le .Wr i teL ine ( i ) ;
31i se considera definitivamente asignada después de la operación &i utilizada para inicializar p. La asignación a
29
32*p tiene como resultado la inicialización de i, pero la inclusión de esta inicialización corresponde al
30programador, y no habrá ningún error en tiempo de compilación si se quita la asignación.
33
34Las reglas de asignación definitiva para el operador & permiten evitar tal inicialización redundante de variables
35locales. Por ejemplo, muchas interfaces API externas toman un puntero a una estructura que rellena la API. Las
36llamadas a esas API suelen pasar la dirección de una variable de estructura local, y sin esta regla, se requeriría
37una inicialización redundante de la variable.

3818.5.5 Incremento y decremento de punteros


39En un contexto no seguro, los operadores ++ y -- (§7.5.9 y §7.6.5) se pueden aplicar a las variables de todos los
40tipos de puntero excepto void*. Así, por cada tipo de puntero T*, se definen de manera implícita los siguientes
41operadores:
42 T* opera to r ++(T* x ) ;
43 T* opera to r - - (T* x ) ;

785380 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


786 Capítulo 18 Código no seguro

1Los operadores generan los mismos resultados que x+1 y x - 1, respectivamente (§18.5.6). En otras palabras,
2para una variable de puntero de tipo T*, el operador ++ agrega sizeof(T) a la dirección contenida en la
3variable, y el operador -- resta sizeof(T) de la dirección contenida en la variable.
4Si una operación de incremento o disminución de puntero desborda el dominio del tipo de puntero, se define
5dependiendo de la implementación, pero no se producen excepciones.

618.5.6 Aritmética con punteros


7En un contexto no seguro, los operadores + y - (§7.7.4 y §7.7.5) se pueden aplicar a los valores de todos los
8tipos de puntero excepto void*. Así, por cada tipo de puntero T*, se definen de manera implícita los siguientes
9operadores:

10 T* operator +(T* x, int y);

11 T* operator +(T* x, uint y);

12 T* operator +(T* x, long y);


14 T* operator +(int x, T* y);
13
15 T* operator +(uint x, T* y);

16 T* operator +(long x, T* y);


18 T* operator –(T* x, int y);
17
19 T* operator –(T* x, uint y);

20 T* operator –(T* x, long y);


22 long operator –(T* x, T* y);
21Dada una expresión P de un tipo de puntero T* y una expresión N de tipo int, uint, long o ulong, las
23
24expresiones P + N y N + P calculan el valor de puntero de tipo T* que resulta de agregar N * sizeof(T) a la
25dirección dada por P. De igual forma, la expresión P - N calcula el valor de puntero de tipo T* que resulta de
26restar N * sizeof(T) de la dirección dada por P.
27Dadas dos expresiones, P y Q, de un tipo de puntero T*, la expresión P – Q calcula la diferencia entre las
28direcciones dadas por P y Q y a continuación divide la diferencia por sizeof(T). El tipo del resultado siempre es
29long. En efecto, P - Q se calcula como ((long)(P) - (long)(Q)) / sizeof(T).
30Por ejemplo:

31 using System;
32 class Test

33 {
34 static void Main() {

35 unsafe {

36 int* values = stackalloc int[20];

37 int* p = &values[1];

38 int* q = &values[15];

39 Console.WriteLine("p - q = {0}", p - q);


44
40produce el resultado:
41
45 p - q = -14

42
46 q - p = 14

43Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


787 381
788Especificación del lenguaje C#

1Si una operación de aritmética de punteros desborda el dominio del tipo de puntero, se trunca el resultado
2dependiendo de la implementación, pero no se producen excepciones.

789382 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


790 Capítulo 18 Código no seguro

118.5.7 Comparación de punteros


2En un contexto no seguro, los operadores == , !=, <, >, <= y => (§7.9) pueden aplicarse a valores de todos
3los tipos de puntero. Los operadores de comparación de punteros son:

4 bool operator ==(void* x, void* y);


5 bool operator !=(void* x, void* y);
6 bool operator <(void* x, void* y);
7 bool operator >(void* x, void* y);
8 bool operator <=(void* x, void* y);
9 bool operator >=(void* x, void* y);
10Dado que existe una conversión implícita entre cualquier tipo de puntero y el tipo void*, pueden compararse
11operandos de cualquier tipo de puntero mediante estos operadores. Los operadores de comparación comparan
12las direcciones proporcionadas por los dos operandos como si fueran enteros sin signo.

1318.5.8 Operador Sizeof


14El operador s i zeo fdevuelve el número de bytes ocupados por una variable de un tipo dado. El tipo especificado
15como uno de los operandos de s i zeo fdebe ser de un tipo no administrado (unmanaged-type) (§18.2).
16 sizeof-expression:
17 sizeof ( unmanaged-type )
18El resultado del operador sizeof es un valor de tipo int. Para ciertos tipos predefinidos, el operador sizeof
19produce un valor constante, como se muestra en la tabla siguiente.
20

Expresión Resultado
sizeof(sbyte) 1
sizeof(byte) 1
sizeof(short) 2
sizeof(ushort) 2
sizeof(int) 4
sizeof(uint) 4
sizeof(long) 8
sizeof(ulong) 8
sizeof(char) 2
sizeof(float) 4
sizeof(double) 8
sizeof(bool) 1
21
22Para todos los demás tipos, el resultado del operador sizeof se define según la implementación y se clasifica
23como un valor, no como una constante.
24El orden en que se empaquetan los miembros en una estructura no está especificado.

791Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 383


792Especificación del lenguaje C#

1Para fines de alineación, puede haber un relleno sin nombre al comienzo de una estructura, dentro de una
2estructura y al final de una estructura. El contenido de los bits utilizados para el relleno es indeterminado.
3Al aplicarse a un operando con tipo de estructura, el resultado es el número total de bytes de una variable de ese
4tipo, incluidos los bytes de relleno.

518.6 Instrucción fixed


6En un contexto no seguro, la instrucción incrustada (embedded-statement) (§8) permite una construcción
7adicional, la instrucción f i xed, que se utiliza para “fijar” una variable móvil de manera que su dirección
8permanece constante en toda la duración de la instrucción.
9 embedded-statement:
10 ...
11 fixed-statement
12 fixed-statement:
13 f i xed ( pointer-type fixed-pointer-declarators ) embedded-statement
14 fixed-pointer-declarators:
15 fixed-pointer-declarator
16 fixed-pointer-declarators , fixed-pointer-declarator
17 fixed-pointer-declarator:
18 identifier = fixed-pointer-initializer
19 fixed-pointer-initializer:
20 & variable-reference
21 expression
22Cada declarador de puntero fijo (fixed-pointer-declarator) declara una variable local del tipo de puntero
23(pointer-type) dado e inicializa dicha variable con la dirección calculada por el inicializador de puntero fijo
24(fixed-pointer-initializer) correspondiente. Una variable local declarada en una instrucción f i xedes accesible en
25cualquier inicializador de puntero fijo (fixed-pointer-initializer) situado a la derecha de la declaración de la
26variable, y en la instrucción incrustada (embedded-statement) de la instrucción f i xed. Una variable local
27declarada por una instrucción fixed se considera de sólo lectura. Se producirá un error en tiempo de compilación
28si la instrucción incrustada intenta modificar esta variable local (por medio de asignación o utilizando los
29operadores ++ y --) o bien si la pasa como un parámetro ref o out.
30Un inicializador de puntero fijo (fixed-pointer-initializer) puede ser uno de los siguientes:
31• El símbolo (token) “&” seguido de una referencia de variable (variable-reference) (§5.3.3) a una variable
32 móvil (§18.3) de un tipo no administrado T, siempre que el tipo T* sea implícitamente convertible al tipo de
33 puntero proporcionado en la instrucción fixed. En este caso, el inicializador calcula la dirección de la
34 variable dada, y se garantiza que ésta permanecerá en una dirección fija durante toda la ejecución de la
35 instrucción fixed.
36• Una expresión de un tipo de matriz (array-type) con elementos de un tipo no administrado T, siempre que el
37 tipo T* sea implícitamente convertible al tipo de puntero proporcionado por la instrucción fixed. En este
38 caso, el inicializador calcula la dirección del primer elemento de la matriz, y se garantiza que toda la matriz
39 permanecerá en una dirección fija durante toda la ejecución de la instrucción fixed. El comportamiento de la
40 instrucción fixed se define según sea su implementación si la expresión de matriz es nula o la matriz
41 contiene cero elementos.
42• Una expresión de tipo string, siempre que el tipo char* sea implícitamente convertible al tipo de puntero
43 proporcionado en la instrucción fixed. En este caso, el inicializador calcula la dirección del primer carácter
44 de la cadena, y se garantiza que toda la cadena permanecerá en una dirección fija durante toda la ejecución

793384 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


794 Capítulo 18 Código no seguro

1 de la instrucción f i xed. El comportamiento de la instrucción f i xedse define según sea su implementación si


2 la expresión de cadena es nula.
3Por cada dirección calculada por un inicializador de puntero fijo (fixed-pointer-initializer), la instrucción f i xed
4garantiza que la variable a la que hace referencia la dirección no está sujeta a reubicación o eliminación por el
5recolector de elementos no utilizados en toda la duración de la instrucción f i xed. Por ejemplo, si la dirección
6calculada por un inicializador de puntero fijo (fixed-pointer-initializer) hace referencia a un campo de un objeto
7o un elemento de una instancia de matriz, la instrucción fixed garantiza que la instancia de objeto contenedora
8no se reubicará ni eliminará en toda la duración de la instrucción.
9Es responsabilidad del programador asegurarse de que los punteros creados mediante instrucciones f i xedno
10permanecen tras la ejecución de dichas instrucciones. Por ejemplo, cuando se pasan punteros creados con
11instrucciones f i xeda interfaces API externas, es responsabilidad del programador asegurarse de que las API no
12retienen memoria de estos punteros.
13Los objetos fijos pueden provocar la fragmentación del montón al no poder moverse. Por este motivo, los
14objetos deben ser fijos sólo cuando sea absolutamente necesario y por el menor tiempo posible.
15En el ejemplo
16 c lass Tes t
17 {
18 s ta t i c i n t x ;
19 int y;
20 unsa fe s ta t i c vo id F ( i n t * p) {
21 *p = 1 ;
22 }
23 s ta t i c vo id Main ( ) {
24 Tes t t = new Tes t ( ) ;
25 i n t [ ] a = new i n t [10 ] ;
26 unsa fe {
27 f i xed ( i n t * p = &x) F (p ) ;
28 f i xed ( i n t * p = &t .y ) F (p ) ;
29 f i xed ( i n t * p = &a[0 ] ) F (p ) ;
30 f i xed ( i n t * p = a) F (p ) ;
31 }
34se muestran varios usos de la instrucción f i xed. La primera instrucción fija y obtiene la dirección de un campo
32
35estático, la segunda fija y obtiene la dirección de un campo de instancia, y la tercera fija y obtiene la dirección
33
36de un elemento de matriz. En cada caso hubiera sido un error utilizar el operador & habitual, ya que las variables
37se clasifican todas como variables móviles.
38La tercera y cuarta instrucción f i xeddel ejemplo anterior producen idénticos resultados. En general, para una
39instancia de matriz a, especificar &a[0] en una instrucción fixed es lo mismo que especificar simplemente a.
40Este es otro ejemplo de la instrucción fixed, esta vez utilizando string:
41 c lass Tes t
42 {
43 s ta t i c s t r i ng name = "xx" ;
44 unsa fe s ta t i c vo id F (char* p) {
45 f o r ( i n t i = 0 ; p[ i ] != ' \ 0 ' ; ++i )
46 Conso le .Wr i teL ine (p [ i ] ) ;
47 }

795Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 385


796Especificación del lenguaje C#

1 static void Main() {

2 unsafe {

3 fixed (char* p = name) F(p);

4 fixed (char* p = "xx") F(p);

58En una matriz de}contexto no seguro, los elementos de matrices multidimensionales se almacenan por orden de
9índice creciente, comenzando con el índice 0 y terminando con el índice Length – 1. Los elementos de matrices
6
10multidimensionales se almacenan de manera que los índices de la dimensión del extremo derecho se
117incrementan en primer lugar, a continuación la dimensión situada inmediatamente a su izquierda, etc., siguiendo
12por la izquierda. Dentro de una instrucción fixed que obtiene un puntero p a una instancia de matriz a, los
13valores de puntero comprendidos entre p y p + a.Length - 1 representan direcciones de los elementos de la
14matriz. De igual forma, las variables comprendidas en el intervalo de p[0] a p[a.Length - 1] representan los
15propios elementos de la matriz. Dada la forma en que se almacenan las matrices, se puede tratar una matriz de
16cualquier dimensión como si fuera lineal.
17Por ejemplo:

18 using System;
19 class Test

20 {

21 static void Main() {

22 int[,,] a = new int[2,3,4];

23 unsafe {

24 fixed (int* p = a) {

25
29 for (int i = 0; i < 2; ++i)
26
30 for (int j = 0; j < 3; ++j) {
27
31 for (int k = 0; k < 4; ++k)
28
32 Console.Write("[{0},{1},{2}] = {3,2} ", i, j, k, a[i,j,k]);

33 Console.WriteLine();
37produce el resultado:
34
38
35 [0,0,0] = 0 [0,0,1] = 1 [0,0,2] = 2 [0,0,3] = 3

39
36 [0,1,0] = 4 [0,1,1] = 5 [0,1,2] = 6 [0,1,3] = 7

40 [0,2,0] = 8 [0,2,1] = 9 [0,2,2] = 10 [0,2,3] = 11

41 [1,0,0] = 12 [1,0,1] = 13 [1,0,2] = 14 [1,0,3] = 15


44En el siguiente ejemplo:
42
45 class Test
43
46 {

47 unsafe static void Fill(int* p, int count, int value) {

48
49
797386 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
798 Capítulo 18 Código no seguro

1 static void Main() {

2 int[] a = new int[100];

3 unsafe {

4 fixed (int* p = a) Fill(p, 100, -1);

58se utiliza una instrucción


} f i xedpara fijar una matriz de manera que pueda pasarse su dirección a un método que
9toma un puntero.
6
7

799Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 387


800Especificación del lenguaje C#

1Un valor char* producido al fijar una instancia de cadena siempre apunta a una cadena terminada en null.
2Dentro de una instrucción fixed que obtiene un puntero p a una instancia de cadena s, los valores de puntero
3comprendidos entre p y p + s.Length - 1 representan direcciones de los caracteres de la cadena, y el valor de
4puntero p + s.Length siempre apunta a un carácter null (el carácter con valor '\0').
5La modificación de objetos de tipo administrado mediante punteros fijos puede producir un comportamiento
6indefinido. Por ejemplo, dado que las cadenas son invariables, es responsabilidad del programador asegurarse de
7que los caracteres de una cadena fija a los que hace referencia un puntero no se modifiquen.
8La terminación automática en null de cadenas es especialmente cómoda al llamar a interfaces API externas que
9esperan cadenas “de estilo C”. Observe, sin embargo, que no se permite una instancia de cadena que contenga
10caracteres null. Si se detectan dichos caracteres null, la cadena aparecerá truncada al tratarla como char*
11terminada en null.

1218.7 Asignación de pila


13En un contexto no seguro, una declaración de variable local (§8.5.1) puede incluir un inicializador de asignación
14de pila que asigne memoria de la pila de llamadas.
15 local-variable-initializer:
16 expression
17 array-initializer
18 stackalloc-initializer
19 stackalloc-initializer:
20 stackalloc unmanaged-type [ expression ]
21El tipo no administrado (unmanaged-type) indica el tipo de los elementos que se almacenarán en la ubicación
22recién asignada, y la expresión (expression) indica el número de estos elementos. Tomados juntos, especifican el
23tamaño de asignación requerido. Dado que el tamaño de una asignación de pila no puede ser negativo, supone
24un error en tiempo de compilación especificar el número de elementos como una expresión constante (constant-
25expression) que se evalúa como un número negativo.
26Un inicializador de asignación de pila de la forma stackalloc T[E] requiere que T sea un tipo no administrado
27(§18.2) y E sea una expresión de tipo int. La construcción asigna E * sizeof(T) bytes de la pila de llamadas y
28devuelve un puntero de tipo T* al bloque recién asignado. Si E es un valor negativo, el comportamiento es
29indefinido. Si E es cero, no se realiza ninguna asignación, y el puntero devuelto se define según su
30implementación. Si no hay memoria suficiente disponible para asignar un bloque del tamaño dado, se inicia una
31excepción System.StackOverflowException.
32El contenido de la memoria recién asignada es indefinido.
33No se permiten los inicializadores de asignación de pila en bloques catch o finally (§8.10).
34No hay forma de liberar explícitamente la memoria asignada con stackalloc. Todos los bloques asignados de
35memoria de la pila y creados durante la ejecución de un miembro de función se descartan automáticamente
36cuando éste devuelve un valor. Esto se corresponde con la función alloca, una extensión habitual en las
37implementaciones de C y C++.

801388 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


802 Capítulo 18 Código no seguro

1En el siguiente ejemplo:

2 using System;
3 class Test

4 {

5 static string IntToString(int value) {

6 int n = value >= 0? value: -value;

7 unsafe {

8 char* buffer = stackalloc char[16];

9 char* p = buffer + 16;

10 do {

11 *--p = (char)(n % 10 + '0');


18
12 static void Main() {

19
13 Console.WriteLine(IntToString(12345));

20
14 Console.WriteLine(IntToString(-999));
23
21se utiliza un inicializador s tacka l l oc
15 en el método I n t ToS t r i ng
para asignar un búfer de 16 caracteres en la pila.
24El búfer queda descartado automáticamente cuando el método devuelve un valor.
22
16
25
1718.8 Asignación dinámica de memoria
26Salvo por el operador s tacka l l oc
, C# no proporciona construcciones predefinidas para administrar memoria no
27recolectada por el recolector de elementos no utilizados. Tales servicios suelen proporcionarlos bibliotecas de
28clases auxiliares o importadas directamente del sistema operativo subyacente. Por ejemplo, la clase Memory de
29abajo muestra cómo es posible obtener acceso desde C# a las funciones del montón de un sistema operativo
30subyacente:

31 using System;

32 using System.Runtime.InteropServices;
33 public unsafe class Memory

34 {

35 // Handle for the process heap. This handle is used in all calls to the
37 static int ph = GetProcessHeap();
36
38 // Private instance constructor to prevent instantiation.
39 private Memory() {}
40 // Allocates a memory block of the given size. The allocated memory is

41 // automatically initialized to zero.


42 public static void* Alloc(int size) {

43 void* result = HeapAlloc(ph, HEAP_ZERO_MEMORY, size);

44 if (result == null) throw new OutOfMemoryException();

45
46
803Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 389
804Especificación del lenguaje C#

1 // Copies count bytes from src to dst. The source and destination

2 // blocks are permitted to overlap.


3 public static void Copy(void* src, void* dst, int count) {

4 byte* ps = (byte*)src;

5 byte* pd = (byte*)dst;

6 if (ps > pd) {

7 for (; count != 0; count--) *pd++ = *ps++;

8 }

139 // Frees a memory block.


14
10 public static void Free(void* block) {

15
11 if (!HeapFree(ph, 0, block)) throw new InvalidOperationException();

16
17
12 // Re-allocates a memory block. If the reallocation request is for a

18 // larger size, the additional region of memory is automatically

19
20 public static void* ReAlloc(void* block, int size) {

21 void* result = HeapReAlloc(ph, HEAP_ZERO_MEMORY, block, size);

22 if (result == null) throw new OutOfMemoryException();

23
25 // Returns the size of a memory block.
24
26 public static int SizeOf(void* block) {

27 int result = HeapSize(ph, 0, block);

28 if (result == -1) throw new InvalidOperationException();

29
31 // Heap API flags
30
32 const int HEAP_ZERO_MEMORY = 0x00000008;
33 // Heap API functions
34 [DllImport("kernel32")]

35 static extern int GetProcessHeap();


36 [DllImport("kernel32")]

37 static extern void* HeapAlloc(int hHeap, int flags, int size);


38 [DllImport("kernel32")]

39 static extern bool HeapFree(int hHeap, int flags, void* block);


40 [DllImport("kernel32")]

41 static extern void* HeapReAlloc(int hHeap, int flags,

42
43 [DllImport("kernel32")]

44 static extern int HeapSize(int hHeap, int flags, void* block);

45

805390 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


806 Capítulo 18 Código no seguro

1A continuación se muestra un ejemplo que utiliza la clase Memory :

2 class Test

3 {

4 static void Main() {

5 unsafe {

6 byte* buffer = (byte*)Memory.Alloc(256);

7 try {

8 for (int i = 0; i < 256; i++) buffer[i] = (byte)i;

9 byte[] array = new byte[256];

10 fixed (byte* p = array) Memory.Copy(buffer, p, 256);

11 }
19El ejemplo asigna 256 bytes de memoria mediante Memory.A l l oce inicializa el bloque de memoria con valores
12
20crecientes de 0 a 255. A continuación, asigna una matriz de bytes de 256 elementos y utiliza Memory.Copy para
13
21copiar el contenido del bloque de memoria en la matriz de bytes. Por último, se libera el bloque de memoria
22
14mediante Memory.Free y se envía el contenido de la matriz de bytes como resultado a la consola.
15
16
17
18

807Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 391


808Especificación del lenguaje C#

1 A.Comentarios de documentación

2C# proporciona un mecanismo para que los programadores puedan documentar el código mediante una sintaxis
3de comentarios que contiene texto XML. En los archivos de código fuente, es posible utilizar comentarios con
4un cierto formato para indicar a una herramienta que genere texto XML a partir de estos comentarios y de los
5elementos de código fuente a los que precede. Los comentarios que utilizan esta sintaxis se denominan
6comentarios de documentación. Deben preceder inmediatamente a un tipo definido por el usuario (como una
7clase, una estructura o una interfaz) o a un miembro (como un campo, un evento, una propiedad o un método).
8La herramienta de generación de XML se denomina generador de documentación. (Este generador podría ser,
9pero no es necesario que así sea, el propio compilador de C#.) El resultado producido por el generador de
10documentación se denomina archivo de documentación. Un archivo de documentación se utiliza como entrada
11a un visor de documentación, una herramienta pensada para generar alguna ordenación de presentación visual
12de información de tipo y su documentación asociada.
13Esta especificación sugiere el uso de un conjunto de etiquetas en los comentarios de esta clase de
14documentación, sin embargo, siempre que se respete la correcta sintaxis del lenguaje XML, se puede utilizar el
15conjunto de etiquetas que se desee para este fin.

16A.1 Introducción
17Es posible utilizar comentarios con un formato determinado para indicar a una herramienta que genere texto
18XML a partir de los mismos y de los elementos de código fuente a los que preceden. Tales comentarios pueden
19ser: comentarios de una sola línea que comienzan con tres barras diagonales (/ / ),/ o comentarios delimitados que
20van precedidos de una barra diagonal y dos asteriscos (/ * *). Deben preceder inmediatamente al tipo definido por
21el usuario (como una clase, un delegado o una interfaz) o al miembro (como un campo, un evento, una
22propiedad o un método) al que hacen referencia los comentarios. Las secciones de atributos (§17.2) se
23consideran parte de las declaraciones; por lo tanto, los comentarios de documentación deben preceder a los
24atributos que se aplican a un tipo o a un miembro.
25Sintaxis:
26 single-line-doc-comment:
27 / / / input-charactersopt
28 delimited-doc-comment:
29 / * * delimited-comment-charactersopt * /
30En un comentario de una sola línea (single-line-doc-comment), si aparece un carácter de espacio en blanco
31(whitespace) a continuación de los caracteres / / /en cada uno de los comentarios de documentación de una sola
32línea (single-line-doc-comments), adyacentes al comentario de documentación de una sola línea (single-line-
33doc-comment) actual, el carácter de espacio en blanco (whitespace) no se incluirá en los resultados XML.
34En un comentario de documentación delimitado (delimited-doc-comment), si el primer carácter distinto del
35espacio en blanco (whitespace) es un asterisco y el mismo patrón de caracteres opcionales de espacio en blanco
36y un carácter de asterisco se repite al principio de cada una de las líneas dentro del comentario de
37documentación delimitado (delimited-doc-comment), entonces los caracteres del patrón repetido no se incluyen
38en el código XML resultante. El patrón puede incluir caracteres de espacio en blanco (whitespace) antes y
39después del carácter de asterisco.

809392 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


810 Apéndice A Comentarios de documentación

1Ejemplo:

2 /// <summary>Class <c>Point</c> models a point in a two-dimensional

3 /// plane.</summary>

4 ///

5 public class Point

6 {
10El texto de los comentarios de documentación debe estar correctamente estructurado según las reglas del
117lenguaje XML (http://www.w3.org/TR/REC-xml). Si hay incorrecciones en el texto XML, se generará una
128advertencia y el archivo de documentación incluirá un comentario que hará referencia al error encontrado.
139Aunque los desarrolladores pueden crear su propio juego de etiquetas, existe un juego recomendado en la
14sección §18.8. Algunas de las etiquetas recomendadas tienen significados especiales:
15• La etiqueta <param> se utiliza para describir parámetros. Si se utiliza dicha etiqueta, el generador de
16 documentación deberá comprobar que el parámetro especificado existe y que todos los parámetros se
17 encuentran descritos en los comentarios de documentación. Si esta comprobación encuentra algún error, el
18 generador de documentación emitirá una advertencia.
19• El atributo c re fse puede asociar a cualquier etiqueta para proporcionar una referencia a un elemento de
20 código. El generador de documentación debe comprobar si existe dicho elemento de código. Si la
21 comprobación no tiene éxito, el generador de documentación emite una advertencia. Al buscar un nombre
22 descrito en un atributo c re f, el generador de la documentación debe respetar la visibilidad de espacio de
23 nombres según las instrucciones using que aparecen dentro del código fuente.
24• La etiqueta <summary> está pensada para su uso en un visor de documentación para mostrar información
25 adicional sobre un tipo o un miembro.
26• La etiqueta <include> incluye información de un archivo XML externo.
27Observe que el archivo de documentación no proporciona información completa sobre el tipo y los miembros
28(por ejemplo, no contiene ninguna información de tipos). Para obtener tal información acerca de un tipo o un
29miembro, el archivo de documentación debe utilizarse conjuntamente con el mecanismo de reflexión sobre el
30tipo o el miembro real.

31A.2 Etiquetas recomendadas


32El generador de documentación debe aceptar y procesar todas aquellas etiquetas que sean válidas según las
33reglas de XML. Las siguientes etiquetas proporcionan la funcionalidad habitual en la documentación de usuario.
34(Por supuesto, se pueden utilizar otras etiquetas).
35

811Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 393


812Especificación del lenguaje C#

Etiqueta Sección Finalidad


<c> 18.8 Establecer un tipo de fuente de código para un texto
<code> 18.8 Establecer una o más líneas de código fuente o indicar
el final de un programa
<example> 18.8 Indicar un ejemplo
<exception> 18.8 Identifica las excepciones que pueden iniciar un método
<include> 18.8 Incluye XML procedente de un archivo externo
<list> 18.8 Crear una lista o una tabla
<para> 18.8 Permite agregar una estructura al texto
<param> 18.8 Describe un parámetro para un método o constructor
<paramref> 18.8 Identifica una palabra como nombre de parámetro
<permission> 18.8 Documenta la accesibilidad de seguridad de un
miembro
<summary> 18.8 Describe un tipo
<returns> 18.8 Describe el valor devuelto de un método
<see> 18.8 Especifica un vínculo
<seealso> 18.8 Genera una entrada de tipo Vea también
<summary> 18.8 Describe un miembro de un tipo
<value> 18.8 Describe una propiedad
36
37A.2.1 <c>
38Esta etiqueta proporciona un mecanismo para indicar que se debe establecer una fuente especial para un
39fragmento de texto dentro de una descripción, tal como la que se usa para un bloque de código. Para líneas de
40código real, utilice <code> (§18.8).
41Sintaxis:
42 <c>text</c>
43Ejemplo:
44 / / / <summary>C lass <c>Po in t< /c> mode l s a po in t i n a two - d imens iona l
45 / / / p lane .< /summary>
46 pub l i c c lass Po in t
47 {
48 // ...
49 }
50A.2.2 <code>
51Esta etiqueta se utiliza para establecer una o más líneas de código fuente o para indicar el final de un programa
52en alguna fuente especial. Para pequeños fragmentos de código en texto narrativo, utilice <c> (§18.8).
53Sintaxis:

813394 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


814 Apéndice A Comentarios de documentación

1 <code>source code or program output</code>


2Ejemplo:

3 /// <summary>This method changes the point's location by

4 /// the given x- and y-offsets.

5 /// <example>For example:

6 /// <code>

7 /// Point p = new Point(3,5);

8 /// p.Translate(-1,3);

139 public void Translate(int xor, int yor) {


10
14 X += xor;
11
15 Y += yor;
17A.2.3 <example>
12
16
18Esta etiqueta permite código de ejemplo dentro de un comentario, para especificar cómo puede utilizarse un
19método u otro miembro de biblioteca. Generalmente, esto también supone la utilización de la etiqueta <code>
20(§18 .8).
21Sintaxis:
22 <example>description</example>
23Ejemplo:
24Vea <code>( §18.8) para obtener un ejemplo.

25A.2.4 <exception>
26Esta etiqueta proporciona un medio para documentar las excepciones que puede iniciar un método.
27Sintaxis:
28 <except i on c re f="member">description</except i on>
29donde
30 cref="member "
31 El nombre de un miembro. El generador de documentación comprueba si el miembro en cuestión existe
32 y traduce member al nombre del elemento canónico en el archivo de documentación.
33 description
34 Descripción de las circunstancias en las que se inicia la excepción.
35Ejemplo:

815Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 395


816Especificación del lenguaje C#

1 public class DataBaseOperations

2 {

3 /// <exception cref="MasterFileFormatCorruptException"></exception>

4 /// <exception cref="MasterFileLockedOpenException"></exception>

5 public static void ReadRecord(int flag) {

6 if (flag == 1)

7 throw new MasterFileFormatCorruptException();

8
13A.2.5 <include>
149Esta etiqueta permite incluir información de un documento XML externo al archivo de código fuente. El archivo
15
10externo debe ser un documento XML sin errores; al documento se aplica una expresión XPath para especificar
16qué parte de la sintaxis XML del documento debe incluirse. La etiqueta <inc lude> se sustituye entonces por el
11texto XML seleccionado del documento externo.
17
18Sintaxis:
12
19 <inc lude f i l e="filename" path=" xpath" />
20donde
21 f i l e="filename"
22 Nombre de un archivo XML externo. El nombre del archivo se interpreta en relación con el archivo que
23 contiene la etiqueta include.
24 path=" xpath"
25 Una expresión XPath que selecciona parte del código XML del archivo XML externo.
26Ejemplo:
27Si el código fuente contuviera una declaración como la siguiente:
28 / / / <inc lude f i l e=" docs .xml" path=' ex t radoc /c l ass [@name=" In tL i s t " ] / * ' />
29 pub l i c c lass I n tL i s t { … }
30y el archivo externo “docs .xml” tuviera el siguiente contenido:
31 <?xml vers ion="1 .0 "?>
32 <ext radoc>
33 <c lass name=" I n tL i s t " >
34 <summary>
35 Conta ins a l i s t o f i n tegers .
36 </summary>
37 </c lass>
38 <c lass name=" St r i ngL i s t " >
39 <summary>
40 Conta ins a l i s t o f i n tegers .
41entonces la documentación resultante sería la misma que si el código fuente contuviera:
44
42
43

817396 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


818 Apéndice A Comentarios de documentación

1 /// <summary>

2 /// Contains a list of integers.

3 /// </summary>
54A.2.6 <list>
6Esta etiqueta se utiliza para crear una lista o tabla de elementos. Puede contener un bloque <l i s theader>para
7definir la fila de encabezado de una tabla o de una lista de definiciones. (Cuando se define una tabla, sólo es
8necesario suministrar una entrada para te rm en el encabezado.)
9Cada elemento de la lista se especifica con un bloque <i tem> . Al crear una lista de definiciones, es necesario
10especificar tanto te rm como description. Sin embargo, para una tabla, lista con viñetas o lista numerada, sólo
11es necesario especificar description.

819Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 397


820Especificación del lenguaje C#

1Sintaxis:

2 <list type="bullet" | "number" | "table">

3 <listheader>

4 <term>term</term>

5 <description>description</description>

6 </listheader>

7 <item>

8 <term>term</term>

9 <description>description</description>

10 </item>
17donde
11
18 term
12
19 El término que se desea definir, cuya definición se encuentra en desc r i p t i on
.
13
20 description
14
21 Puede ser el elemento de una lista numerada o con viñetas, o la definición de un te rm.
15
22Ejemplo:
16
23 public class MyClass

24 {

25 /// <summary>Here is an example of a bulleted list:

26 /// <list type="bullet">

27 /// <item>

28 /// <description>Item 1.</description>

29 /// </item>

30 /// <item>

31 /// <description>Item 2.</description>

32 /// </item>
39A.2.7 <para>
33
40Este etiqueta debe utilizarse dentro de otras, como <summary> (§18 .8) o <retu rns> (§18.8) y permite
34
41agregar la estructura al texto.
35Sintaxis:
42
36
43 <para>conten t </para>
44
37donde
45 content
38
46 Texto del párrafo.

821398 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


822 Apéndice A Comentarios de documentación

1Ejemplo:

2 /// <summary>This is the entry point of the Point class testing program.

3 /// <para>This program tests each method and operator, and

4 /// is intended to be run after any non-trvial maintenance has

5 /// been performed on the Point class.</para></summary>

6 public static void Main() {


9A.2.8 <param>
107Esta etiqueta se utiliza para describir un parámetro para un método, constructor o indizador.
118Sintaxis:

12 <param name="name">description</param>
13donde
14 name
15 Nombre del parámetro.
16 description
17 Una descripción del parámetro.
18Ejemplo:

19 /// <summary>This method changes the point's location to

20 /// the given coordinates.</summary>

21 /// <param name="xor">the new x-coordinate.</param>

22 /// <param name="yor">the new y-coordinate.</param>

23 public void Move(int xor, int yor) {

24
27A.2.9 <paramref>
28Esta etiqueta se utiliza para indicar que una palabra es un parámetro. El archivo de documentación se puede
25
29procesar de manera que aplique un formato diferente a este parámetro.
26
30Sintaxis:

31 <paramref name="name"/>
32donde
33 name
34 Nombre del parámetro.
35Ejemplo:

36 /// <summary>This constructor initializes the new Point to

37 /// (<paramref name="xor"/>,<paramref name="yor"/>).</summary>

38 /// <param name="xor">the new Point's x-coordinate.</param>

39

823Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 399


824Especificación del lenguaje C#

1 public Point(int xor, int yor) {

2 X = xor;

3 Y = yor;
54A.2.10 <permission>
6Esta etiqueta permite documentar la accesibilidad de seguridad de un miembro.
7Sintaxis:

8 <permission cref="member">description</permission>
9donde
10 cref="member "
11 El nombre de un miembro. El generador de documentación comprueba si el elemento de código dado
12 existe y traduce member al nombre del elemento canónico en el archivo de documentación.
13 description
14 Descripción del acceso al miembro.
15Ejemplo:
16 / / / <permiss ion c re f="Sys tem.Secur i t y. Pe rmiss ionSe t ">Everyone can
17 / / / access th i s method .< /pe rmiss ion>
18 pub l i c s ta t i c vo id Tes t ( ) {
19 // ...
20 }
21A.2.11 <summary>
22Esta etiqueta se utiliza para especificar información general sobre un tipo. (Utilice <summary> (§18 .8) para
23describir los miembros de un tipo.)
24Sintaxis:
25 <summary>desc r i p t i on</summary>
26donde
27 description
28 Incluye el texto del resumen.
29Ejemplo:
30 / / / <summary>C lass <c>Po in t< /c> mode l s a po in t i n a
31 / / / two - d imens iona l p lane .< /summary>
32 pub l i c c lass Po in t
33 {
34 // ...
35 }

36A.2.12 <returns>
37Esta etiqueta se utiliza para describir el valor devuelto de un método.
38Sintaxis:
39 <retu rns>desc r i p t i on</ re tu rns>

825400 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


826 Apéndice A Comentarios de documentación

1donde
2 description
3 Descripción del valor devuelto.

827Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 401


828Especificación del lenguaje C#

1Ejemplo:

2 /// <summary>Report a point's location as a string.</summary>

3 /// <returns>A string representing a point's location, in the form (x,y),

4 /// without any leading, trailing, or embedded whitespace.</returns>

5 public override string ToString() {


86A.2.13 <see>
9Esta etiqueta permite especificar un vínculo dentro del texto. Utilice <seea l so> (§18 .8) para indicar el texto
7
10que debe aparecer en una sección Vea también.
11Sintaxis:
12 <see c re f="member" />
13donde
14 cref="member "
15 El nombre de un miembro. El generador de documentación comprueba si el elemento de código dado
16 existe y cambia member por el nombre del elemento en el archivo de documentación generado.
17Ejemplo:
18 / / / <summary>Th i s method changes the po in t ' s l oca t i on to
19 /// the g iven coord ina tes .< /summary>
20 / / / <see c re f=" Trans la te " />
21 pub l i c vo id Move( in t xor , i n t yor ) {
22 X = xor ;
23 Y = yor ;
25
24 / / / <summary>Th i s method changes the po in t ' s l oca t i on by
26 /// the g iven x - and y - o f f se ts .
27 / / / </summary>
28 / / / <see c re f="Move" />
29 pub l i c vo id Trans la te ( i n t xor , i n t yor ) {
30 X += xor ;
31 Y += yor ;
33A.2.14 <seealso>
32
34Esta etiqueta permite generar una entrada para la sección Vea también. Utilice <see> (§18 .8) para especificar
35un vínculo desde dentro del texto.
36Sintaxis:
37 <seea l so c re f="member" />
38donde
39 cref="member "
40 El nombre de un miembro. El generador de documentación comprueba si el elemento de código dado
41 existe y cambia member por el nombre del elemento en el archivo de documentación generado.

829402 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


830 Apéndice A Comentarios de documentación

1Ejemplo:

2 /// <summary>This method determines whether two Points have the same

3 /// location.</summary>

4 /// <seealso cref="operator=="/>

5 /// <seealso cref="operator!="/>

6 public override bool Equals(object o) {


9A.2.15 <summary>
107Esta etiqueta se utiliza para describir un miembro de un tipo. Utilice <summary> (§18 .8) para describir el
118propio tipo.
12Sintaxis:
13 <summary>description</summary>
14donde
15 description
16 Un resumen del miembro.
17Ejemplo:
18 / / / <summary>Th i s cons t ruc to r i n i t i a l i zes the new Po in t to (0 ,0 ) .< / summary>
19 pub l i c Po in t ( ) : th i s (0 ,0 ) {
20 }
21A.2.16 <value>
22Esta etiqueta permite describir una propiedad.
23Sintaxis:
24 <va lue>proper ty desc r i p t i on</va lue>
25donde
26 property description
27 Descripción de la propiedad.
28Ejemplo:
29 / / / <va lue>Proper ty <c>X</c> rep resen ts the po in t ' s x - coord ina te .< /va lue>
30 pub l i c i n t X
31 {
32 get { re tu rn x ; }
33 se t { x = va lue ; }
34A.3 Procesar el archivo de documentación
35
36El generador de documentación genera una cadena de id. por cada elemento del código fuente marcado con un
37comentario de documentación. Esta cadena de id. identifica de forma única un elemento fuente. Un visor de
38documentación puede utilizar una cadena de id. para identificar el correspondiente elemento de
39metadatos/reflexión al que se aplica la documentación.
40El archivo de documentación no es una representación jerárquica del código fuente, es una lista sencilla con una
41cadena de id. generada para cada elemento.

831Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 403


832Especificación del lenguaje C#

1A.3.1 Formato de cadena de Id.


2El generador de documentación cumple las siguientes reglas cuando genera las cadenas de id.:
3• No se coloca ningún espacio en blanco en la cadena.
4• La primera parte de la cadena identifica el tipo de miembro que se desea documentar por medio de un único
5 carácter seguido de dos puntos. Se definen los siguientes tipos de miembros:
6

Carácter Descripción
E Evento
F Campo
M Método (incluidos constructores, destructores y operadores)
N Espacio de nombres
P Propiedad (incluidos los indizadores)
T Tipo (tal como class, delegate, enum, interface y struct)
! Cadena de error; el resto de la cadena proporciona información
acerca del error. Por ejemplo, el generador de documentación
genera información de error para vínculos que no se pueden
resolver.
7
8• La segunda parte de la cadena es el nombre completo del elemento, empezando en la raíz del espacio de
9 nombres. El nombre del elemento, los tipos contenedores y el espacio de nombres se separan mediante
10 puntos. Si el nombre del elemento ya contiene puntos, éstos se reemplazan por caracteres # (U+0023 ). (Se
11 asume que ningún elemento tiene este carácter en su nombre).
12• Para los métodos y propiedades con argumentos, sigue la lista de argumentos entre paréntesis. Si no existen
13 argumentos, los paréntesis se omiten. Los argumentos se separan por comas. La codificación de cada
14 argumento es la misma que una firma CLI, como sigue: los argumentos se representan por su nombre
15 completo. Por ejemplo, i n tse convierte en Sys tem. In t32, string se convierte en System.String, object
16 se convierte en System.Object, y así sucesivamente. Los argumentos que contienen el modificador out o
17 ref llevan un signo @ tras su nombre de tipo. Los argumentos que se pasan por valor o a través de params
18 no tienen una notación especial. Los argumentos que son matrices se representan como [ lowerbound : size ,
19 … , lowerbound : size ], donde el número de comas es el rango menos 1, y los límites inferiores y tamaños
20 de cada dimensión, si se conocen, se representan en formato decimal. Si no se especifica un límite inferior ni
21 un tamaño, se omite. Si el límite inferior (lowerbound) y el tamaño (size) de una dimensión particular se
22 omiten, también se omite el signo “:”. Las matrices escalonadas se representan con un “[]” por nivel. Los
23 argumentos con tipos de puntero distintos de void se representan con un * a continuación del nombre de
24 tipo. Un puntero void se representa mediante un nombre de tipo de System.Void.

25A.3.2 Ejemplos de cadena de Id.


26Los siguientes ejemplos muestran cada uno un fragmento de código C#, junto con la cadena de id. que se
27produce a partir de cada elemento de código fuente que puede tener un comentario de documentación:
28• Los tipos se representan con su nombre completo.

29 enum Color { Red, Blue, Green }

833404 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


834 Apéndice A Comentarios de documentación

1 namespace Acme

2 {

34 struct ValueType {...}


5 class Widget: IProcess

6 {

78 public interface IMenuItem {...}


9 public delegate void Del(int i);
10 public enum Direction { North, South, East, West }

11 }

12
13 "T:Color"

14 "T:Acme.IProcess"

15 "T:Acme.ValueType"

16 "T:Acme.Widget"

17 "T:Acme.Widget.NestedClass"
21• Los campos se representan con su nombre completo.
18
22
19 namespace Acme

23
20 {

24 struct ValueType

25 {
28
26 class Widget: IProcess

29
27 {

30 public class NestedClass

31 {
34
32 private string message;

35
33 private static Color defaultColor;

36 private const double PI = 3.14159;

37 protected readonly double monthlyAverage;

38 private long[] array1;

39 private Widget[,] array2;

40
41
42
43

835Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 405


836Especificación del lenguaje C#

1 "F:Acme.ValueType.total"

2 "F:Acme.Widget.NestedClass.value"

3 "F:Acme.Widget.message"

4 "F:Acme.Widget.defaultColor"

5 "F:Acme.Widget.PI"

6 "F:Acme.Widget.monthlyAverage"

7
8
9
10

837406 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


838 Apéndice A Comentarios de documentación

1• Constructores.

2 namespace Acme

3 {

4 class Widget: IProcess

57 public Widget() {...}


86 public Widget(string s) {...}

9 }

10
11 "M:Acme.Widget.#cctor"

12 "M:Acme.Widget.#ctor"
14•
13 Destructores.

15 namespace Acme

16 {

17 class Widget: IProcess

18 {

19 ~Widget() {...}
22 "M:Acme.Widget.Finalize"
23
20• Métodos.
21
24 namespace Acme

25 {

26 struct ValueType

27 {
30
28 class Widget: IProcess

31
29 {

32 public class NestedClass

33 {
36
34 public static void M0() {...}

37
35 public void M1(char c, out float f, ref ValueType v) {...}

38 public void M2(short[] x1, int[,] x2, long[][] x3) {...}

39 public void M3(long[][] x3, Widget[][,,] x4) {...}

40 public unsafe void M4(char *pc, Color **pf) {...}

41 public unsafe void M5(void *pv, double *[][,] pd) {...}

42
43
44
839Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 407
840Especificación del lenguaje C#

1 "M:Acme.ValueType.M(System.Int32)"

2 "M:Acme.Widget.NestedClass.M(System.Int32)"

3 "M:Acme.Widget.M0"

4 "M:Acme.Widget.M1(System.Char,System.Single@,Acme.ValueType@)"

5 "M:Acme.Widget.M2(System.Int16[],System.Int32[0:,0:],System.Int64[][])"

6 "M:Acme.Widget.M3(System.Int64[][],Acme.Widget[0:,0:,0:][])"

7
8
9

841408 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


842 Apéndice A Comentarios de documentación

1• Propiedades e indizadores.

2 namespace Acme

3 {

4 class Widget: IProcess

5 {

6 public int Width { get {...} set {...} }

7 public int this[int i] { get {...} set {...} }


11 "P:Acme.Widget.Width"
8
12 "P:Acme.Widget.Item(System.Int32)"
149•
13 Eventos.
10
15 namespace Acme

16 {

17 class Widget: IProcess

18 {

19 public event Del AnEvent;


22 "E:Acme.Widget.AnEvent"
23
20• Operadores unarios.
21
24 namespace Acme

25 {

26 class Widget: IProcess

27 {

28 public static Widget operator+(Widget x) {...}


31 "M:Acme.Widget.op_UnaryPlus(Acme.Widget)"
32
29 El conjunto completo de nombres de función de operadores unarios es el siguiente: op_UnaryP lus,
33 op_UnaryNegat i on, op_LogicalNot, op_OnesComplement, op_Increment, op_Decrement, op_True
30
34 y op_False.
35• Operadores binarios.

36 namespace Acme

37 {

38 class Widget: IProcess

39 {

40 public static Widget operator+(Widget x1, Widget x2) {...}


43 "M:Acme.Widget.op_Addition(Acme.Widget,Acme.Widget)"
44
41 El conjunto completo de nombres de funciones de operadores binarios es el siguiente: op_Addition,
45 op_Subtraction, op_Multiply, op_Division, op_Modulus, op_BitwiseAnd, op_BitwiseOr,
42
46 op_ExclusiveOr, op_LeftShift, op_RightShift, op_Equality, op_Inequality, op_LessThan,
47 op_LessThanOrEqual, op_GreaterThan y op_GreaterThanOrEqual.

843Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 409


844Especificación del lenguaje C#

1• Los operadores de conversión tienen un carácter final “~” seguido del tipo de valor devuelto.

2 namespace Acme

3 {

4 class Widget: IProcess

5 {

6 public static explicit operator int(Widget x) {...}


107 "M:Acme.Widget.op_Explicit(Acme.Widget)~System.Int32"

118 "M:Acme.Widget.op_Implicit(Acme.Widget)~System.Int64"
12A.4 Un ejemplo
9
13A.4.1 Código fuente C#
14El ejemplo siguiente muestra el código fuente de una clase Po in t:

15 namespace Graphics

16 {

17
18 /// <summary>Class <c>Point</c> models a point in a two-dimensional plane.

19 /// </summary>
22 /// <summary>Instance variable <c>x</c> represents the point's
20
23 /// x-coordinate.</summary>
21
24
25 /// <summary>Instance variable <c>y</c> represents the point's

26 /// y-coordinate.</summary>

27
28 /// <value>Property <c>X</c> represents the point's x-coordinate.</value>

29 public int X

30 {

31 get { return x; }
34
32 /// <value>Property <c>Y</c> represents the point's y-coordinate.</value>

35
33 public int Y

36 {

37 get { return y; }
40
38 /// <summary>This constructor initializes the new Point to

41
39 /// (0,0).</summary>

42

845410 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


846 Apéndice A Comentarios de documentación

1 /// <summary>This constructor initializes the new Point to

2 /// (<paramref name="xor"/>,<paramref name="yor"/>).</summary>

3 /// <param><c>xor</c> is the new Point's x-coordinate.</param>

4 /// <param><c>yor</c> is the new Point's y-coordinate.</param>

5 public Point(int xor, int yor) {


96 /// <summary>This method changes the point's location to

107 /// the given coordinates.</summary>

118 /// <param><c>xor</c> is the new x-coordinate.</param>

12 /// <param><c>yor</c> is the new y-coordinate.</param>

13 /// <see cref="Translate"/>

14 public void Move(int xor, int yor) {


18 /// <summary>This method changes the point's location by
15
19 /// the given x- and y-offsets.
16
20 /// <example>For example:
17
21 /// <code>

22 /// Point p = new Point(3,5);

23 /// p.Translate(-1,3);

24 /// </code>

25 /// results in <c>p</c>'s having the value (2,8).

26 /// </example>

27 /// </summary>

28
35 /// <summary>This method determines whether two Points have the same
29
36 /// location.</summary>
30
37 /// <param><c>o</c> is the object to be compared to the current object.
31
38 /// </param>
32
39 /// <returns>True if the Points have the same location and they have
33
40 /// the exact same type; otherwise, false.</returns>
34
41 /// <seealso cref="operator=="/>

42
47 if (this == o) {
43
48 return true;
44
49
45
46

847Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 411


848Especificación del lenguaje C#

1 if (GetType() == o.GetType()) {

2 Point p = (Point)o;

3 return (X == p.X) && (Y == p.Y);

4 }
75 /// <summary>Report a point's location as a string.</summary>

86 /// <returns>A string representing a point's location, in the form (x,y),

9 /// without any leading, training, or embedded whitespace.</returns>

10 public override string ToString() {


13
11 /// <summary>This operator determines whether two Points have the same

14
12 /// location.</summary>

15 /// <param><c>p1</c> is the first Point to be compared.</param>

16 /// <param><c>p2</c> is the second Point to be compared.</param>

17 /// <returns>True if the Points have the same location and they have

18 /// the exact same type; otherwise, false.</returns>

19 /// <seealso cref="Equals"/>

20 /// <seealso cref="operator!="/>

21 public static bool operator==(Point p1, Point p2) {

22 if ((object)p1 == null || (object)p2 == null) {

23 return false;

24
32 /// <summary>This operator determines whether two Points have the same
25
33 /// location.</summary>
26
34 /// <param><c>p1</c> is the first Point to be compared.</param>
27
35 /// <param><c>p2</c> is the second Point to be compared.</param>
28
36 /// <returns>True if the Points do not have the same location and the
29
37 /// exact same type; otherwise, false.</returns>
30
38 /// <seealso cref="Equals"/>
43
31 /// <summary>This is the entry point of the Point class testing
39
44 /// program.
40
45 /// <para>This program tests each method and operator, and
41
46 /// is intended to be run after any non-trvial maintenance has
42
47 /// been performed on the Point class.</para></summary>

48 public static void Main() {

49
50
849412 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
51
52
850 Apéndice A Comentarios de documentación

1A.4.2 XML resultante


2Éste es el resultado producido por un generador de documentación habiéndose proporcionado al mismo el
3código fuente para la clase Po in t, como se mostró anteriormente:

4 <?xml version="1.0"?>

5 <doc>

6 <assembly>

7 <name>Point</name>

8 </assembly>

9 <members>

10 <member name="T:Graphics.Point">
15 <member name="F:Graphics.Point.x">
11
16 <summary>Instance variable <c>x</c> represents the point's
12
17 x-coordinate.</summary>
13
19 <member name="F:Graphics.Point.y">
18
14
20 <summary>Instance variable <c>y</c> represents the point's

21 y-coordinate.</summary>
23 <member name="M:Graphics.Point.#ctor">
22
24 <summary>This constructor initializes the new Point to

25 (0,0).</summary>
27 <member name="M:Graphics.Point.#ctor(System.Int32,System.Int32)">
26
28 <summary>This constructor initializes the new Point to

29 (<paramref name="xor"/>,<paramref name="yor"/>).</summary>

30 <param><c>xor</c> is the new Point's x-coordinate.</param>


33
31 <member name="M:Graphics.Point.Move(System.Int32,System.Int32)">

34
32 <summary>This method changes the point's location to

35 the given coordinates.</summary>

36 <param><c>xor</c> is the new x-coordinate.</param>

37 <param><c>yor</c> is the new y-coordinate.</param>

38
39
40

851Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 413


852Especificación del lenguaje C#

1 <member

2 name="M:Graphics.Point.Translate(System.Int32,System.Int32)">

3 <summary>This method changes the point's location by

4 the given x- and y-offsets.

5 <example>For example:

6 <code>

7 Point p = new Point(3,5);

8 p.Translate(-1,3);

9 </code>

10 results in <c>p</c>'s having the value (2,8).


17 <member name="M:Graphics.Point.Equals(System.Object)">
11
18 <summary>This method determines whether two Points have the same
12
19 location.</summary>
13
20 <param><c>o</c> is the object to be compared to the current
14
21 object.
15
22 </param>
16
23 <returns>True if the Points have the same location and they have

24 the exact same type; otherwise, false.</returns>


30
25 <member name="M:Graphics.Point.ToString">

31
26 <summary>Report a point's location as a string.</summary>

32
27 <returns>A string representing a point's location, in the form

33
28 (x,y),
36
34 <member
29
37
35 name="M:Graphics.Point.op_Equality(Graphics.Point,Graphics.Point)">

38 <summary>This operator determines whether two Points have the

39 same

40 location.</summary>

41 <param><c>p1</c> is the first Point to be compared.</param>

42 <param><c>p2</c> is the second Point to be compared.</param>

43 <returns>True if the Points have the same location and they have

44
45
46
47
48414
853 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.
854 Apéndice A Comentarios de documentación

1 <member

2 name="M:Graphics.Point.op_Inequality(Graphics.Point,Graphics.Point)">

3 <summary>This operator determines whether two Points have the

4 same

5 location.</summary>

6 <param><c>p1</c> is the first Point to be compared.</param>

7 <param><c>p2</c> is the second Point to be compared.</param>

8 <returns>True if the Points do not have the same location and

9 the
15 <member name="M:Graphics.Point.Main">
10
16 <summary>This is the entry point of the Point class testing
11
17 program.
12
18 <para>This program tests each method and operator, and
13
19 is intended to be run after any non-trvial maintenance has
22
14 <member name="P:Graphics.Point.X">
20
23 <value>Property <c>X</c> represents the point's
21
24 x-coordinate.</value>
26 <member name="P:Graphics.Point.Y">
25
27 <value>Property <c>Y</c> represents the point's

28 y-coordinate.</value>

29 </member>

30
31

855Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 415


856Especificación del lenguaje C#

1 B.Gramática

2Este apéndice contiene resúmenes de las gramáticas léxicas y sintácticas que pertenecen al documento principal,
3así como de las extensiones de gramática del contexto no seguro. Las construcciones de gramática aparecen aquí
4en el mismo orden en que aparecen en el documento principal.

5B.1 Gramática léxica


6 input:
7 input-sectionopt
8 input-section:
9 input-section-part
10 input-section input-section-part
11 input-section-part:
12 input-elementsopt new-line
13 pp-directive
14 input-elements:
15 input-element
16 input-elements input-element
17 input-element:
18 whitespace
19 comment
20 token

21B.1.1 Terminadores de línea


22 new-line:
23 Carácter de retorno de carro (U+000D )
24 Carácter de salto de línea (U+000A )
25 Carácter de retorno de carro (U+000D) seguido de un carácter de salto de línea (U+000A)
26 Carácter de línea siguiente (U+0085)
27 Carácter separador de línea (U+2028)
28 Carácter separador de párrafo (U+2029)

29B.1.2 Espacio en blanco


30 whitespace:
31 Cualquier carácter con clase Unicode Zs
32 Carácter de tabulación horizontal (U+0009)
33 Carácter de tabulación vertical (U+000B)
34 Carácter de avance de página (U+000C)

35B.1.3 Comentarios
36 comment:
37 single-line-comment
38 delimited-comment

857416 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


858 Apéndice C Referencias

1 single-line-comment:
2 / / input-charactersopt
3 input-characters:
4 input-character
5 input-characters input-character
6 input-character:
7 Cualquier carácter Unicode salvo un carácter de nueva línea (new-line-character)
8 new-line-character:
9 Carácter de retorno de carro (U+000D )
10 Carácter de salto de línea (U+000A )
11 Carácter de línea siguiente (U+0085)
12 Carácter separador de línea (U+2028)
13 Carácter separador de párrafo (U+2029)
14 delimited-comment::
15 / * delimited-comment-textopt asterisks /
16 delimited-comment-text::
17 delimited-comment-section
18 delimited-comment-text delimited-comment-section
19 delimited-comment-section::
20 not-asterisk
21 asterisks not-slash
22 asterisks::
23 *
24 asterisks *
25 not-asterisk:
26 Cualquier carácter Unicode excepto *
27 not-slash:
28 Cualquier carácter Unicode excepto /

29B.1.4 Símbolos
30 token:
31 identifier
32 keyword
33 integer-literal
34 real-literal
35 character-literal
36 string-literal
37 operator-or-punctuator

38B.1.5 Secuencias de escape de caracteres Unicode


39 unicode-escape-sequence:
40 \u hex-digit hex-digit hex-digit hex-digit
41 \U hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit hex-digit

859Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 417


860Especificación del lenguaje C#

1B.1.6 Identifiers
2 identifier:
3 available-identifier
4 @ identifier-or-keyword
5 available-identifier:
6 Un elemento identifier-or-keyword que no sea keyword
7 identifier-or-keyword:
8 identifier-start-character identifier-part-charactersopt
9 identifier-start-character:
10 letter-character
11 _ (carácter de subrayado U+005F )
12 identifier-part-characters:
13 identifier-part-character
14 identifier-part-characters identifier-part-character
15 identifier-part-character:
16 letter-character
17 decimal-digit-character
18 connecting-character
19 combining-character
20 formatting-character
21 letter-character:
22 Un carácter Unicode de clases Lu, Ll, Lt, Lm, Lo, o Nl
23 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de clases
24 Lu, Ll, Lt, Lm, Lo o Nl
25 combining-character:
26 Un carácter Unicode de clases Mn o Mc
27 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de clases
28 Mn o Mc
29 decimal-digit-character:
30 Un carácter Unicode de la clase Nd
31 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de la clase
32 Nd
33 connecting-character:
34 Un carácter Unicode de la clase Pc
35 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de la clase
36 Pc
37 formatting-character:
38 Un carácter Unicode de la clase Cf
39 Una secuencia de escape Unicode (unicode-escape-sequence) que representa un carácter de la clase
40 Cf

861418 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


862 Apéndice C Referencias

1B.1.7 Palabras clave


2 keyword: una de las siguientes
3 abs t rac t as base boo l break
4 byte case ca tch char checked
5 c lass cons t cont inue dec ima l de fau l t
6 de legate do doub le e l se enum
7 event exp l i c i t exte rn f a l se f i na l l y
8 f i xed f l oa t fo r f o reach goto
9 if imp l i c i t in int i n te r face
10 i n te rna l is l ock l ong namespace
11 new nu l l ob jec t opera to r out
12 ove r r i de params pr i va te pro tec tedpub l i c
13 readon ly re f re tu rn sby te sea led
14 shor t s i zeo f s tacka l l oc
s ta t i c s t r i ng
15 s t ruc t sw i t ch th i s th row t rue
16 t ry t ypeo f u in t u long unchecked
17 unsa fe ushor t us ing v i r tua l vo id
18 vo la t i l e whi l e

19B.1.8 Literales
20 literal:
21 boolean-literal
22 integer-literal
23 real-literal
24 character-literal
25 string-literal
26 null-literal
27 boolean-literal:
28 t rue
29 f a l se
30 integer-literal:
31 decimal-integer-literal
32 hexadecimal-integer-literal
33 decimal-integer-literal:
34 decimal-digits integer-type-suffixopt
35 decimal-digits:
36 decimal-digit
37 decimal-digits decimal-digit
38 decimal-digit: uno de los siguientes
39 0 1 2 3 4 5 6 7 8 9
40 integer-type-suffix: uno de los siguientes
41 U u L l UL Ul uL ul LU Lu lU lu
42 hexadecimal-integer-literal:
43 0x hex-digits integer-type-suffixopt
44 0X hex-digits integer-type-suffixopt

863Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 419


864Especificación del lenguaje C#

1 hex-digits:
2 hex-digit
3 hex-digits hex-digit
4 hex-digit: uno de los siguientes
5 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f
6 real-literal:
7 decimal-digits . decimal-digits exponent-partopt real-type-suffixopt
8 . decimal-digits exponent-partopt real-type-suffixopt
9 decimal-digits exponent-part real-type-suffixopt
10 decimal-digits real-type-suffix
11 exponent-part:
12 e signopt decimal-digits
13 E signopt decimal-digits
14 sign: uno de los siguientes
15 + -
16 real-type-suffix: uno de los siguientes
17 F f D d M m
18 character-literal:
19 ' character '
20 character:
21 single-character
22 simple-escape-sequence
23 hexadecimal-escape-sequence
24 unicode-escape-sequence
25 single-character:
26 Cualquier carácter excepto ' (U+0027 ), \ (U+005C), y carácter de nueva línea (new-line-
27 character)
28 simple-escape-sequence: una de las siguientes
29 \' \" \\ \0 \a \b \f \n \r \t \v
30 hexadecimal-escape-sequence:
31 \x hex-digit hex-digitopt hex-digitopt hex-digitopt
32 string-literal:
33 regular-string-literal
34 verbatim-string-literal
35 regular-string-literal:
36 " regular-string-literal-charactersopt "
37 regular-string-literal-characters:
38 regular-string-literal-character
39 regular-string-literal-characters regular-string-literal-character
40 regular-string-literal-character:
41 single-regular-string-literal-character
42 simple-escape-sequence
43 hexadecimal-escape-sequence
44 unicode-escape-sequence

865420 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


866 Apéndice C Referencias

1 single-regular-string-literal-character:
2 Cualquier carácter excepto " (U+0022 ), \ (U+005C), y un carácter de nueva línea (new-line-
3 character)
4 verbatim-string-literal:
5 @" verbatim -string-literal-charactersopt "
6 verbatim-string-literal-characters:
7 verbatim-string-literal-character
8 verbatim-string-literal-characters verbatim-string-literal-character
9 verbatim-string-literal-character:
10 single-verbatim-string-literal-character
11 quote-escape-sequence
12 single-verbatim-string-literal-character:
13 cualquier carácter excepto "
14 quote-escape-sequence:
15 ""
16 null-literal:
17 null

18B.1.9 Operadores y signos de puntuación


19 operator-or-punctuator: uno de los siguientes
20 { } [ ] ( ) . , : ;
21 + - * / % & | ^ ! ~
22 = < > ? ++ -- && || << >>
23 == != <= >= += -= *= /= %= &=
24 |= ^= <<= >>= ->

25B.1.10 Directivas de preprocesamiento


26 pp-directive:
27 pp-declaration
28 pp-conditional
29 pp-line
30 pp-diagnostic
31 pp-region
32 pp-new-line:
33 whitespaceopt single-line-commentopt new-line
34 conditional-symbol:
35 Cualquier elemento identifier-or-keyword excepto true o false
36 pp-expression:
37 whitespaceopt pp-or-expression whitespaceopt
38 pp-or-expression:
39 pp-and-expression
40 pp-or-expression whitespaceopt || whitespaceopt pp-and-expression
41 pp-and-expression:
42 pp-equality-expression
43 pp-and-expression whitespaceopt && whitespaceopt pp-equality-expression

867Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 421


868Especificación del lenguaje C#

1 pp-equality-expression:
2 pp-unary-expression
3 pp-equality-expression whitespaceopt == whitespaceopt pp-unary-expression
4 pp-equality-expression whitespaceopt != whitespaceopt pp-unary-expression
5 pp-unary-expression:
6 pp-primary-expression
7 ! whitespaceopt pp-unary-expression
8 pp-primary-expression:
9 t rue
10 f a l se
11 conditional-symbol
12 ( whitespaceopt pp-expression whitespaceopt )
13 pp-declaration:
14 whitespaceopt # whitespaceopt define whitespace conditional-symbol pp-new-line
15 whitespaceopt # whitespaceopt undef whitespace conditional-symbol pp-new-line
16 pp-conditional:
17 pp-if-section pp-elif-sectionsopt pp-else-sectionopt pp-endif
18 pp-if-section:
19 whitespaceopt # whitespaceopt if whitespace pp-expression pp-new-line conditional-
20 sectionopt
21 pp-elif-sections:
22 pp-elif-section
23 pp-elif-sections pp-elif-section
24 pp-elif-section:
25 whitespaceopt # whitespaceopt elif whitespace pp-expression pp-new-line conditional-
26 sectionopt
27 pp-else-section:
28 whitespaceopt # whitespaceopt else pp-new-line conditional-sectionopt
29 pp-endif:
30 whitespaceopt # whitespaceopt endif pp-new-line
31 conditional-section:
32 input-section
33 skipped-section
34 skipped-section:
35 skipped-section-part
36 skipped-section skipped-section-part
37 skipped-section-part:
38 skipped-charactersopt new-line
39 pp-directive
40 skipped-characters:
41 whitespaceopt not-number-sign input-charactersopt
42 not-number-sign:
43 Cualquier carácter de entrada (input-character) excepto #

869422 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


870 Apéndice C Referencias

1 pp-line:
2 whitespaceopt # whitespaceopt l i ne whitespace line-indicator pp-new-line
3 line-indicator:
4 decimal-digits whitespace file-name
5 decimal-digits
6 de fau l t
7 file-name:
8 " file-name-characters "
9 file-name-characters:
10 file-name-character
11 file-name-characters file-name-character
12 file-name-character:
13 Cualquier carácter de entrada (input-character) excepto "
14 pp-diagnostic:
15 whitespaceopt # whitespaceopt er ro r pp-message
16 whitespaceopt # whitespaceopt warning pp-message
17 pp-message:
18 new-line
19 whitespace input-charactersopt new-line
20 pp-region:
21 pp-start-region conditional-sectionopt pp-end-region
22 pp-start-region:
23 whitespaceopt # whitespaceopt region pp-message
24 pp-end-region:
25 whitespaceopt # whitespaceopt endregion pp-message

26B.2 Gramática sintáctica

27B.2.1 Conceptos básicos


28 namespace-name:
29 namespace-or-type-name
30 type-name:
31 namespace-or-type-name
32 namespace-or-type-name:
33 identifier
34 namespace-or-type-name . identifier

35B.2.2 Tipos
36 type:
37 value-type
38 reference-type
39 value-type:
40 struct-type
41 enum-type

871Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 423


872Especificación del lenguaje C#

1 struct-type:
2 type-name
3 simple-type
4 simple-type:
5 numeric-type
6 boo l
7 numeric-type:
8 integral-type
9 floating-point-type
10 dec ima l
11 integral-type:
12 sby te
13 byte
14 shor t
15 ushor t
16 int
17 u in t
18 l ong
19 u long
20 char
21 floating-point-type:
22 f l oa t
23 doub le
24 enum-type:
25 type-name
26 reference-type:
27 class-type
28 interface-type
29 array-type
30 delegate-type
31 class-type:
32 type-name
33 ob jec t
34 s t r i ng
35 interface-type:
36 type-name
37 array-type:
38 non-array-type rank-specifiers
39 non-array-type:
40 type
41 rank-specifiers:
42 rank-specifier
43 rank-specifiers rank-specifier
44 rank-specifier:
45 [ dim-separatorsopt ]

873424 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


874 Apéndice C Referencias

1 dim-separators:
2 ,
3 dim-separators ,
4 delegate-type:
5 type-name

6B.2.3 Variables
7 variable-reference:
8 expression

9B.2.4 Expresiones
10 argument-list:
11 argument
12 argument-list , argument
13 argument:
14 expression
15 re f variable-reference
16 out variable-reference
17 primary-expression:
18 primary-no-array-creation-expression
19 array-creation-expression
20 primary-no-array-creation-expression:
21 literal
22 simple-name
23 parenthesized-expression
24 member-access
25 invocation-expression
26 element-access
27 this-access
28 base-access
29 post-increment-expression
30 post-decrement-expression
31 object-creation-expression
32 delegate-creation-expression
33 typeof-expression
34 checked-expression
35 unchecked-expression
36 simple-name:
37 identifier
38 parenthesized-expression:
39 ( expression )
40 member-access:
41 primary-expression . identifier
42 predefined-type . identifier
43 predefined-type: uno de
44 boo l byte char dec ima l doub lef l oa t int l ong
45 ob jec t sby te shor t s t r i ngu in t u long ushor t

875Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 425


876Especificación del lenguaje C#

1 invocation-expression:
2 primary-expression ( argument-listopt )
3 element-access:
4 primary-no-array-creation-expression [ expression-list ]
5 expression-list:
6 expression
7 expression-list , expression
8 this-access:
9 th i s
10 base-access:
11 base . identifier
12 base [ expression-list ]
13 post-increment-expression:
14 primary-expression ++
15 post-decrement-expression:
16 primary-expression --
17 object-creation-expression:
18 new type ( argument-listopt )
19 array-creation-expression:
20 new non-array-type [ expression-list ] rank-specifiersopt array-initializeropt
21 new array-type array-initializer
22 delegate-creation-expression:
23 new delegate-type ( expression )
24 typeof-expression:
25 typeof ( type )
26 typeof ( void )
27 checked-expression:
28 checked ( expression )
29 unchecked-expression:
30 unchecked ( expression )
31 unary-expression:
32 primary-expression
33 + unary-expression
34 - unary-expression
35 ! unary-expression
36 ~ unary-expression
37 pre-increment-expression
38 pre-decrement-expression
39 cast-expression
40 pre-increment-expression:
41 ++ unary-expression
42 pre-decrement-expression:
43 -- unary-expression

877426 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


878 Apéndice C Referencias

1 cast-expression:
2 ( type ) unary-expression
3 multiplicative-expression:
4 unary-expression
5 multiplicative-expression * unary-expression
6 multiplicative-expression / unary-expression
7 multiplicative-expression % unary-expression
8 additive-expression:
9 multiplicative-expression
10 additive-expression + multiplicative-expression
11 additive-expression – multiplicative-expression
12 shift-expression:
13 additive-expression
14 shift-expression << additive-expression
15 shift-expression >> additive-expression
16 relational-expression:
17 shift-expression
18 relational-expression < shift-expression
19 relational-expression > shift-expression
20 relational-expression <= shift-expression
21 relational-expression >= shift-expression
22 relational-expression is type
23 relational-expression as type
24 equality-expression:
25 relational-expression
26 equality-expression == relational-expression
27 equality-expression != relational-expression
28 and-expression:
29 equality-expression
30 and-expression & equality-expression
31 exclusive-or-expression:
32 and-expression
33 exclusive-or-expression ^ and-expression
34 inclusive-or-expression:
35 exclusive-or-expression
36 inclusive-or-expression | exclusive-or-expression
37 conditional-and-expression:
38 inclusive-or-expression
39 conditional-and-expression && inclusive-or-expression
40 conditional-or-expression:
41 conditional-and-expression
42 conditional-or-expression || conditional-and-expression
43 conditional-expression:
44 conditional-or-expression
45 conditional-or-expression ? expression : expression

879Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 427


880Especificación del lenguaje C#

1 assignment:
2 unary-expression assignment-operator expression
3 assignment-operator: uno de
4 = += -= *= /= %= &= |= ^= <<= >>=
5 expression:
6 conditional-expression
7 assignment
8 constant-expression:
9 expression
10 boolean-expression:
11 expression

12B.2.5 Instrucciones
13 statement:
14 labeled-statement
15 declaration-statement
16 embedded-statement
17 embedded-statement:
18 block
19 empty-statement
20 expression-statement
21 selection-statement
22 iteration-statement
23 jump-statement
24 try-statement
25 checked-statement
26 unchecked-statement
27 lock-statement
28 using-statement
29 block:
30 { statement-listopt }
31 statement-list:
32 statement
33 statement-list statement
34 empty-statement:
35 ;
36 labeled-statement:
37 identifier : statement
38 declaration-statement:
39 local-variable-declaration ;
40 local-constant-declaration ;
41 local-variable-declaration:
42 type local-variable-declarators
43 local-variable-declarators:
44 local-variable-declarator
45 local-variable-declarators , local-variable-declarator

881428 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


882 Apéndice C Referencias

1 local-variable-declarator:
2 identifier
3 identifier = local-variable-initializer
4 local-variable-initializer:
5 expression
6 array-initializer
7 local-constant-declaration:
8 cons t type constant-declarators
9 constant-declarators:
10 constant-declarator
11 constant-declarators , constant-declarator
12 constant-declarator:
13 identifier = constant-expression
14 expression-statement:
15 statement-expression ;
16 statement-expression:
17 invocation-expression
18 object-creation-expression
19 assignment
20 post-increment-expression
21 post-decrement-expression
22 pre-increment-expression
23 pre-decrement-expression
24 selection-statement:
25 if-statement
26 switch-statement
27 if-statement:
28 i f ( boolean-expression ) embedded-statement
29 if ( boolean-expression ) embedded-statement else embedded-statement
30 boolean-expression:
31 expression
32 switch-statement:
33 switch ( expression ) switch-block
34 switch-block:
35 { switch-sectionsopt }
36 switch-sections:
37 switch-section
38 switch-sections switch-section
39 switch-section:
40 switch-labels statement-list
41 switch-labels:
42 switch-label
43 switch-labels switch-label

883Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 429


884Especificación del lenguaje C#

1 switch-label:
2 case constant-expression :
3 default :
4 iteration-statement:
5 while-statement
6 do-statement
7 for-statement
8 foreach-statement
9 while-statement:
10 while ( boolean-expression ) embedded-statement
11 do-statement:
12 do embedded-statement while ( boolean-expression ) ;
13 for-statement:
14 for ( for-initializeropt ; for-conditionopt ; for-iteratoropt ) embedded-statement
15 for-initializer:
16 local-variable-declaration
17 statement-expression-list
18 for-condition:
19 boolean-expression
20 for-iterator:
21 statement-expression-list
22 statement-expression-list:
23 statement-expression
24 statement-expression-list , statement-expression
25 foreach-statement:
26 foreach ( type identifier in expression ) embedded-statement
27 jump-statement:
28 break-statement
29 continue-statement
30 goto-statement
31 return-statement
32 throw-statement
33 break-statement:
34 break ;
35 continue-statement:
36 continue ;
37 goto-statement:
38 goto identifier ;
39 goto case constant-expression ;
40 goto default ;
41 return-statement:
42 return expressionopt ;
43 throw-statement:
44 throw expressionopt ;

885430 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


886 Apéndice C Referencias

1 try-statement:
2 t r y block catch-clauses
3 t r y block finally-clause
4 try block catch-clauses finally-clause
5 catch-clauses:
6 specific-catch-clauses general-catch-clauseopt
7 specific-catch-clausesopt general-catch-clause
8 specific-catch-clauses:
9 specific-catch-clause
10 specific-catch-clauses specific-catch-clause
11 specific-catch-clause:
12 catch ( class-type identifieropt ) block
13 general-catch-clause:
14 catch block
15 finally-clause:
16 finally block
17 checked-statement:
18 checked block
19 unchecked-statement:
20 unchecked block
21 lock-statement:
22 lock ( expression ) embedded-statement
23 using-statement:
24 using ( resource-acquisition ) embedded-statement
25 resource-acquisition:
26 local-variable-declaration
27 expression

28B.2.6 Espacios de nombres


29 compilation-unit:
30 using-directivesopt global-attributesopt namespace-member-declarationsopt
31 namespace-declaration:
32 namespace qualified-identifier namespace-body ;opt
33 qualified-identifier:
34 identifier
35 qualified-identifier . identifier
36 namespace-body:
37 { using-directivesopt namespace-member-declarationsopt }
38 using-directives:
39 using-directive
40 using-directives using-directive
41 using-directive:
42 using-alias-directive
43 using-namespace-directive

887Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 431


888Especificación del lenguaje C#

1 using-alias-directive:
2 us ing identifier = namespace-or-type-name ;
3 using-namespace-directive:
4 using namespace-name ;
5 namespace-member-declarations:
6 namespace-member-declaration
7 namespace-member-declarations namespace-member-declaration
8 namespace-member-declaration:
9 namespace-declaration
10 type-declaration
11 type-declaration:
12 class-declaration
13 struct-declaration
14 interface-declaration
15 enum-declaration
16 delegate-declaration

17B.2.7 Clases
18 class-declaration:
19 attributesopt class-modifiersopt class identifier class-baseopt class-body ;opt
20 class-modifiers:
21 class-modifier
22 class-modifiers class-modifier
23 class-modifier:
24 new
25 public
26 protected
27 internal
28 private
29 abstract
30 sealed
31 class-base:
32 : class-type
33 : interface-type-list
34 : class-type , interface-type-list
35 interface-type-list:
36 interface-type
37 interface-type-list , interface-type
38 class-body:
39 { class-member-declarationsopt }
40 class-member-declarations:
41 class-member-declaration
42 class-member-declarations class-member-declaration

889432 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


890 Apéndice C Referencias

1 class-member-declaration:
2 constant-declaration
3 field-declaration
4 method-declaration
5 property-declaration
6 event-declaration
7 indexer-declaration
8 operator-declaration
9 constructor-declaration
10 destructor-declaration
11 static-constructor-declaration
12 type-declaration
13 constant-declaration:
14 attributesopt constant-modifiersopt cons t type constant-declarators ;
15 constant-modifiers:
16 constant-modifier
17 constant-modifiers constant-modifier
18 constant-modifier:
19 new
20 pub l i c
21 protected
22 internal
23 private
24 constant-declarators:
25 constant-declarator
26 constant-declarators , constant-declarator
27 constant-declarator:
28 identifier = constant-expression
29 field-declaration:
30 attributesopt field-modifiersopt type variable-declarators ;
31 field-modifiers:
32 field-modifier
33 field-modifiers field-modifier
34 field-modifier:
35 new
36 public
37 protected
38 internal
39 private
40 static
41 readonly
42 volatile
43 variable-declarators:
44 variable-declarator
45 variable-declarators , variable-declarator

891Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 433


892Especificación del lenguaje C#

1 variable-declarator:
2 identifier
3 identifier = variable-initializer
4 variable-initializer:
5 expression
6 array-initializer
7 method-declaration:
8 method-header method-body
9 method-header:
10 attributesopt method-modifiersopt return-type member-name ( formal-parameter-listopt )
11 method-modifiers:
12 method-modifier
13 method-modifiers method-modifier
14 method-modifier:
15 new
16 pub l i c
17 pro tec ted
18 i n te rna l
19 pr i va te
20 s ta t i c
21 v i r tua l
22 sea led
23 ove r r i de
24 abs t rac t
25 exte rn
26 return-type:
27 type
28 vo id
29 member-name:
30 identifier
31 interface-type . identifier
32 method-body:
33 block
34 ;
35 formal-parameter-list:
36 fixed-parameters
37 fixed-parameters , parameter-array
38 parameter-array
39 fixed-parameters:
40 fixed-parameter
41 fixed-parameters , fixed-parameter
42 fixed-parameter:
43 attributesopt parameter-modifieropt type identifier
44 parameter-modifier:
45 re f
46 out

893434 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


894 Apéndice C Referencias

1 parameter-array:
2 attributesopt params array-type identifier
3 property-declaration:
4 attributesopt property-modifiersopt type member-name { accessor-declarations }
5 property-modifiers:
6 property-modifier
7 property-modifiers property-modifier
8 property-modifier:
9 new
10 pub l i c
11 pro tec ted
12 i n te rna l
13 pr i va te
14 s ta t i c
15 v i r tua l
16 sea led
17 ove r r i de
18 abs t rac t
19 exte rn
20 member-name:
21 identifier
22 interface-type . identifier
23 accessor-declarations:
24 get-accessor-declaration set-accessor-declarationopt
25 set-accessor-declaration get-accessor-declarationopt
26 get-accessor-declaration:
27 attributesopt get accessor-body
28 set-accessor-declaration:
29 attributesopt se t accessor-body
30 accessor-body:
31 block
32 ;
33 event-declaration:
34 attributesopt event-modifiersopt event type variable-declarators ;
35 attributesopt event-modifiersopt event type member-name { event-accessor-declarations }
36 event-modifiers:
37 event-modifier
38 event-modifiers event-modifier

895Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 435


896Especificación del lenguaje C#

1 event-modifier:
2 new
3 pub l i c
4 pro tec ted
5 i n te rna l
6 pr i va te
7 s ta t i c
8 v i r tua l
9 sea led
10 ove r r i de
11 abs t rac t
12 exte rn
13 event-accessor-declarations:
14 add-accessor-declaration remove-accessor-declaration
15 remove-accessor-declaration add-accessor-declaration
16 add-accessor-declaration:
17 attributesopt add block
18 remove-accessor-declaration:
19 attributesopt remove block
20 indexer-declaration:
21 attributesopt indexer-modifiersopt indexer-declarator { accessor-declarations }
22 indexer-modifiers:
23 indexer-modifier
24 indexer-modifiers indexer-modifier
25 indexer-modifier:
26 new
27 pub l i c
28 pro tec ted
29 i n te rna l
30 pr i va te
31 v i r tua l
32 sea led
33 ove r r i de
34 abs t rac t
35 exte rn
36 indexer-declarator:
37 type th i s [ formal-parameter-list ]
38 type interface-type . this [ formal-parameter-list ]
39 operator-declaration:
40 attributesopt operator-modifiers operator-declarator operator-body
41 operator-modifiers:
42 operator-modifier
43 operator-modifiers operator-modifier
44 operator-modifier:
45 public
46 static
47 extern

897436 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


898 Apéndice C Referencias

1 operator-declarator:
2 unary-operator-declarator
3 binary-operator-declarator
4 conversion-operator-declarator
5 unary-operator-declarator:
6 type opera to r overloadable-unary-operator ( type identifier )
7 overloadable-unary-operator: uno de
8 + - ! ~ ++ -- true false
9 binary-operator-declarator:
10 type operator overloadable-binary-operator ( type identifier , type identifier )
11 overloadable-binary-operator: uno de
12 + - * / % & | ^ << >> == != > < >= <=
13 conversion-operator-declarator:
14 implicit operator type ( type identifier )
15 explicit operator type ( type identifier )
16 operator-body:
17 block
18 ;
19 constructor-declaration:
20 attributesopt constructor-modifiersopt constructor-declarator constructor-body
21 constructor-modifiers:
22 constructor-modifier
23 constructor-modifiers constructor-modifier
24 constructor-modifier:
25 public
26 protected
27 internal
28 private
29 extern
30 constructor-declarator:
31 identifier ( formal-parameter-listopt ) constructor-initializeropt
32 constructor-initializer:
33 : base ( argument-listopt )
34 : this ( argument-listopt )
35 constructor-body:
36 block
37 ;
38 static-constructor-declaration:
39 attributesopt static-constructor-modifiers identifier ( ) static-constructor-body
40 static-constructor-modifiers:
41 externopt static
42 static externopt

899Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 437


900Especificación del lenguaje C#

1 static-constructor-body:
2 block
3 ;
4 destructor-declaration:
5 attributesopt ex te rnopt ~ identifier ( ) destructor-body
6 destructor-body:
7 block
8 ;

9B.2.8 Estructuras
10 struct-declaration:
11 attributesopt struct-modifiersopt struct identifier struct-interfacesopt struct-body ;opt
12 struct-modifiers:
13 struct-modifier
14 struct-modifiers struct-modifier
15 struct-modifier:
16 new
17 public
18 protected
19 internal
20 private
21 struct-interfaces:
22 : interface-type-list
23 struct-body:
24 { struct-member-declarationsopt }
25 struct-member-declarations:
26 struct-member-declaration
27 struct-member-declarations struct-member-declaration
28 struct-member-declaration:
29 constant-declaration
30 field-declaration
31 method-declaration
32 property-declaration
33 event-declaration
34 indexer-declaration
35 operator-declaration
36 constructor-declaration
37 static-constructor-declaration
38 type-declaration

39B.2.9 Matrices
40 array-type:
41 non-array-type rank-specifiers
42 non-array-type:
43 type

901438 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


902 Apéndice C Referencias

1 rank-specifiers:
2 rank-specifier
3 rank-specifiers rank-specifier
4 rank-specifier:
5 [ dim-separatorsopt ]
6 dim-separators:
7 ,
8 dim-separators ,
9 array-initializer:
10 { variable-initializer-listopt }
11 { variable-initializer-list , }
12 variable-initializer-list:
13 variable-initializer
14 variable-initializer-list , variable-initializer
15 variable-initializer:
16 expression
17 array-initializer

18B.2.10 Interfaces
19 interface-declaration:
20 attributesopt interface-modifiersopt interface identifier interface-baseopt interface-body ;opt
21 interface-modifiers:
22 interface-modifier
23 interface-modifiers interface-modifier
24 interface-modifier:
25 new
26 public
27 protected
28 internal
29 private
30 interface-base:
31 : interface-type-list
32 interface-body:
33 { interface-member-declarationsopt }
34 interface-member-declarations:
35 interface-member-declaration
36 interface-member-declarations interface-member-declaration
37 interface-member-declaration:
38 interface-method-declaration
39 interface-property-declaration
40 interface-event-declaration
41 interface-indexer-declaration
42 interface-method-declaration:
43 attributesopt newopt return-type identifier ( formal-parameter-listopt ) ;

903Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 439


904Especificación del lenguaje C#

1 interface-property-declaration:
2 attributesopt new opt type identifier { interface-accessors }
3 interface-accessors:
4 attributesopt get ;
5 attributesopt set ;
6 attributesopt get ; attributesopt set ;
7 attributesopt set ; attributesopt get ;
8 interface-event-declaration:
9 attributesopt newopt event type identifier ;
10 interface-indexer-declaration:
11 attributesopt newopt type this [ formal-parameter-list ] { interface-accessors }

12B.2.11 Enumeraciones
13 enum-declaration:
14 attributesopt enum-modifiersopt enum identifier enum-baseopt enum-body ;opt
15 enum-base:
16 : integral-type
17 enum-body:
18 { enum-member-declarationsopt }
19 { enum-member-declarations , }
20 enum-modifiers:
21 enum-modifier
22 enum-modifiers enum-modifier
23 enum-modifier:
24 new
25 public
26 protected
27 internal
28 private
29 enum-member-declarations:
30 enum-member-declaration
31 enum-member-declarations , enum-member-declaration
32 enum-member-declaration:
33 attributesopt identifier
34 attributesopt identifier = constant-expression

35B.2.12 Delegados
36 delegate-declaration:
37 attributesopt delegate-modifiersopt delegate return-type identifier
38 ( formal-parameter-listopt ) ;
39 delegate-modifiers:
40 delegate-modifier
41 delegate-modifiers delegate-modifier

905440 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


906 Apéndice C Referencias

1 delegate-modifier:
2 new
3 pub l i c
4 pro tec ted
5 i n te rna l
6 private

7B.2.13 Atributos
8 global-attributes:
9 global-attribute-sections
10 global-attribute-sections:
11 global-attribute-section
12 global-attribute-sections global-attribute-section
13 global-attribute-section:
14 [ global-attribute-target-specifier attribute-list ]
15 [ global-attribute-target-specifier attribute-list ,]
16 global-attribute-target-specifier:
17 global-attribute-target :
18 global-attribute-target:
19 assembly
20 module
21 attributes:
22 attribute-sections
23 attribute-sections:
24 attribute-section
25 attribute-sections attribute-section
26 attribute-section:
27 [ attribute-target-specifieropt attribute-list ]
28 [ attribute-target-specifieropt attribute-list , ]
29 attribute-target-specifier:
30 attribute-target :
31 attribute-target:
32 field
33 event
34 method
35 param
36 property
37 return
38 type
39 attribute-list:
40 attribute
41 attribute-list , attribute
42 attribute:
43 attribute-name attribute-argumentsopt

907Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 441


908Especificación del lenguaje C#

1 attribute-name:
2 type-name
3 attribute-arguments:
4 ( positional-argument-listopt )
5 ( positional-argument-list , named-argument-list )
6 ( named-argument-list )
7 positional-argument-list:
8 positional-argument
9 positional-argument-list , positional-argument
10 positional-argument:
11 attribute-argument-expression
12 named-argument-list:
13 named-argument
14 named-argument-list , named-argument
15 named-argument:
16 identifier = attribute-argument-expression
17 attribute-argument-expression:
18 expression

19B.3 Extensiones de la gramática para el código no seguro


20 class-modifier:
21 ...
22 unsa fe
23 struct-modifier:
24 ...
25 unsa fe
26 interface-modifier:
27 ...
28 unsa fe
29 delegate-modifier:
30 ...
31 unsa fe
32 field-modifier:
33 ...
34 unsa fe
35 method-modifier:
36 ...
37 unsa fe
38 property-modifier:
39 ...
40 unsa fe
41 event-modifier:
42 ...
43 unsa fe

909442 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


910 Apéndice C Referencias

1 indexer-modifier:
2 ...
3 unsa fe
4 operator-modifier:
5 ...
6 unsa fe
7 constructor-modifier:
8 ...
9 unsa fe
10 destructor-declaration:
11 attributesopt ex te rnopt unsa feopt ~ identifier ( ) destructor-body
12 attributesopt unsafeopt externopt ~ identifier ( ) destructor-body
13 static-constructor-modifiers:
14 externopt unsafeopt static
15 unsafeopt externopt static
16 externopt static unsafeopt
17 unsafeopt static externopt
18 static externopt unsafeopt
19 static unsafeopt externopt
20 embedded-statement:
21 ...
22 unsafe-statement
23 unsafe-statement:
24 unsafe block
25 type:
26 value-type
27 reference-type
28 pointer-type
29 pointer-type:
30 unmanaged-type *
31 void *
32 unmanaged-type:
33 type
34 primary-no-array-creation-expression:
35 ...
36 pointer-member-access
37 pointer-element-access
38 sizeof-expression
39 unary-expression:
40 ...
41 pointer-indirection-expression
42 addressof-expression
43 pointer-indirection-expression:
44 * unary-expression
45 pointer-member-access:
46 primary-expression -> identifier

911Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 443


912Especificación del lenguaje C#

1 pointer-element-access:
2 primary-no-array-creation-expression [ expression ]
3 addressof-expression:
4 & unary-expression
5 sizeof-expression:
6 s i zeo f ( unmanaged-type )
7 embedded-statement:
8 ...
9 fixed-statement
10 fixed-statement:
11 fixed ( pointer-type fixed-pointer-declarators ) embedded-statement
12 fixed-pointer-declarators:
13 fixed-pointer-declarator
14 fixed-pointer-declarators , fixed-pointer-declarator
15 fixed-pointer-declarator:
16 identifier = fixed-pointer-initializer
17 fixed-pointer-initializer:
18 & variable-reference
19 expression
20 local-variable-initializer:
21 expression
22 array-initializer
23 stackalloc-initializer
24 stackalloc-initializer:
25 stackalloc unmanaged-type [ expression ]

913444 Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos.


914 Apéndice C Referencias

1 C.Referencias

2Unicode Consortium. The Unicode Standard, Version 3.0. Addison-Wesley, Reading, Massachusetts (EE.UU.),
32000, ISBN 0-201-616335-5.
4IEEE. IEEE Standard for Binary Floating-Point Arithmetic. ANSI/IEEE Standard 754-1985. Disponible en
5http://www.ieee.org.
6ISO/IEC. C++. ANSI/ISO/IEC 14882:1998.

915Copyright  Microsoft Corporation 1999-2003. Reservados todos los derechos. 445

También podría gustarte