ClasesClasses

Una clase es una estructura de datos que puede contener miembros de datos (constantes y campos), miembros de función (métodos, propiedades, eventos, indizadores, operadores, constructores de instancias, destructores y constructores estáticos) y tipos anidados.A class is a data structure that may contain data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, destructors and static constructors), and nested types. Tipos de clase admiten la herencia, un mecanismo mediante el cual una clase derivada puede extender y especializar una clase base.Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

Declaraciones de claseClass declarations

Un class_declaration es un type_declaration (declaraciones de tipo) que declara una clase nueva.A class_declaration is a type_declaration (Type declarations) that declares a new class.

class_declaration
    : attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list?
      class_base? type_parameter_constraints_clause* class_body ';'?
    ;

Un class_declaration consta de un conjunto opcional de atributos (atributos), seguido de un conjunto opcional de class_modifiers (modificadores de clase), seguido de un elemento opcional partial modificador, seguido por la palabra clave class y un identificador que los nombres de la clase, seguida por un opcional type_parameter_list (parámetros de tipo), seguido de un elemento opcional class_base especificación (base clase especificación de), seguido de un conjunto opcional de type_parameter_constraints_clauses (restricciones de parámetro de tipo), seguido de un class_body (Cuerpo de la clase), seguido opcionalmente de un punto y coma.A class_declaration consists of an optional set of attributes (Attributes), followed by an optional set of class_modifiers (Class modifiers), followed by an optional partial modifier, followed by the keyword class and an identifier that names the class, followed by an optional type_parameter_list (Type parameters), followed by an optional class_base specification (Class base specification) , followed by an optional set of type_parameter_constraints_clauses (Type parameter constraints), followed by a class_body (Class body), optionally followed by a semicolon.

No se puede proporcionar una declaración de clase type_parameter_constraints_clauses a menos que también proporciona un type_parameter_list.A class declaration cannot supply type_parameter_constraints_clauses unless it also supplies a type_parameter_list.

Una declaración de clase que proporciona un type_parameter_list es un declaración de clase genérica.A class declaration that supplies a type_parameter_list is a generic class declaration. Además, cualquier clase anidada dentro de una declaración de clase genérica o una declaración de struct genérico es una declaración de clase genérica, puesto que se deben proporcionar parámetros de tipo para el tipo de contenedor para crear un tipo construido.Additionally, any class nested inside a generic class declaration or a generic struct declaration is itself a generic class declaration, since type parameters for the containing type must be supplied to create a constructed type.

Modificadores de claseClass modifiers

Un class_declaration puede incluir opcionalmente una secuencia de modificadores de clase:A class_declaration may optionally include a sequence of class modifiers:

class_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'abstract'
    | 'sealed'
    | 'static'
    | class_modifier_unsafe
    ;

Es un error en tiempo de compilación para el mismo modificador aparezca varias veces en una declaración de clase.It is a compile-time error for the same modifier to appear multiple times in a class declaration.

El new modificador está permitido en clases anidadas.The new modifier is permitted on nested classes. Especifica que la clase oculta un miembro heredado con el mismo nombre, como se describe en el nuevo modificador.It specifies that the class hides an inherited member by the same name, as described in The new modifier. Es un error en tiempo de compilación para el new modificador aparezca en una declaración de clase que no es una declaración de clase anidada.It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration.

El public, protected, internal, y private modificadores controlan el acceso de la clase.The public, protected, internal, and private modifiers control the accessibility of the class. Dependiendo del contexto en el que se produce la declaración de clase, algunos de estos modificadores pueden no estar permitidos (accesibilidad declarada).Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

El abstract, sealed y static modificadores se tratan en las secciones siguientes.The abstract, sealed and static modifiers are discussed in the following sections.

Clases abstractasAbstract classes

El abstract modificador se usa para indicar que una clase está incompleta y que está pensada para usarse solo como una clase base.The abstract modifier is used to indicate that a class is incomplete and that it is intended to be used only as a base class. Una clase abstracta difiere de una clase no abstracta de las maneras siguientes:An abstract class differs from a non-abstract class in the following ways:

  • Una clase abstracta no se pueden crear instancias directamente, y es un error en tiempo de compilación para usar el new operador en una clase abstracta.An abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class. Aunque es posible tener variables y valores cuyos tipos en tiempo de compilación son abstractos, tales variables y valores necesariamente será null o contendrán referencias a instancias de clases no abstractas derivadas de los tipos abstractos.While it is possible to have variables and values whose compile-time types are abstract, such variables and values will necessarily either be null or contain references to instances of non-abstract classes derived from the abstract types.
  • Una clase abstracta se permiten (aunque no es necesario) para contener miembros abstractos.An abstract class is permitted (but not required) to contain abstract members.
  • Una clase abstracta no puede estar sellada.An abstract class cannot be sealed.

Cuando una clase no abstracta se deriva de una clase abstracta, la clase no abstracta debe incluir implementaciones reales de todos los miembros abstractos heredados, anulando a esos miembros abstractos.When a non-abstract class is derived from an abstract class, the non-abstract class must include actual implementations of all inherited abstract members, thereby overriding those abstract members. En el ejemploIn the example

abstract class A
{
    public abstract void F();
}

abstract class B: A
{
    public void G() {}
}

class C: B
{
    public override void F() {
        // actual implementation of F
    }
}

la clase abstracta A presenta un método abstracto F.the abstract class A introduces an abstract method F. Clase B presenta un método adicional G, pero proporciona una implementación de F, B debe declararse como abstracta.Class B introduces an additional method G, but since it doesn't provide an implementation of F, B must also be declared abstract. Clase C invalida F y proporciona una implementación real.Class C overrides F and provides an actual implementation. Puesto que no hay ningún miembro abstracto en C, C se permitía (aunque no es necesario) para ser no abstracta.Since there are no abstract members in C, C is permitted (but not required) to be non-abstract.

Clases selladasSealed classes

El sealed modificador se usa para impedir la derivación de una clase.The sealed modifier is used to prevent derivation from a class. Si se especifica una clase sellada como clase base de otra clase, se produce un error de tiempo de compilación.A compile-time error occurs if a sealed class is specified as the base class of another class.

Una clase sellada no puede ser también una clase abstracta.A sealed class cannot also be an abstract class.

El sealed modificador se usa principalmente para evitar una derivación imprevista, pero también permite ciertas optimizaciones en tiempo de ejecución.The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. En concreto, dado que una clase sellada no puede tener clases derivadas, es posible transformar las invocaciones de miembros de función virtual en instancias de una clase sellada en llamadas no virtuales.In particular, because a sealed class is known to never have any derived classes, it is possible to transform virtual function member invocations on sealed class instances into non-virtual invocations.

Clases estáticasStatic classes

El static modificador se usa para marcar la clase que se declara como un clase estática.The static modifier is used to mark the class being declared as a static class. Una clase estática no pueden crearse instancias, no se puede usar como un tipo y puede contener a solo miembros estáticos.A static class cannot be instantiated, cannot be used as a type and can contain only static members. Sólo una clase estática puede contener declaraciones de métodos de extensión (métodos de extensión).Only a static class can contain declarations of extension methods (Extension methods).

Una declaración de clase estática está sujeto a las restricciones siguientes:A static class declaration is subject to the following restrictions:

  • Una clase estática no puede incluir un sealed o abstract modificador.A static class may not include a sealed or abstract modifier. Sin embargo, tenga en cuenta que puesto que no se crea una instancia o derivada de una clase estática, se comporta como si fuese sellada y abstracta.Note, however, that since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.
  • Una clase estática no puede incluir un class_base especificación (clase Especificación base) y no se puede especificar explícitamente una clase base o una lista de interfaces implementadas.A static class may not include a class_base specification (Class base specification) and cannot explicitly specify a base class or a list of implemented interfaces. Una clase estática hereda implícitamente de tipo object.A static class implicitly inherits from type object.
  • Una clase estática solo puede contener miembros estáticos (miembros estáticos y de instancia).A static class can only contain static members (Static and instance members). Tenga en cuenta que las constantes y tipos anidados se clasifican como miembros estáticos.Note that constants and nested types are classified as static members.
  • Una clase estática no puede tener miembros con protected o protected internal accesibilidad declarada.A static class cannot have members with protected or protected internal declared accessibility.

Es un error en tiempo de compilación que infringe cualquiera de estas restricciones.It is a compile-time error to violate any of these restrictions.

Una clase estática no tiene ningún constructor de instancia.A static class has no instance constructors. No es posible declarar un constructor de instancia en una clase estática y ningún constructor de instancia predeterminado (constructores predeterminados) se proporciona para una clase estática.It is not possible to declare an instance constructor in a static class, and no default instance constructor (Default constructors) is provided for a static class.

Los miembros de una clase estática no son automáticamente estáticos y las declaraciones de miembro deben incluir explícitamente una static modificador (excepto las constantes y tipos anidados).The members of a static class are not automatically static, and the member declarations must explicitly include a static modifier (except for constants and nested types). Cuando una clase se anida dentro de una clase estática externa, la clase anidada no es una clase estática a menos que incluya explícitamente un static modificador.When a class is nested within a static outer class, the nested class is not a static class unless it explicitly includes a static modifier.

Hacer referencia a tipos de clase estáticaReferencing static class types

Un namespace_or_type_name (Namespace y nombres de tipo) tiene permiso para hacer referencia a una clase estática siA namespace_or_type_name (Namespace and type names) is permitted to reference a static class if

  • El namespace_or_type_name es el T en un namespace_or_type_name del formulario T.I, oThe namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • El namespace_or_type_name es el T en un typeof_expression (listas de argumentos1) del formulario typeof(T).The namespace_or_type_name is the T in a typeof_expression (Argument lists1) of the form typeof(T).

Un primary_expression (miembros de función) tiene permiso para hacer referencia a una clase estática siA primary_expression (Function members) is permitted to reference a static class if

En cualquier otro contexto es un error en tiempo de compilación para hacer referencia a una clase estática.In any other context it is a compile-time error to reference a static class. Por ejemplo, es un error para una clase estática que se usará como una clase base, un tipo constituyente (tipos anidados) de un miembro, un argumento de tipo genérico o una restricción de parámetro de tipo.For example, it is an error for a static class to be used as a base class, a constituent type (Nested types) of a member, a generic type argument, or a type parameter constraint. Del mismo modo, no se puede usar una clase estática en un tipo de matriz, un tipo de puntero, un new expresión, una expresión de conversión, un is expresión, un as expresión, un sizeof expresión o una expresión de valor predeterminado.Likewise, a static class cannot be used in an array type, a pointer type, a new expression, a cast expression, an is expression, an as expression, a sizeof expression, or a default value expression.

Modificador parcialPartial modifier

El partial modificador se usa para indicar que este class_declaration es una declaración de tipo parcial.The partial modifier is used to indicate that this class_declaration is a partial type declaration. Combinan varias declaraciones parciales de tipos con el mismo nombre dentro de una declaración de espacio de nombres o tipo envolvente a la declaración de un tipo de formulario, siguiendo las reglas especificadas en tipos parciales.Multiple partial type declarations with the same name within an enclosing namespace or type declaration combine to form one type declaration, following the rules specified in Partial types.

Tener la declaración de una clase que se distribuye en segmentos independientes del texto del programa puede ser útil si son producidos o mantenerse en contextos diferentes estos segmentos.Having the declaration of a class distributed over separate segments of program text can be useful if these segments are produced or maintained in different contexts. Por ejemplo, una parte de una declaración de clase puede ser generan automáticamente, mientras que la otra se crea manualmente.For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. Separación textual de los dos impide las actualizaciones en uno respecto a entrar en conflicto con las actualizaciones de otros.Textual separation of the two prevents updates by one from conflicting with updates by the other.

Parámetros de tipoType parameters

Un parámetro de tipo es un identificador simple que denota un marcador de posición para un argumento de tipo proporcionado para crear un tipo construido.A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. Un parámetro de tipo es un marcador de posición formal para un tipo que se proporcionarán más adelante.A type parameter is a formal placeholder for a type that will be supplied later. Por el contrario, un argumento de tipo (argumentos de tipo) es el tipo real que se sustituye por el parámetro de tipo cuando se crea un tipo construido.By contrast, a type argument (Type arguments) is the actual type that is substituted for the type parameter when a constructed type is created.

type_parameter_list
    : '<' type_parameters '>'
    ;

type_parameters
    : attributes? type_parameter
    | type_parameters ',' attributes? type_parameter
    ;

type_parameter
    : identifier
    ;

Cada parámetro de tipo en una declaración de clase define un nombre en el espacio de declaración (declaraciones) de esa clase.Each type parameter in a class declaration defines a name in the declaration space (Declarations) of that class. Por lo tanto, no puede tener el mismo nombre que otro parámetro de tipo o un miembro declarado en esa clase.Thus, it cannot have the same name as another type parameter or a member declared in that class. Un parámetro de tipo no puede tener el mismo nombre que el propio tipo.A type parameter cannot have the same name as the type itself.

Especificación de clase baseClass base specification

Una declaración de clase puede incluir un class_base especificación, que define la clase base directa de la clase y las interfaces (Interfaces) directamente implementada por la clase.A class declaration may include a class_base specification, which defines the direct base class of the class and the interfaces (Interfaces) directly implemented by the class.

class_base
    : ':' class_type
    | ':' interface_type_list
    | ':' class_type ',' interface_type_list
    ;

interface_type_list
    : interface_type (',' interface_type)*
    ;

La clase base especificada en una declaración de clase puede ser un tipo de clase construida (construido tipos).The base class specified in a class declaration can be a constructed class type (Constructed types). Una clase base no puede ser un parámetro de tipo por sí mismo, aunque puede implicar a los parámetros de tipo que están en ámbito.A base class cannot be a type parameter on its own, though it can involve the type parameters that are in scope.

class Extend<V>: V {}            // Error, type parameter used as base class

Clases baseBase classes

Cuando un class_type se incluye en el class_base, especifica la clase base directa de la clase que se declara.When a class_type is included in the class_base, it specifies the direct base class of the class being declared. Si una declaración de clase no tiene ningún class_base, o si el class_base listas sólo los tipos de interfaz, se supone que la clase base directa se object.If a class declaration has no class_base, or if the class_base lists only interface types, the direct base class is assumed to be object. Una clase hereda los miembros de su clase base directa, como se describe en herencia.A class inherits members from its direct base class, as described in Inheritance.

En el ejemploIn the example

class A {}

class B: A {}

clase A se dice que la clase base directa de B, y B se dice que se derive de A.class A is said to be the direct base class of B, and B is said to be derived from A. Puesto que A does explícitamente no especificar una clase base directa, su clase base directa es implícitamente object.Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

Para un tipo de clase construida, si una clase base se especifica en la declaración de clase genérica, se obtiene la clase base del tipo construido sustituyendo, para cada tipo_parámetro en la declaración de clase base correspondiente type_argument del tipo construido.For a constructed class type, if a base class is specified in the generic class declaration, the base class of the constructed type is obtained by substituting, for each type_parameter in the base class declaration, the corresponding type_argument of the constructed type. Dadas las declaraciones de clase genéricaGiven the generic class declarations

class B<U,V> {...}

class G<T>: B<string,T[]> {...}

la clase base del tipo construido G<int> sería B<string,int[]>.the base class of the constructed type G<int> would be B<string,int[]>.

La clase base directa de un tipo de clase debe ser al menos tan accesible como el propio tipo de clase (dominios de accesibilidad).The direct base class of a class type must be at least as accessible as the class type itself (Accessibility domains). Por ejemplo, es un error en tiempo de compilación para un public clase derive de una private o internal clase.For example, it is a compile-time error for a public class to derive from a private or internal class.

La clase base directa de un tipo de clase no debe ser cualquiera de los siguientes tipos: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, o System.ValueType.The direct base class of a class type must not be any of the following types: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, or System.ValueType. Además, no se puede usar una declaración de clase genérica System.Attribute como una clase base directa o indirecta.Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

Al determinar el significado de la especificación de la clase base directa A de una clase B, la clase base directa de B temporalmente se supone que es object.While determining the meaning of the direct base class specification A of a class B, the direct base class of B is temporarily assumed to be object. Intuitivamente Esto garantiza que el significado de una especificación de clase base no pueden hacerlo de forma recursiva depender de sí misma.Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. El ejemplo:The example:

class A<T> {
   public class B {}
}

class C : A<C.B> {}

es un error desde en la especificación de la clase base A<C.B> la clase base directa de C se considera objecty por lo tanto, (según las reglas del Namespace y nombres de tipo) C no se considera tener un miembro B.is in error since in the base class specification A<C.B> the direct base class of C is considered to be object, and hence (by the rules of Namespace and type names) C is not considered to have a member B.

Las clases base de un tipo de clase son la clase base directa y sus clases base.The base classes of a class type are the direct base class and its base classes. En otras palabras, el conjunto de clases bases es el cierre transitivo de la relación de clase base directa.In other words, the set of base classes is the transitive closure of the direct base class relationship. Que hace referencia en el ejemplo anterior, las clases base de B son A y object.Referring to the example above, the base classes of B are A and object. En el ejemploIn the example

class A {...}

class B<T>: A {...}

class C<T>: B<IComparable<T>> {...}

class D<T>: C<T[]> {...}

las clases base de D<int> son C<int[]>, B<IComparable<int[]>>, A, y object.the base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

Excepto para la clase object, cada tipo de clase tiene exactamente una clase base directa.Except for class object, every class type has exactly one direct base class. La object clase no tiene ninguna clase base directa y es la clase base fundamental de todas las demás clases.The object class has no direct base class and is the ultimate base class of all other classes.

Cuando una clase B deriva una clase A, es un error de tiempo de compilación de A depender B.When a class B derives from a class A, it is a compile-time error for A to depend on B. Una clase depende directamente de su clase base directa (si existe) y depende directamente de la clase en el que está inmediatamente anidada (si existe).A class directly depends on its direct base class (if any) and directly depends on the class within which it is immediately nested (if any). Dada esta definición, el conjunto completo de las clases de los que depende una clase es el cierre reflexivo y transitivo de la depende directamente relación.Given this definition, the complete set of classes upon which a class depends is the reflexive and transitive closure of the directly depends on relationship.

El ejemploThe example

class A: A {}

es incorrecto porque depende de la clase en sí mismo.is erroneous because the class depends on itself. Del mismo modo, el ejemploLikewise, the example

class A: B {}
class B: C {}
class C: A {}

es un error porque las clases tienen una dependencia circular entre ellas.is in error because the classes circularly depend on themselves. Por último, el ejemploFinally, the example

class A: B.C {}

class B: A
{
    public class C {}
}

se produce un error en tiempo de compilación porque A depende B.C (su clase base directa), que depende de B (su clase inmediatamente envolvente), que depende de forma circular A.results in a compile-time error because A depends on B.C (its direct base class), which depends on B (its immediately enclosing class), which circularly depends on A.

Tenga en cuenta que una clase no depende de las clases que están anidadas dentro de él.Note that a class does not depend on the classes that are nested within it. En el ejemploIn the example

class A
{
    class B: A {}
}

B depende de A (porque A es su clase base directa y su clase inmediatamente envolvente), pero A no depende de B (puesto que B no es una clase base ni una clase contenedora de A ).B depends on A (because A is both its direct base class and its immediately enclosing class), but A does not depend on B (since B is neither a base class nor an enclosing class of A). Por lo tanto, el ejemplo es válido.Thus, the example is valid.

No es posible derivar un sealed clase.It is not possible to derive from a sealed class. En el ejemploIn the example

sealed class A {}

class B: A {}            // Error, cannot derive from a sealed class

clase B es un error porque intenta derivar el sealed clase A.class B is in error because it attempts to derive from the sealed class A.

Implementaciones de interfacesInterface implementations

Un class_base especificación puede incluir una lista de tipos de interfaz, en cuyo caso la clase se dice implementar directamente los tipos de interfaz determinado.A class_base specification may include a list of interface types, in which case the class is said to directly implement the given interface types. Implementaciones de interfaz se tratan con más detalle en implementaciones de interfaz.Interface implementations are discussed further in Interface implementations.

Restricciones de parámetro de tipoType parameter constraints

Las declaraciones de tipo y método genéricas pueden especificar las restricciones del parámetro mediante la inclusión de type_parameter_constraints_clauses.Generic type and method declarations can optionally specify type parameter constraints by including type_parameter_constraints_clauses.

type_parameter_constraints_clause
    : 'where' type_parameter ':' type_parameter_constraints
    ;

type_parameter_constraints
    : primary_constraint
    | secondary_constraints
    | constructor_constraint
    | primary_constraint ',' secondary_constraints
    | primary_constraint ',' constructor_constraint
    | secondary_constraints ',' constructor_constraint
    | primary_constraint ',' secondary_constraints ',' constructor_constraint
    ;

primary_constraint
    : class_type
    | 'class'
    | 'struct'
    ;

secondary_constraints
    : interface_type
    | type_parameter
    | secondary_constraints ',' interface_type
    | secondary_constraints ',' type_parameter
    ;

constructor_constraint
    : 'new' '(' ')'
    ;

Cada type_parameter_constraints_clause consta del token where, seguido del nombre de un parámetro de tipo, seguido de dos puntos y la lista de restricciones para ese parámetro de tipo.Each type_parameter_constraints_clause consists of the token where, followed by the name of a type parameter, followed by a colon and the list of constraints for that type parameter. Puede haber a lo sumo un where cláusula para cada parámetro de tipo y el where cláusulas pueden aparecer en cualquier orden.There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. Al igual que el get y set tokens en un descriptor de acceso de propiedad, el where símbolo (token) no es una palabra clave.Like the get and set tokens in a property accessor, the where token is not a keyword.

La lista de restricciones en un where cláusula puede incluir cualquiera de los siguientes componentes, en este orden: una restricción principal única, una o más restricciones secundarias y la restricción de constructor new().The list of constraints given in a where clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, new().

Una restricción principal puede ser un tipo de clase o el hacen referencia a la restricción de tipo class o restricción de tipo de valor struct.A primary constraint can be a class type or the reference type constraint class or the value type constraint struct. Una restricción secundaria puede ser un tipo_parámetro o interface_type.A secondary constraint can be a type_parameter or interface_type.

La restricción de tipo de referencia especifica que un argumento de tipo utilizado para el parámetro de tipo debe ser un tipo de referencia.The reference type constraint specifies that a type argument used for the type parameter must be a reference type. Todos los tipos de clase, tipos de interfaz, tipos de delegado, los tipos de matriz y los parámetros de tipo que se sabe que es un tipo de referencia (como se define a continuación) se aplica esta restricción.All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

La restricción de tipo de valor especifica que un argumento de tipo utilizado para el parámetro de tipo debe ser un tipo de valor distinto de NULL.The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. Todos los tipos de estructura que no aceptan valores NULL, los tipos de enumeración y los parámetros de tipo que tiene la restricción de tipo de valor se aplica esta restricción.All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Tenga en cuenta que aunque se clasifica como un tipo de valor, un tipo que acepta valores null (tipos que aceptan valores NULL) no satisface la restricción de tipo de valor.Note that although classified as a value type, a nullable type (Nullable types) does not satisfy the value type constraint. Un parámetro de tipo que tiene la restricción de tipo de valor no puede tener también el constructor_constraint.A type parameter having the value type constraint cannot also have the constructor_constraint.

Tipos de puntero nunca pueden ser argumentos de tipo y no se consideran para satisfacer cualquier las referencia valor o tipo de restricciones de tipo.Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

Si una restricción es un tipo de clase, un tipo de interfaz o un parámetro de tipo, dicho tipo especifica un tipo base"mínimo" que debe ser compatibles con cada argumento de tipo utilizado para ese parámetro de tipo.If a constraint is a class type, an interface type, or a type parameter, that type specifies a minimal "base type" that every type argument used for that type parameter must support. Cada vez que se utiliza un tipo construido o método genérico, el argumento de tipo se compara con las restricciones en el parámetro de tipo en tiempo de compilación.Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. El argumento de tipo proporcionado debe cumplir las condiciones descritas en que satisfacen las restricciones.The type argument supplied must satisfy the conditions described in Satisfying constraints.

Un class_type restricción debe cumplir las reglas siguientes:A class_type constraint must satisfy the following rules:

  • El tipo debe ser un tipo de clase.The type must be a class type.
  • El tipo no debe ser sealed.The type must not be sealed.
  • El tipo no debe ser uno de los siguientes tipos: System.Array, System.Delegate, System.Enum, o System.ValueType.The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • El tipo no debe ser object.The type must not be object. Dado que todos los tipos que derivan de object, este tipo de restricción tendría ningún efecto si se permitiera.Because all types derive from object, such a constraint would have no effect if it were permitted.
  • A lo sumo una restricción para un parámetro de tipo especificado puede ser un tipo de clase.At most one constraint for a given type parameter can be a class type.

Un tipo especificado como un interface_type restricción debe cumplir las reglas siguientes:A type specified as an interface_type constraint must satisfy the following rules:

  • El tipo debe ser un tipo de interfaz.The type must be an interface type.
  • Un tipo no debe especificarse más de una vez en un determinado where cláusula.A type must not be specified more than once in a given where clause.

En cualquier caso, la restricción puede incluir cualquiera de los parámetros de tipo del tipo asociado o declaración de método como parte de un tipo construido y puede implicar al tipo que se declara.In either case, the constraint can involve any of the type parameters of the associated type or method declaration as part of a constructed type, and can involve the type being declared.

Cualquier tipo de clase o interfaz especificado como una restricción de parámetro de tipo debe ser al menos tan accesible (restricciones de accesibilidad) como el tipo o método genérico que se declara.Any class or interface type specified as a type parameter constraint must be at least as accessible (Accessibility constraints) as the generic type or method being declared.

Un tipo especificado como un tipo_parámetro restricción debe cumplir las reglas siguientes:A type specified as a type_parameter constraint must satisfy the following rules:

  • El tipo debe ser un parámetro de tipo.The type must be a type parameter.
  • Un tipo no debe especificarse más de una vez en un determinado where cláusula.A type must not be specified more than once in a given where clause.

Además no debe haber ningún ciclo en el gráfico de dependencias de parámetros de tipo, donde dependencia es una relación transitiva definida por:In addition there must be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:

  • Si un parámetro de tipo T se utiliza como una restricción de parámetro de tipo S , a continuación, S depende T.If a type parameter T is used as a constraint for type parameter S then S depends on T.
  • Si un parámetro de tipo S depende de un parámetro de tipo T y T depende de un parámetro de tipo U , a continuación, S depende U.If a type parameter S depends on a type parameter T and T depends on a type parameter U then S depends on U.

Dada a esta relación, es un error en tiempo de compilación de un parámetro de tipo a depender de sí misma (directa o indirectamente).Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

Las restricciones deben ser coherentes entre los parámetros de tipo dependiente.Any constraints must be consistent among dependent type parameters. Si el parámetro de tipo S depende del parámetro de tipo T a continuación:If type parameter S depends on type parameter T then:

  • T no se debe tener la restricción de tipo de valor.T must not have the value type constraint. En caso contrario, T está sellado eficazmente lo S hubiera estado obligado a ser el mismo tipo que T, eliminando la necesidad de dos parámetros de tipo.Otherwise, T is effectively sealed so S would be forced to be the same type as T, eliminating the need for two type parameters.
  • Si S tiene la restricción de tipo de valor, a continuación, T no debe tener un class_type restricción.If S has the value type constraint then T must not have a class_type constraint.
  • Si S tiene un class_type restricción A y T tiene un class_type restricción B , a continuación, debe haber una conversión de identidad o implícita hacer referencia a la conversión de A a B o una conversión implícita de referencia de B a A.If S has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.
  • Si S también depende del parámetro de tipo U y U tiene un class_type restricción A y T tiene un class_type restricción B , a continuación, debe haber una conversión de identidad o una conversión implícita de referencia de A a B o una conversión implícita de referencia de B a A.If S also depends on type parameter U and U has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.

Es válido para S tener la restricción de tipo de valor y T a tiene la restricción de tipo de referencia.It is valid for S to have the value type constraint and T to have the reference type constraint. Esto limita T a los tipos de System.Object, System.ValueType, System.Enumy cualquier tipo de interfaz.Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

Si el where cláusula para un parámetro de tipo incluye una restricción de constructor (que tiene el formato new()), es posible utilizar el new operador para crear instancias del tipo (lasexpresionesdecreacióndeobjetos).If the where clause for a type parameter includes a constructor constraint (which has the form new()), it is possible to use the new operator to create instances of the type (Object creation expressions). Cualquier tipo de argumento utilizado para un parámetro de tipo con una restricción de constructor debe tener un constructor público sin parámetros (este constructor existe de manera implícita para cualquier tipo de valor) o ser un parámetro de tipo con la restricción de tipo de valor o la restricción de constructor (consulte la Restricciones de parámetro de tipo para obtener más información).Any type argument used for a type parameter with a constructor constraint must have a public parameterless constructor (this constructor implicitly exists for any value type) or be a type parameter having the value type constraint or constructor constraint (see Type parameter constraints for details).

Los siguientes son ejemplos de restricciones:The following are examples of constraints:

interface IPrintable
{
    void Print();
}

interface IComparable<T>
{
    int CompareTo(T value);
}

interface IKeyProvider<T>
{
    T GetKey();
}

class Printer<T> where T: IPrintable {...}

class SortedList<T> where T: IComparable<T> {...}

class Dictionary<K,V>
    where K: IComparable<K>
    where V: IPrintable, IKeyProvider<K>, new()
{
    ...
}

El ejemplo siguiente es error, porque provoca una circularidad en el gráfico de dependencias de los parámetros de tipo:The following example is in error because it causes a circularity in the dependency graph of the type parameters:

class Circular<S,T>
    where S: T
    where T: S                // Error, circularity in dependency graph
{
    ...
}

Los ejemplos siguientes muestran otras situaciones no válidas:The following examples illustrate additional invalid situations:

class Sealed<S,T>
    where S: T
    where T: struct        // Error, T is sealed
{
    ...
}

class A {...}

class B {...}

class Incompat<S,T>
    where S: A, T
    where T: B                // Error, incompatible class-type constraints
{
    ...
}

class StructWithClass<S,T,U>
    where S: struct, T
    where T: U
    where U: A                // Error, A incompatible with struct
{
    ...
}

El clase base efectiva de un parámetro de tipo T se define como sigue:The effective base class of a type parameter T is defined as follows:

  • Si T no tiene ninguna restricción principal o restricciones de parámetro de tipo, su clase base efectiva es object.If T has no primary constraints or type parameter constraints, its effective base class is object.
  • Si T tiene la restricción de tipo de valor, su clase base efectiva es System.ValueType.If T has the value type constraint, its effective base class is System.ValueType.
  • Si T tiene un class_type restricción C pero no tipo_parámetro restricciones, su clase base efectiva es C.If T has a class_type constraint C but no type_parameter constraints, its effective base class is C.
  • Si T no tiene ningún class_type restricción, pero tiene uno o varios tipo_parámetro restricciones, su clase base efectiva es el tipo más abarcado (eleva la conversión operadores) en el conjunto de clases base efectivas de su tipo_parámetro restricciones.If T has no class_type constraint but has one or more type_parameter constraints, its effective base class is the most encompassed type (Lifted conversion operators) in the set of effective base classes of its type_parameter constraints. Las reglas de coherencia Asegúrese de que existe un tipo más abarcado.The consistency rules ensure that such a most encompassed type exists.
  • Si T tiene tanto un class_type restricción y uno o más tipo_parámetro restricciones, su clase base efectiva es el tipo más abarcado (eleva la conversión operadores) en el conjunto formado por el class_type restricción de T y las clases base efectiva de su tipo_parámetro restricciones.If T has both a class_type constraint and one or more type_parameter constraints, its effective base class is the most encompassed type (Lifted conversion operators) in the set consisting of the class_type constraint of T and the effective base classes of its type_parameter constraints. Las reglas de coherencia Asegúrese de que existe un tipo más abarcado.The consistency rules ensure that such a most encompassed type exists.
  • Si T tiene la restricción de tipo de referencia, pero no class_type restricciones, su clase base efectiva es object.If T has the reference type constraint but no class_type constraints, its effective base class is object.

Con el propósito de estas reglas, si T tiene una restricción V que es un value_type, utilice en su lugar el tipo más específico de base de V que es un class_type.For the purpose of these rules, if T has a constraint V that is a value_type, use instead the most specific base type of V that is a class_type. Esto no puede ocurrir nunca en una restricción dada explícitamente, pero puede producirse cuando una declaración de método de reemplazo o una implementación explícita de un método de interfaz implícitamente heredan las restricciones de un método genérico.This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method.

Estas reglas garantizan que la clase base efectiva es siempre un class_type.These rules ensure that the effective base class is always a class_type.

El conjunto de interfaces efectivas de un parámetro de tipo T se define como sigue:The effective interface set of a type parameter T is defined as follows:

  • Si T no tiene ningún secondary_constraints, su conjunto de interfaces efectivas está vacío.If T has no secondary_constraints, its effective interface set is empty.
  • Si T tiene interface_type restricciones, pero no tipo_parámetro restricciones, su conjunto de interfaces efectivas es su conjunto de interface_type restricciones.If T has interface_type constraints but no type_parameter constraints, its effective interface set is its set of interface_type constraints.
  • Si T no tiene ningún interface_type , pero tiene restricciones tipo_parámetro restricciones, su conjunto de interfaces efectiva es la unión de los conjuntos de interfaz eficaz de su tipo_ parámetro restricciones.If T has no interface_type constraints but has type_parameter constraints, its effective interface set is the union of the effective interface sets of its type_parameter constraints.
  • Si T tiene ambos interface_type restricciones y tipo_parámetro restricciones, su conjunto de interfaces efectiva es la unión de su conjunto de interface_type las restricciones y los conjuntos de interfaz eficaz de su tipo_parámetro restricciones.If T has both interface_type constraints and type_parameter constraints, its effective interface set is the union of its set of interface_type constraints and the effective interface sets of its type_parameter constraints.

Es un parámetro de tipo sabe que es un tipo de referencia si tiene la restricción de tipo de referencia o no es de su clase base efectiva object o System.ValueType.A type parameter is known to be a reference type if it has the reference type constraint or its effective base class is not object or System.ValueType.

Los valores de un tipo de parámetro de tipo restringido pueden utilizarse para tener acceso a los miembros de instancia implicados en las restricciones.Values of a constrained type parameter type can be used to access the instance members implied by the constraints. En el ejemploIn the example

interface IPrintable
{
    void Print();
}

class Printer<T> where T: IPrintable
{
    void PrintOne(T x) {
        x.Print();
    }
}

los métodos de IPrintable se puede invocar directamente en x porque T está restringido a implementar siempre IPrintable.the methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

Cuerpo de la claseClass body

El class_body de una clase define los miembros de esa clase.The class_body of a class defines the members of that class.

class_body
    : '{' class_member_declaration* '}'
    ;

Tipos parcialesPartial types

Una declaración de tipos se puede dividir entre varios declaraciones parciales de tipos.A type declaration can be split across multiple partial type declarations. La declaración de tipos se construye a partir de sus partes siguiendo las reglas en esta sección, con lo que se trata como una sola declaración durante el resto del procesamiento de tiempo de compilación y tiempo de ejecución del programa.The type declaration is constructed from its parts by following the rules in this section, whereupon it is treated as a single declaration during the remainder of the compile-time and run-time processing of the program.

Un class_declaration, struct_declaration o interface_declaration representa una declaración de tipo parcial si incluye un partial modificador.A class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partial no es una palabra clave y sólo actúa como un modificador si aparece inmediatamente antes de una de las palabras clave class, struct o interface en una declaración de tipo, o antes del tipo void en una declaración de método.partial is not a keyword, and only acts as a modifier if it appears immediately before one of the keywords class, struct or interface in a type declaration, or before the type void in a method declaration. En otros contextos, se puede usar como un identificador normal.In other contexts it can be used as a normal identifier.

Cada parte de una declaración de tipo parcial debe incluir un partial modificador.Each part of a partial type declaration must include a partial modifier. Debe tener el mismo nombre y declararse en el mismo espacio de nombres o en otras partes de la declaración de tipos.It must have the same name and be declared in the same namespace or type declaration as the other parts. El partial modificador indica que las partes adicionales de la declaración de tipos pueden existir en otra parte, pero la existencia de tales elementos adicionales no es un requisito; es válida para un tipo con una sola declaración incluir el partial modificador.The partial modifier indicates that additional parts of the type declaration may exist elsewhere, but the existence of such additional parts is not a requirement; it is valid for a type with a single declaration to include the partial modifier.

Todas las partes de un tipo parcial se deben compilar juntos tal que las partes se pueden combinar en tiempo de compilación en una declaración de tipo único.All parts of a partial type must be compiled together such that the parts can be merged at compile-time into a single type declaration. Tipos parciales no permiten ampliar los tipos ya compilados en concreto.Partial types specifically do not allow already compiled types to be extended.

Los tipos anidados se pueden declarar en varias partes mediante el uso de la partial modificador.Nested types may be declared in multiple parts by using the partial modifier. Normalmente, el tipo contenedor se declara mediante partial bien y cada parte del tipo anidado que se declara en una parte distinta del tipo contenedor.Typically, the containing type is declared using partial as well, and each part of the nested type is declared in a different part of the containing type.

El partial modificador no está permitido en declaraciones de delegado o enumeración.The partial modifier is not permitted on delegate or enum declarations.

AtributosAttributes

Los atributos de un tipo parcial se determinan mediante la combinación, en un orden no especificado, los atributos de cada una de las partes.The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. Si se coloca un atributo en varias partes, es equivalente a especificar el atributo varias veces en el tipo.If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. Por ejemplo, las dos partes:For example, the two parts:

[Attr1, Attr2("hello")]
partial class A {}

[Attr3, Attr2("goodbye")]
partial class A {}

son equivalentes a una declaración de tales como:are equivalent to a declaration such as:

[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")]
class A {}

Atributos de tipos de parámetros se combinan de manera similar.Attributes on type parameters combine in a similar fashion.

ModificadoresModifiers

Cuando una declaración de tipo parcial incluye una especificación de accesibilidad (el public, protected, internal, y private modificadores) debe coincidir con todas las otras partes que incluyen una especificación de accesibilidad.When a partial type declaration includes an accessibility specification (the public, protected, internal, and private modifiers) it must agree with all other parts that include an accessibility specification. Si ninguna parte de un tipo parcial incluye una especificación de accesibilidad, el tipo tiene la accesibilidad predeterminada apropiada (accesibilidad declarada).If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (Declared accessibility).

Si uno o más declaraciones parciales de un tipo anidado incluyen un new modificador, no se notifica ninguna advertencia si el tipo anidado oculta un miembro heredado (ocultar mediante herencia).If one or more partial declarations of a nested type include a new modifier, no warning is reported if the nested type hides an inherited member (Hiding through inheritance).

Si uno o más declaraciones parciales de una clase incluyen un abstract modificador, la clase se considera abstracta (clases abstractas).If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (Abstract classes). En caso contrario, la clase se considera no abstractas.Otherwise, the class is considered non-abstract.

Si uno o más declaraciones parciales de una clase incluyen un sealed modificador, la clase se considera sellado (clases Sealed).If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (Sealed classes). En caso contrario, se considera la clase no sellada.Otherwise, the class is considered unsealed.

Tenga en cuenta que una clase no puede ser abstract y sealed.Note that a class cannot be both abstract and sealed.

Cuando el unsafe modificador se usa en una declaración de tipo parcial, solo esa parte concreta se considera un contexto no seguro (contextos no seguros).When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (Unsafe contexts).

Parámetros de tipo y restriccionesType parameters and constraints

Si un tipo genérico se declara en varias partes, cada parte debe declarar los parámetros de tipo.If a generic type is declared in multiple parts, each part must state the type parameters. Cada parte debe tener el mismo número de parámetros de tipo y el mismo nombre para cada parámetro de tipo, en orden.Each part must have the same number of type parameters, and the same name for each type parameter, in order.

Cuando una declaración de tipo genérico parcial incluye restricciones (where cláusulas), las restricciones deben coincidir con todas las otras partes que incluyen restricciones.When a partial generic type declaration includes constraints (where clauses), the constraints must agree with all other parts that include constraints. En concreto, cada elemento que incluya restricciones debe tener restricciones para el mismo conjunto de parámetros de tipo, y para cada parámetro de tipo de los conjuntos de principal, secundaria y las restricciones de constructor deben ser equivalentes.Specifically, each part that includes constraints must have constraints for the same set of type parameters, and for each type parameter the sets of primary, secondary, and constructor constraints must be equivalent. Dos conjuntos de restricciones son equivalentes si contienen a los mismos miembros.Two sets of constraints are equivalent if they contain the same members. Si ninguna parte de un tipo genérico parcial especifica las restricciones del parámetro, el tipo se consideran parámetros sin restricciones.If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.

El ejemploThe example

partial class Dictionary<K,V>
    where K: IComparable<K>
    where V: IKeyProvider<K>, IPersistable
{
    ...
}

partial class Dictionary<K,V>
    where V: IPersistable, IKeyProvider<K>
    where K: IComparable<K>
{
    ...
}

partial class Dictionary<K,V>
{
    ...
}

es correcto porque aquellas partes que incluyen restricciones (los dos primeros) de forma eficaz especifican el mismo conjunto de principal, secundaria y las restricciones de constructor para el mismo conjunto de parámetros de tipo, respectivamente.is correct because those parts that include constraints (the first two) effectively specify the same set of primary, secondary, and constructor constraints for the same set of type parameters, respectively.

Clase baseBase class

Cuando una declaración de clase parcial incluye una especificación de clase base, debe coincidir con todas las otras partes que incluyen una especificación de clase base.When a partial class declaration includes a base class specification it must agree with all other parts that include a base class specification. Si ninguna parte de una clase parcial incluye una especificación de clase base, se convierte en la clase base System.Object (clases Base).If no part of a partial class includes a base class specification, the base class becomes System.Object (Base classes).

Interfaces baseBase interfaces

El conjunto de interfaces base para un tipo declarado en varias partes es la unión de las interfaces base especificadas en cada parte.The set of base interfaces for a type declared in multiple parts is the union of the base interfaces specified on each part. Una interfaz base concreta solo puede llamarse una vez en cada parte, pero se permite varias partes de un nombre de las mismas interfaces bases.A particular base interface may only be named once on each part, but it is permitted for multiple parts to name the same base interface(s). Solo debe haber una implementación de los miembros de cualquier interfaz base determinada.There must only be one implementation of the members of any given base interface.

En el ejemploIn the example

partial class C: IA, IB {...}

partial class C: IC {...}

partial class C: IA, IB {...}

el conjunto de interfaces base para la clase C es IA, IB, y IC.the set of base interfaces for class C is IA, IB, and IC.

Normalmente, cada parte proporciona una implementación de las interfaces declaradas en esa parte; Sin embargo, esto no es un requisito.Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. Una parte puede proporcionar la implementación para una interfaz declarada en una parte distinta:A part may provide the implementation for an interface declared on a different part:

partial class X
{
    int IComparable.CompareTo(object o) {...}
}

partial class X: IComparable
{
    ...
}

MiembrosMembers

A excepción de los métodos parciales (métodos parciales), el conjunto de miembros de un tipo declarado en varias partes es simplemente la unión del conjunto de miembros declarados en cada parte.With the exception of partial methods (Partial methods), the set of members of a type declared in multiple parts is simply the union of the set of members declared in each part. Los cuerpos de todas las partes de la declaración de tipo comparten el mismo espacio de declaración (declaraciones) y el ámbito de cada miembro (ámbitos) se extiende a los cuerpos de todas las partes.The bodies of all parts of the type declaration share the same declaration space (Declarations), and the scope of each member (Scopes) extends to the bodies of all the parts. El dominio de accesibilidad de cualquier miembro siempre incluye todas las partes del tipo envolvente. un private miembro declarado en una parte es libremente accesible desde otra parte.The accessibility domain of any member always includes all the parts of the enclosing type; a private member declared in one part is freely accessible from another part. Es un error en tiempo de compilación para declarar el mismo miembro en más de una parte del tipo, a menos que ese miembro es un tipo con el partial modificador.It is a compile-time error to declare the same member in more than one part of the type, unless that member is a type with the partial modifier.

partial class A
{
    int x;                     // Error, cannot declare x more than once

    partial class Inner        // Ok, Inner is a partial type
    {
        int y;
    }
}

partial class A
{
    int x;                     // Error, cannot declare x more than once

    partial class Inner        // Ok, Inner is a partial type
    {
        int z;
    }
}

El orden de los miembros dentro de un tipo es rara vez significativo al código de C#, pero puede ser importante cuando se interactúa con otros lenguajes y entornos.The ordering of members within a type is rarely significant to C# code, but may be significant when interfacing with other languages and environments. En estos casos, el orden de los miembros dentro de un tipo declarado en varias partes es indefinido.In these cases, the ordering of members within a type declared in multiple parts is undefined.

Métodos parcialesPartial methods

Métodos parciales pueden definirse en una parte de una declaración de tipo y se implementa en otro.Partial methods can be defined in one part of a type declaration and implemented in another. La implementación es opcional; Si ninguna parte implementa el método parcial, la declaración de método parcial y todas las llamadas a la se quitan de la declaración del tipo resultante de la combinación de las partes.The implementation is optional; if no part implements the partial method, the partial method declaration and all calls to it are removed from the type declaration resulting from the combination of the parts.

Los métodos parciales no se puede definir los modificadores de acceso, pero son implícitamente private.Partial methods cannot define access modifiers, but are implicitly private. Tipo de valor devuelto debe ser void, y no pueden tener sus parámetros la out modificador.Their return type must be void, and their parameters cannot have the out modifier. El identificador partial se reconoce como palabra clave especial en una declaración de método sólo si aparece justo antes de que el void tipo; en caso contrario, se puede usar como un identificador normal.The identifier partial is recognized as a special keyword in a method declaration only if it appears right before the void type; otherwise it can be used as a normal identifier. Un método parcial no puede implementar explícitamente los métodos de interfaz.A partial method cannot explicitly implement interface methods.

Hay dos tipos de declaraciones de método parcial: Si el cuerpo de la declaración del método es un punto y coma, la declaración se dice que un definir la declaración de método parcial.There are two kinds of partial method declarations: If the body of the method declaration is a semicolon, the declaration is said to be a defining partial method declaration. Si el cuerpo se expresa como un bloque, la declaración se dice que un implementar la declaración de método parcial.If the body is given as a block, the declaration is said to be an implementing partial method declaration. Entre las partes de una declaración de tipo puede haber solo una declaración de método parcial con una firma determinada de definición y puede haber solo una implementación de la declaración de método parcial con una firma dada.Across the parts of a type declaration there can be only one defining partial method declaration with a given signature, and there can be only one implementing partial method declaration with a given signature. Si tiene una declaración de método parcial de implementación, una definición de la declaración de método parcial correspondiente debe existir y deben coincidir con las declaraciones como se especifica en la siguiente:If an implementing partial method declaration is given, a corresponding defining partial method declaration must exist, and the declarations must match as specified in the following:

  • Las declaraciones deben tener los mismos modificadores (aunque no necesariamente en el mismo orden), nombre del método, el número de parámetros de tipo y el número de parámetros.The declarations must have the same modifiers (although not necessarily in the same order), method name, number of type parameters and number of parameters.
  • Los parámetros correspondientes en las declaraciones deben tener los mismos modificadores (aunque no necesariamente en el mismo orden) y los mismos tipos (módulo las diferencias en los nombres de parámetro de tipo).Corresponding parameters in the declarations must have the same modifiers (although not necessarily in the same order) and the same types (modulo differences in type parameter names).
  • Parámetros de tipo correspondientes en las declaraciones de deben tener las mismas restricciones (módulo las diferencias en los nombres de parámetro de tipo).Corresponding type parameters in the declarations must have the same constraints (modulo differences in type parameter names).

Una declaración de método parcial de implementación puede aparecer en el mismo elemento como la declaración de método parcial de definición correspondiente.An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.

Solo un método parcial definición participa en la resolución de sobrecarga.Only a defining partial method participates in overload resolution. Por lo tanto, si se proporciona una declaración de implementación, expresiones de invocación pueden resolver a las invocaciones del método parcial.Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Dado que siempre devuelve un método parcial void, estas expresiones de invocación siempre será las instrucciones de expresión.Because a partial method always returns void, such invocation expressions will always be expression statements. Además, dado que un método parcial es implícitamente private, siempre se produzcan tales instrucciones dentro de una de las partes de la declaración de tipo en el que se declara el método parcial.Furthermore, because a partial method is implicitly private, such statements will always occur within one of the parts of the type declaration within which the partial method is declared.

Si ninguna parte de una declaración de tipo parcial contiene una declaración de implementación para un determinado método parcial, cualquier instrucción de expresión que realiza la llamada simplemente se quita de la declaración de tipos combinados.If no part of a partial type declaration contains an implementing declaration for a given partial method, any expression statement invoking it is simply removed from the combined type declaration. Por tanto, la expresión de invocación, incluidas las expresiones constituyentes, tiene ningún efecto en tiempo de ejecución.Thus the invocation expression, including any constituent expressions, has no effect at run-time. El método parcial propio también se quita y no será un miembro de la declaración de tipos combinados.The partial method itself is also removed and will not be a member of the combined type declaration.

Si existe una declaración de implementación de un método parcial dado, se conservan las invocaciones de métodos parciales.If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. El método parcial da lugar a una declaración de método similar a la declaración de implementación de método parcial, excepto los siguientes:The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

  • El partial modificador no se incluyeThe partial modifier is not included
  • Los atributos de la declaración del método resultante son los atributos de la definición y la declaración de método parcial implementación combinados en un orden no especificado.The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. No se quitan los duplicados.Duplicates are not removed.
  • Los atributos de los parámetros de la declaración del método resultante son los atributos combinados de los parámetros correspondientes de la definición y la declaración de método parcial de implementación en un orden no especificado.The attributes on the parameters of the resulting method declaration are the combined attributes of the corresponding parameters of the defining and the implementing partial method declaration in unspecified order. No se quitan los duplicados.Duplicates are not removed.

Si se especifica, pero no una declaración de implementación de una declaración de definición para un método parcial M, se aplican las restricciones siguientes:If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:

Los métodos parciales son útiles para permitir que una parte de una declaración de tipo para personalizar el comportamiento de la otra parte, por ejemplo, uno generado por una herramienta.Partial methods are useful for allowing one part of a type declaration to customize the behavior of another part, e.g., one that is generated by a tool. Considere la siguiente declaración de clase parcial:Consider the following partial class declaration:

partial class Customer
{
    string name;

    public string Name {
        get { return name; }
        set {
            OnNameChanging(value);
            name = value;
            OnNameChanged();
        }

    }

    partial void OnNameChanging(string newName);

    partial void OnNameChanged();
}

Si esta clase se compila sin cualquier otra parte, se quitarán las declaraciones de método parcial definición y sus invocaciones y la declaración de clase combinado resultante será equivalente a la siguiente:If this class is compiled without any other parts, the defining partial method declarations and their invocations will be removed, and the resulting combined class declaration will be equivalent to the following:

class Customer
{
    string name;

    public string Name {
        get { return name; }
        set { name = value; }
    }
}

Suponga que otra parte se proporciona, sin embargo, que proporciona las declaraciones de implementación de los métodos parciales:Assume that another part is given, however, which provides implementing declarations of the partial methods:

partial class Customer
{
    partial void OnNameChanging(string newName)
    {
        Console.WriteLine("Changing " + name + " to " + newName);
    }

    partial void OnNameChanged()
    {
        Console.WriteLine("Changed to " + name);
    }
}

A continuación, la declaración de clase combinado resultante será equivalente a la siguiente:Then the resulting combined class declaration will be equivalent to the following:

class Customer
{
    string name;

    public string Name {
        get { return name; }
        set {
            OnNameChanging(value);
            name = value;
            OnNameChanged();
        }

    }

    void OnNameChanging(string newName)
    {
        Console.WriteLine("Changing " + name + " to " + newName);
    }

    void OnNameChanged()
    {
        Console.WriteLine("Changed to " + name);
    }
}

Enlace de nombresName binding

Aunque cada parte de un tipo extensible debe declararse dentro del mismo espacio de nombres, las partes se suelen escribir en las declaraciones de espacio de nombres diferente.Although each part of an extensible type must be declared within the same namespace, the parts are typically written within different namespace declarations. Por lo tanto, diferentes using directivas (directivas Using) pueden estar presentes para cada parte.Thus, different using directives (Using directives) may be present for each part. Al interpretar los nombres simples (inferencia) dentro de una sola parte, el using se consideran las directivas de las declaraciones de espacio de nombres envolvente esa parte.When interpreting simple names (Type inference) within one part, only the using directives of the namespace declaration(s) enclosing that part are considered. Esto puede producir el mismo identificador con diferentes significados en diferentes partes:This may result in the same identifier having different meanings in different parts:

namespace N
{
    using List = System.Collections.ArrayList;

    partial class A
    {
        List x;                // x has type System.Collections.ArrayList
    }
}

namespace N
{
    using List = Widgets.LinkedList;

    partial class A
    {
        List y;                // y has type Widgets.LinkedList
    }
}

Miembros de la claseClass members

Los miembros de una clase se componen de los miembros introducidos por su class_member_declarations y los miembros heredan de la clase base directa.The members of a class consist of the members introduced by its class_member_declarations and the members inherited from the direct base class.

class_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | operator_declaration
    | constructor_declaration
    | destructor_declaration
    | static_constructor_declaration
    | type_declaration
    ;

Los miembros de un tipo de clase se dividen en las siguientes categorías:The members of a class type are divided into the following categories:

  • Constantes, que representan valores constantes asociados a la clase (constantes).Constants, which represent constant values associated with the class (Constants).
  • Los campos, que son las variables de la clase (campos).Fields, which are the variables of the class (Fields).
  • Métodos, que implementan los cálculos y acciones que pueden realizarse mediante la clase (métodos).Methods, which implement the computations and actions that can be performed by the class (Methods).
  • Propiedades, que definen características con nombre y las acciones asociadas a la lectura y escritura de esas características (propiedades).Properties, which define named characteristics and the actions associated with reading and writing those characteristics (Properties).
  • Eventos, que definen las notificaciones que se pueden generar mediante la clase (eventos).Events, which define notifications that can be generated by the class (Events).
  • Indizadores, que permiten a las instancias de la clase que se debe indexar de la misma manera (sintácticamente) que las matrices (indizadores).Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays (Indexers).
  • Operadores, que definen los operadores de expresión que se pueden aplicar a las instancias de la clase (operadores).Operators, which define the expression operators that can be applied to instances of the class (Operators).
  • Constructores de instancias, que implementan las acciones necesarias para inicializar instancias de la clase (constructores de instancias)Instance constructors, which implement the actions required to initialize instances of the class (Instance constructors)
  • Los destructores, que implementan las acciones antes de instancias de la clase se descarten de forma permanente (destructores).Destructors, which implement the actions to be performed before instances of the class are permanently discarded (Destructors).
  • Constructores estáticos, que implementan las acciones necesarias para inicializar la propia clase (constructores estáticos).Static constructors, which implement the actions required to initialize the class itself (Static constructors).
  • Tipos que representan los tipos que son locales a la clase (tipos anidados).Types, which represent the types that are local to the class (Nested types).

Los miembros que pueden contener código ejecutable se conocen colectivamente como la miembros de función del tipo de clase.Members that can contain executable code are collectively known as the function members of the class type. Los miembros de función de un tipo de clase son los métodos, propiedades, eventos, indizadores, operadores, constructores de instancias, destructores y constructores estáticos de ese tipo de clase.The function members of a class type are the methods, properties, events, indexers, operators, instance constructors, destructors, and static constructors of that class type.

Un class_declaration crea un nuevo espacio de declaración (declaraciones) y el class_member_declarations contenidas inmediatamente en la clase _declaration introducir nuevos miembros en este espacio de declaración.A class_declaration creates a new declaration space (Declarations), and the class_member_declarations immediately contained by the class_declaration introduce new members into this declaration space. Las siguientes reglas se aplican a class_member_declarations:The following rules apply to class_member_declarations:

  • Constructores de instancias, destructores y constructores estáticos deben tener el mismo nombre que la clase inmediatamente envolvente.Instance constructors, destructors and static constructors must have the same name as the immediately enclosing class. Todos los demás miembros deben tener nombres que difieren del nombre de la clase inmediatamente envolvente.All other members must have names that differ from the name of the immediately enclosing class.
  • El nombre de constante, campo, propiedad, evento o tipo debe ser diferente de los nombres de todos los demás miembros declarados en la misma clase.The name of a constant, field, property, event, or type must differ from the names of all other members declared in the same class.
  • El nombre de un método debe ser diferente de los nombres de todos los demás de que no son métodos declarados en la misma clase.The name of a method must differ from the names of all other non-methods declared in the same class. Además, la firma (firmas y sobrecarga) de un método debe ser diferentes de las firmas de todos los demás métodos declarados en la misma clase, y dos métodos declarados en la misma clase no pueden tener signaturas que se diferencien únicamente por ref y out.In addition, the signature (Signatures and overloading) of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out.
  • La firma de un constructor de instancia debe ser diferentes de las firmas de todos los demás constructores de instancia declarados en la misma clase, y dos constructores declarados en la misma clase no pueden tener firmas que se diferencien únicamente por ref y out.The signature of an instance constructor must differ from the signatures of all other instance constructors declared in the same class, and two constructors declared in the same class may not have signatures that differ solely by ref and out.
  • La firma de un indizador debe ser diferente de las firmas de todos los demás indexadores declarados en la misma clase.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • La firma de un operador debe ser diferente de las firmas de todos los demás operadores declarados en la misma clase.The signature of an operator must differ from the signatures of all other operators declared in the same class.

Los miembros heredados de un tipo de clase (herencia) no forman parte del espacio de declaración de una clase.The inherited members of a class type (Inheritance) are not part of the declaration space of a class. Por lo tanto, una clase derivada puede declarar a un miembro con el mismo nombre o signatura que un miembro heredado (que en realidad oculta al miembro heredado).Thus, a derived class is allowed to declare a member with the same name or signature as an inherited member (which in effect hides the inherited member).

El tipo de instanciaThe instance type

Cada declaración de clase tiene un tipo enlace asociado (dependientes e independientes tipos), el tipo de instancia.Each class declaration has an associated bound type (Bound and unbound types), the instance type. Para una declaración de clase genérica, el tipo de instancia se forma mediante la creación de un tipo construido (construido tipos) de la declaración de tipo, con cada uno del tipo suministrado argumentos es el correspondiente parámetro de tipo.For a generic class declaration, the instance type is formed by creating a constructed type (Constructed types) from the type declaration, with each of the supplied type arguments being the corresponding type parameter. Puesto que el tipo de instancia utiliza los parámetros de tipo, que puede usarse únicamente donde los parámetros de tipo están en ámbito; es decir, dentro de la declaración de clase.Since the instance type uses the type parameters, it can only be used where the type parameters are in scope; that is, inside the class declaration. El tipo de instancia es el tipo de this para el código escrito dentro de la declaración de clase.The instance type is the type of this for code written inside the class declaration. Para las clases no genéricas, el tipo de instancia es simplemente la clase declarada.For non-generic classes, the instance type is simply the declared class. A continuación muestra varias declaraciones de clase junto con sus tipos de instancia:The following shows several class declarations along with their instance types:

class A<T>                           // instance type: A<T>
{
    class B {}                       // instance type: A<T>.B
    class C<U> {}                    // instance type: A<T>.C<U>
}

class D {}                           // instance type: D

Miembros de tipos construidosMembers of constructed types

Los miembros no heredados de un tipo construido se obtienen sustituyendo, para cada tipo_parámetro en la declaración de miembro correspondiente type_argument del tipo construido.The non-inherited members of a constructed type are obtained by substituting, for each type_parameter in the member declaration, the corresponding type_argument of the constructed type. El proceso de sustitución se basa en el significado semántico de declaraciones de tipos y no es una sustitución simplemente textual.The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

Por ejemplo, dada la declaración de clase genéricaFor example, given the generic class declaration

class Gen<T,U>
{
    public T[,] a;
    public void G(int i, T t, Gen<U,T> gt) {...}
    public U Prop { get {...} set {...} }
    public int H(double d) {...}
}

el tipo construido Gen<int[],IComparable<string>> tiene los siguientes miembros:the constructed type Gen<int[],IComparable<string>> has the following members:

public int[,][] a;
public void G(int i, int[] t, Gen<IComparable<string>,int[]> gt) {...}
public IComparable<string> Prop { get {...} set {...} }
public int H(double d) {...}

El tipo del miembro a en la declaración de clase genérica Gen es "matriz bidimensional de T", por lo que el tipo del miembro a en el tipo construido anterior es "matriz bidimensional de una matriz unidimensional de int", o int[,][].The type of the member a in the generic class declaration Gen is "two-dimensional array of T", so the type of the member a in the constructed type above is "two-dimensional array of one-dimensional array of int", or int[,][].

Dentro de los miembros de la función de instancia, el tipo de this es el tipo de instancia (el tipo de instancia) de la declaración del contenedor.Within instance function members, the type of this is the instance type (The instance type) of the containing declaration.

Todos los miembros de una clase genérica pueden usar parámetros de tipo de cualquier clase envolvente, ya sea directamente o como parte de un tipo construido.All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. Cuando se cierra un determinado tipo construido (abierto y cerrado tipos) se usa en tiempo de ejecución, cada uso de un parámetro de tipo es reemplazado por el argumento de tipo real proporcionado para el tipo construido.When a particular closed constructed type (Open and closed types) is used at run-time, each use of a type parameter is replaced with the actual type argument supplied to the constructed type. Por ejemplo:For example:

class C<V>
{
    public V f1;
    public C<V> f2 = null;

    public C(V x) {
        this.f1 = x;
        this.f2 = this;
    }
}

class Application
{
    static void Main() {
        C<int> x1 = new C<int>(1);
        Console.WriteLine(x1.f1);        // Prints 1

        C<double> x2 = new C<double>(3.1415);
        Console.WriteLine(x2.f1);        // Prints 3.1415
    }
}

HerenciaInheritance

Una clase hereda los miembros de su tipo de clase base directa.A class inherits the members of its direct base class type. La herencia significa que una clase contiene implícitamente todos los miembros de su tipo de clase base directa, excepto los constructores de instancias, destructores y constructores estáticos de la clase base.Inheritance means that a class implicitly contains all members of its direct base class type, except for the instance constructors, destructors and static constructors of the base class. Algunos aspectos importantes de herencia son:Some important aspects of inheritance are:

  • La herencia es transitiva.Inheritance is transitive. Si C se deriva de B, y B se deriva de A, a continuación, C hereda los miembros declarados en B , así como los miembros declarados en A.If C is derived from B, and B is derived from A, then C inherits the members declared in B as well as the members declared in A.
  • Una clase derivada extiende su clase base directa.A derived class extends its direct base class. Una clase derivada puede agregar nuevos miembros a aquellos de los que hereda, pero no puede quitar la definición de un miembro heredado.A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • Constructores de instancias, destructores y constructores estáticos no se heredan, pero todos los demás miembros, independientemente de su accesibilidad declarada (acceso a miembros).Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (Member access). Sin embargo, dependiendo de su accesibilidad declarada, es posible que los miembros heredados no esté accesibles en una clase derivada.However, depending on their declared accessibility, inherited members might not be accessible in a derived class.
  • Una clase derivada puede ocultar (ocultar mediante herencia) los miembros heredados mediante la declaración de nuevos miembros con el mismo nombre o signatura.A derived class can hide (Hiding through inheritance) inherited members by declaring new members with the same name or signature. Tenga en cuenta sin embargo que ocultar un miembro heredado no elimina dicho miembro, simplemente hace que el miembro accesible directamente a través de la clase derivada.Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.
  • Una instancia de una clase contiene un conjunto de todos los campos de instancia declarados en la clase y sus clases base y una conversión implícita (conversiones implícitas de referencia) existe desde un tipo de clase derivada a cualquiera de sus tipos de clase base.An instance of a class contains a set of all instance fields declared in the class and its base classes, and an implicit conversion (Implicit reference conversions) exists from a derived class type to any of its base class types. Por lo tanto, una referencia a una instancia de alguna clase derivada puede tratarse como una referencia a una instancia de cualquiera de sus clases base.Thus, a reference to an instance of some derived class can be treated as a reference to an instance of any of its base classes.
  • Una clase puede declarar indizadores, propiedades y métodos virtuales y las clases derivadas pueden invalidar la implementación de estos miembros de función.A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. Esto permite que las clases muestren un comportamiento polimórfico ya que las acciones realizadas por una llamada a función miembro varían según el tipo de tiempo de ejecución de la instancia a través del cual se invoca ese miembro de función.This enables classes to exhibit polymorphic behavior wherein the actions performed by a function member invocation varies depending on the run-time type of the instance through which that function member is invoked.

El miembro heredado de un tipo de clase construida son los miembros del tipo de clase base inmediata (clases Base), que se encuentra, sustituyendo los argumentos de tipo para cada repetición del tipo correspondiente del tipo construido los parámetros en el class_base especificación.The inherited member of a constructed class type are the members of the immediate base class type (Base classes), which is found by substituting the type arguments of the constructed type for each occurrence of the corresponding type parameters in the class_base specification. Estos miembros, se transforman a su vez, sustituyendo, para cada tipo_parámetro en la declaración de miembro correspondiente type_argument de la class_base especificación.These members, in turn, are transformed by substituting, for each type_parameter in the member declaration, the corresponding type_argument of the class_base specification.

class B<U>
{
    public U F(long index) {...}
}

class D<T>: B<T[]>
{
    public T G(string s) {...}
}

En el ejemplo anterior, el tipo construido D<int> tiene un miembro no heredado public int G(string s) obtiene sustituyendo el argumento de tipo int para el parámetro de tipo T.In the above example, the constructed type D<int> has a non-inherited member public int G(string s) obtained by substituting the type argument int for the type parameter T. D<int> También tiene un miembro heredado de la declaración de clase B.D<int> also has an inherited member from the class declaration B. Este miembro heredado se determina, determine primero el tipo de clase base B<int[]> de D<int> sustituyendo int para T en la especificación de la clase base B<T[]>.This inherited member is determined by first determining the base class type B<int[]> of D<int> by substituting int for T in the base class specification B<T[]>. A continuación, como un argumento de tipo B, int[] se sustituye por U en public U F(long index), produciendo el miembro heredado public int[] F(long index).Then, as a type argument to B, int[] is substituted for U in public U F(long index), yielding the inherited member public int[] F(long index).

El nuevo modificadorThe new modifier

Un class_member_declaration se pueden declarar un miembro con el mismo nombre o signatura que un miembro heredado.A class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. Cuando esto ocurre, se dice que el miembro de clase derivada a ocultar el miembro de clase base.When this occurs, the derived class member is said to hide the base class member. Ocultar a un miembro heredado no se considera un error, pero hace que el compilador emita una advertencia.Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. Para suprimir la advertencia, la declaración del miembro de clase derivada puede incluir un new modificador para indicar que el miembro derivado está diseñado para ocultar el miembro base.To suppress the warning, the declaration of the derived class member can include a new modifier to indicate that the derived member is intended to hide the base member. En este tema se describe con más detalle en ocultar mediante herencia.This topic is discussed further in Hiding through inheritance.

Si un new modificador se incluye en una declaración que no oculta un miembro heredado, se emite una advertencia a tal efecto.If a new modifier is included in a declaration that doesn't hide an inherited member, a warning to that effect is issued. Se puede suprimir esta advertencia mediante la eliminación de la new modificador.This warning is suppressed by removing the new modifier.

Modificadores de accesoAccess modifiers

Un class_member_declaration puede tener uno de los cinco posibles tipos de accesibilidad declarada (accesibilidad declarada): public, protected internal, protected, internal , o private.A class_member_declaration can have any one of the five possible kinds of declared accessibility (Declared accessibility): public, protected internal, protected, internal, or private. Excepto para el protected internal combinación, es un error en tiempo de compilación para especificar más de un modificador de acceso.Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. Cuando un class_member_declaration no incluye ningún modificador de acceso, private se da por hecho.When a class_member_declaration does not include any access modifiers, private is assumed.

Tipos constituyentesConstituent types

Tipos que se usan en la declaración de un miembro se denominan tipos constituyentes de ese miembro.Types that are used in the declaration of a member are called the constituent types of that member. Los posibles tipos constituyentes son el tipo de una constante, campo, propiedad, indizador o evento, el tipo de valor devuelto de un método u operador y los tipos de parámetro de un método, indizador, operador o constructor de instancia.Possible constituent types are the type of a constant, field, property, event, or indexer, the return type of a method or operator, and the parameter types of a method, indexer, operator, or instance constructor. Los tipos constituyentes de un miembro deben ser al menos tan accesibles como el propio miembro (restricciones de accesibilidad).The constituent types of a member must be at least as accessible as that member itself (Accessibility constraints).

Miembros estáticos y de instanciaStatic and instance members

Son miembros de una clase miembros estáticos o miembros de instancia.Members of a class are either static members or instance members. Por lo general, resulta útil pensar en los miembros estáticos pertenecen a tipos de clases y miembros de instancia como que pertenecen a objetos (instancias de tipos de clase).Generally speaking, it is useful to think of static members as belonging to class types and instance members as belonging to objects (instances of class types).

Cuando una declaración de campo, método, propiedad, evento, operador o constructor incluye un static modificador, declara un miembro estático.When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. Además, una declaración de constante o tipo declara implícitamente un miembro estático.In addition, a constant or type declaration implicitly declares a static member. Los miembros estáticos tienen las siguientes características:Static members have the following characteristics:

  • Cuando un miembro estático M se hace referencia en un member_access (acceso a miembros) del formulario E.M, E debe denotar un tipo que contenga M.When a static member M is referenced in a member_access (Member access) of the form E.M, E must denote a type containing M. Es un error en tiempo de compilación para E para denotar una instancia.It is a compile-time error for E to denote an instance.
  • Un campo estático identifica exactamente una ubicación de almacenamiento para ser compartidos por todas las instancias de un tipo determinado de clases cerrado.A static field identifies exactly one storage location to be shared by all instances of a given closed class type. Independientemente del número de instancias de un tipo de clase cerrado determinado se crea, hay solo una copia de un campo estático.No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.
  • Un miembro de función estático (método, propiedad, evento, operador o constructor) no funciona en una instancia concreta, y es un error en tiempo de compilación para hacer referencia a this en dicho miembro de función.A static function member (method, property, event, operator, or constructor) does not operate on a specific instance, and it is a compile-time error to refer to this in such a function member.

Cuando una declaración de campo, método, propiedad, evento, indizador, constructor o destructor no incluye un static modificador, declara un miembro de instancia.When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. (Un miembro de instancia a veces se denomina a un miembro no estático.) Los miembros de instancia tienen las siguientes características:(An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

  • Cuando un miembro de instancia M se hace referencia en un member_access (acceso a miembros) del formulario E.M, E debe denotar una instancia de un tipo que contiene M.When an instance member M is referenced in a member_access (Member access) of the form E.M, E must denote an instance of a type containing M. Es un error en tiempo de enlace para E para denotar un tipo.It is a binding-time error for E to denote a type.
  • Todas las instancias de una clase contiene un conjunto independiente de todos los campos de instancia de la clase.Every instance of a class contains a separate set of all instance fields of the class.
  • Un miembro de función de instancia (método, propiedad, indizador, constructor de instancia o destructor) funciona en una instancia determinada de la clase, y puede tener acceso a esta instancia como this (este acceso).An instance function member (method, property, indexer, instance constructor, or destructor) operates on a given instance of the class, and this instance can be accessed as this (This access).

El ejemplo siguiente muestra las reglas para tener acceso a estáticas y miembros de instancia:The following example illustrates the rules for accessing static and instance members:

class Test
{
    int x;
    static int y;

    void F() {
        x = 1;            // Ok, same as this.x = 1
        y = 1;            // Ok, same as Test.y = 1
    }

    static void G() {
        x = 1;            // Error, cannot access this.x
        y = 1;            // Ok, same as Test.y = 1
    }

    static void Main() {
        Test t = new Test();
        t.x = 1;          // Ok
        t.y = 1;          // Error, cannot access static member through instance
        Test.x = 1;       // Error, cannot access instance member through type
        Test.y = 1;       // Ok
    }
}

El F método que muestra en un miembro de función de instancia, un simple_name (nombres simples) puede utilizarse para tener acceso a los miembros de instancia y miembros estáticos.The F method shows that in an instance function member, a simple_name (Simple names) can be used to access both instance members and static members. El G método muestra que en un miembro de función estático, se produce un error en tiempo de compilación para tener acceso a un miembro de instancia a través de un simple_name.The G method shows that in a static function member, it is a compile-time error to access an instance member through a simple_name. El Main método muestra que en un member_access (acceso a miembros), deben tener acceso a los miembros de instancia a través de instancias, y deben tener acceso a los miembros estáticos a través de tipos.The Main method shows that in a member_access (Member access), instance members must be accessed through instances, and static members must be accessed through types.

Tipos anidadosNested types

Se llama a un tipo declarado dentro de una declaración de clase o struct un tipo anidado.A type declared within a class or struct declaration is called a nested type. Un tipo que se declara dentro de una unidad de compilación o espacio de nombres se denomina un tipo no anidado.A type that is declared within a compilation unit or namespace is called a non-nested type.

En el ejemploIn the example

using System;

class A
{
    class B
    {
        static void F() {
            Console.WriteLine("A.B.F");
        }
    }
}

clase B es un tipo anidado porque se declara dentro de la clase Ay la clase A es un tipo no anidado porque se declara dentro de una unidad de compilación.class B is a nested type because it is declared within class A, and class A is a non-nested type because it is declared within a compilation unit.

Nombre completoFully qualified name

El nombre completo (nombres completos) para un tipo anidado es S.N donde S es el nombre completo del tipo en el tipo N se declara.The fully qualified name (Fully qualified names) for a nested type is S.N where S is the fully qualified name of the type in which type N is declared.

Accesibilidad declaradaDeclared accessibility

Los tipos no anidados pueden tener public o internal accesibilidad declarada y tener internal accesibilidad declarada de forma predeterminada.Non-nested types can have public or internal declared accessibility and have internal declared accessibility by default. Los tipos anidados pueden tener estas formas de accesibilidad declarada demasiado, además de uno o más formas adicionales de accesibilidad declarada, dependiendo de si el tipo contenedor es una clase o struct:Nested types can have these forms of declared accessibility too, plus one or more additional forms of declared accessibility, depending on whether the containing type is a class or struct:

  • Un tipo anidado que se declara en una clase puede tener cualquiera de las cinco formas de accesibilidad declarada (public, protected internal, protected, internal, o private) y, al igual que otros miembros de clase, el valor predeterminado es private declarado accesibilidad.A nested type that is declared in a class can have any of five forms of declared accessibility (public, protected internal, protected, internal, or private) and, like other class members, defaults to private declared accessibility.
  • Un tipo anidado que se declara en un struct puede tener cualquiera de las tres formas de accesibilidad declarada (public, internal, o private) y, al igual que otros miembros de struct, el valor predeterminado es private accesibilidad declarada.A nested type that is declared in a struct can have any of three forms of declared accessibility (public, internal, or private) and, like other struct members, defaults to private declared accessibility.

El ejemploThe example

public class List
{
    // Private data structure
    private class Node
    { 
        public object Data;
        public Node Next;

        public Node(object data, Node next) {
            this.Data = data;
            this.Next = next;
        }
    }

    private Node first = null;
    private Node last = null;

    // Public interface
    public void AddToFront(object o) {...}
    public void AddToBack(object o) {...}
    public object RemoveFromFront() {...}
    public object RemoveFromBack() {...}
    public int Count { get {...} }
}

declara una clase anidada privada Node.declares a private nested class Node.

OcultarHiding

Puede ocultar un tipo anidado (ocultación de nombres) un miembro base.A nested type may hide (Name hiding) a base member. El new modificador está permitido en declaraciones de tipo anidado para que se puede expresar explícitamente si se oculta.The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. El ejemploThe example

using System;

class Base
{
    public static void M() {
        Console.WriteLine("Base.M");
    }
}

class Derived: Base 
{
    new public class M 
    {
        public static void F() {
            Console.WriteLine("Derived.M.F");
        }
    }
}

class Test 
{
    static void Main() {
        Derived.M.F();
    }
}

muestra una clase anidada M que oculta el método M definido en Base.shows a nested class M that hides the method M defined in Base.

Este accesothis access

Un tipo anidado y su tipo contenedor no tiene una relación especial con respecto a this_access (este acceso).A nested type and its containing type do not have a special relationship with regard to this_access (This access). En concreto, this dentro de un tipo anidado no puede usarse para hacer referencia a los miembros de instancia del tipo contenedor.Specifically, this within a nested type cannot be used to refer to instance members of the containing type. En casos donde un tipo anidado tiene acceso a los miembros de instancia de su tipo contenedor, se puede proporcionar acceso proporcionando el this para la instancia del tipo contenedor como un argumento de constructor para el tipo anidado.In cases where a nested type needs access to the instance members of its containing type, access can be provided by providing the this for the instance of the containing type as a constructor argument for the nested type. El ejemplo siguienteThe following example

using System;

class C
{
    int i = 123;

    public void F() {
        Nested n = new Nested(this);
        n.G();
    }

    public class Nested
    {
        C this_c;

        public Nested(C c) {
            this_c = c;
        }

        public void G() {
            Console.WriteLine(this_c.i);
        }
    }
}

class Test
{
    static void Main() {
        C c = new C();
        c.F();
    }
}

se muestra esta técnica.shows this technique. Una instancia de C crea una instancia de Nested y pasa su propio this a Nesteddel constructor con el fin de proporcionar acceso posterior a Cde miembros de instancia.An instance of C creates an instance of Nested and passes its own this to Nested's constructor in order to provide subsequent access to C's instance members.

Acceso a los miembros privados y protegidos del tipo contenedorAccess to private and protected members of the containing type

Un tipo anidado tiene acceso a todos los miembros que son accesibles para su tipo contenedor, incluidos los miembros del tipo contenedor que tienen private y protected accesibilidad declarada.A nested type has access to all of the members that are accessible to its containing type, including members of the containing type that have private and protected declared accessibility. El ejemploThe example

using System;

class C 
{
    private static void F() {
        Console.WriteLine("C.F");
    }

    public class Nested 
    {
        public static void G() {
            F();
        }
    }
}

class Test 
{
    static void Main() {
        C.Nested.G();
    }
}

se muestra una clase C que contiene una clase anidada Nested.shows a class C that contains a nested class Nested. Dentro de Nested, el método G llama al método estático F definido en C, y F declarado privado accesibilidad.Within Nested, the method G calls the static method F defined in C, and F has private declared accessibility.

Un tipo anidado también puede tener acceso a miembros protegidos que se definen en un tipo base de su tipo contenedor.A nested type also may access protected members defined in a base type of its containing type. En el ejemploIn the example

using System;

class Base 
{
    protected void F() {
        Console.WriteLine("Base.F");
    }
}

class Derived: Base 
{
    public class Nested 
    {
        public void G() {
            Derived d = new Derived();
            d.F();        // ok
        }
    }
}

class Test 
{
    static void Main() {
        Derived.Nested n = new Derived.Nested();
        n.G();
    }
}

la clase anidada Derived.Nested tiene acceso al método protegido F definido en Derivedde la clase base Base, por una llamada a través de una instancia de Derived.the nested class Derived.Nested accesses the protected method F defined in Derived's base class, Base, by calling through an instance of Derived.

Tipos anidados en clases genéricasNested types in generic classes

Una declaración de clase genérica puede contener declaraciones de tipo anidado.A generic class declaration can contain nested type declarations. Los parámetros de tipo de la clase envolvente pueden utilizarse dentro de los tipos anidados.The type parameters of the enclosing class can be used within the nested types. Una declaración de tipo anidado puede contener parámetros de tipo adicionales que se aplican solo al tipo anidado.A nested type declaration can contain additional type parameters that apply only to the nested type.

Cada declaración de tipos contenido dentro de una declaración de clase genérica es implícitamente una declaración de tipo genérico.Every type declaration contained within a generic class declaration is implicitly a generic type declaration. Cuando escribe una referencia a un tipo anidado dentro de un tipo genérico, el tipo construido contenedor, incluidos sus argumentos de tipo, debe tener nombres.When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, must be named. Sin embargo, desde dentro de la clase externa, el tipo anidado puede utilizar sin calificación; el tipo de instancia de la clase externa puede utilizarse de forma implícita al construir el tipo anidado.However, from within the outer class, the nested type can be used without qualification; the instance type of the outer class can be implicitly used when constructing the nested type. El ejemplo siguiente muestra tres maneras diferentes para hacer referencia a un tipo construido creado a partir de Inner; los dos primeros son equivalentes:The following example shows three different correct ways to refer to a constructed type created from Inner; the first two are equivalent:

class Outer<T>
{
    class Inner<U>
    {
        public static void F(T t, U u) {...}
    }

    static void F(T t) {
        Outer<T>.Inner<string>.F(t, "abc");      // These two statements have
        Inner<string>.F(t, "abc");               // the same effect

        Outer<int>.Inner<string>.F(3, "abc");    // This type is different

        Outer.Inner<string>.F(t, "abc");         // Error, Outer needs type arg
    }
}

Aunque es recomendable hacerla programación estilo, un parámetro de tipo en un tipo anidado puede ocultar a un miembro o escribir parámetro declarado en el tipo externo:Although it is bad programming style, a type parameter in a nested type can hide a member or type parameter declared in the outer type:

class Outer<T>
{
    class Inner<T>        // Valid, hides Outer's T
    {
        public T t;       // Refers to Inner's T
    }
}

Nombres de miembro reservadoReserved member names

Para facilitar la subyacente implementación C# tiempo de ejecución, para cada declaración de miembro de origen es una propiedad, evento o indizador, la implementación debe reservar dos firmas de método según el tipo de la declaración de miembro, su nombre y su tipo.To facilitate the underlying C# run-time implementation, for each source member declaration that is a property, event, or indexer, the implementation must reserve two method signatures based on the kind of the member declaration, its name, and its type. Es un error en tiempo de compilación para un programa declarar un miembro cuya firma coincida con una de estas firmas reservadas, incluso si la implementación subyacente de tiempo de ejecución no hace uso de estas reservas.It is a compile-time error for a program to declare a member whose signature matches one of these reserved signatures, even if the underlying run-time implementation does not make use of these reservations.

Los nombres reservados no presentan declaraciones, por lo tanto no participan en la búsqueda de miembros.The reserved names do not introduce declarations, thus they do not participate in member lookup. Sin embargo, una declaración asociada al método reservado firmas participan en la herencia (herencia) y puede estar oculto con el new modificador (el nuevo modificador).However, a declaration's associated reserved method signatures do participate in inheritance (Inheritance), and can be hidden with the new modifier (The new modifier).

La reserva de estos nombres desempeña tres funciones:The reservation of these names serves three purposes:

  • Para permitir la implementación subyacente utilizar un identificador normal como un nombre de método para obtener o establecer el acceso a la característica del lenguaje C#.To allow the underlying implementation to use an ordinary identifier as a method name for get or set access to the C# language feature.
  • Para permitir que otros lenguajes interoperar con un identificador normal como un nombre de método para obtener o establecer el acceso a la característica del lenguaje C#.To allow other languages to interoperate using an ordinary identifier as a method name for get or set access to the C# language feature.
  • Para ayudar a garantizar que el origen aceptado por un compilador adecuado es aceptado por el otro, mediante la realización de las particularidades del miembro reservado nombres coherentes en todas las implementaciones de C#.To help ensure that the source accepted by one conforming compiler is accepted by another, by making the specifics of reserved member names consistent across all C# implementations.

La declaración de un destructor (destructores) también hace que una firma reservar (nombres de miembros reservados para los destructores).The declaration of a destructor (Destructors) also causes a signature to be reserved (Member names reserved for destructors).

Nombres de miembro reservados para las propiedadesMember names reserved for properties

Para una propiedad P (propiedades) de tipo T, las firmas siguientes están reservadas:For a property P (Properties) of type T, the following signatures are reserved:

T get_P();
void set_P(T value);

Tanto las firmas están reservadas, incluso si la propiedad es de solo lectura o solo escritura.Both signatures are reserved, even if the property is read-only or write-only.

En el ejemploIn the example

using System;

class A
{
    public int P {
        get { return 123; }
    }
}

class B: A
{
    new public int get_P() {
        return 456;
    }

    new public void set_P(int value) {
    }
}

class Test
{
    static void Main() {
        B b = new B();
        A a = b;
        Console.WriteLine(a.P);
        Console.WriteLine(b.P);
        Console.WriteLine(b.get_P());
    }
}

una clase A define una propiedad de solo lectura P, reservando así las firmas para get_P y set_P métodos.a class A defines a read-only property P, thus reserving signatures for get_P and set_P methods. Una clase B deriva A y oculta ambas de estas firmas reservadas.A class B derives from A and hides both of these reserved signatures. El ejemplo genera el resultado:The example produces the output:

123
123
456

Nombres de miembros reservados para los eventosMember names reserved for events

Para un evento E (eventos) del tipo de delegado T, las firmas siguientes están reservadas:For an event E (Events) of delegate type T, the following signatures are reserved:

void add_E(T handler);
void remove_E(T handler);

Nombres de miembros reservados para los indizadoresMember names reserved for indexers

Para un indizador (indizadores) de tipo T con lista de parámetros L, las firmas siguientes están reservadas:For an indexer (Indexers) of type T with parameter-list L, the following signatures are reserved:

T get_Item(L);
void set_Item(L, T value);

Tanto las firmas están reservadas, incluso si el indizador es de solo lectura o solo escritura.Both signatures are reserved, even if the indexer is read-only or write-only.

Además del nombre de miembro Item está reservado.Furthermore the member name Item is reserved.

Nombres de miembros reservados para los destructoresMember names reserved for destructors

Para una clase que contiene un destructor (destructores), se reserva la firma siguiente:For a class containing a destructor (Destructors), the following signature is reserved:

void Finalize();

ConstantesConstants

Un constante es un miembro de clase que representa un valor constante: un valor que se puede calcular en tiempo de compilación.A constant is a class member that represents a constant value: a value that can be computed at compile-time. Un constant_declaration presenta una o varias constantes de un tipo determinado.A constant_declaration introduces one or more constants of a given type.

constant_declaration
    : attributes? constant_modifier* 'const' type constant_declarators ';'
    ;

constant_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    ;

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Un constant_declaration puede incluir un conjunto de atributos (atributos), un new modificador (el nuevo modificador), y una combinación válida de los cuatro modificadores de acceso (modificadores de acceso).A constant_declaration may include a set of attributes (Attributes), a new modifier (The new modifier), and a valid combination of the four access modifiers (Access modifiers). Los atributos y modificadores se aplican a todos los miembros declarados por el constant_declaration.The attributes and modifiers apply to all of the members declared by the constant_declaration. Aunque las constantes se consideran miembros estáticos, un constant_declaration no requiere ni permite un static modificador.Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. Es un error para el mismo modificador aparezca varias veces en una declaración de constante.It is an error for the same modifier to appear multiple times in a constant declaration.

El tipo de un constant_declaration especifica el tipo de los miembros introducidos por la declaración.The type of a constant_declaration specifies the type of the members introduced by the declaration. El tipo es seguido por una lista de constant_declarators, cada uno de los cuales incluye un nuevo miembro.The type is followed by a list of constant_declarators, each of which introduces a new member. Un constant_declarator consta de un identificador que designa el miembro, seguido de un "=" símbolo (token), seguido de un constant_expression ( Expresiones constantes) que proporciona el valor del miembro.A constant_declarator consists of an identifier that names the member, followed by an "=" token, followed by a constant_expression (Constant expressions) that gives the value of the member.

El tipo especificado en una constante debe ser declaración sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, un enum_type, o un reference_type.The type specified in a constant declaration must be sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, an enum_type, or a reference_type. Cada constant_expression debe dar un valor del tipo de destino o de un tipo que pueda convertirse al tipo de destino mediante una conversión implícita (conversiones implícitas).Each constant_expression must yield a value of the target type or of a type that can be converted to the target type by an implicit conversion (Implicit conversions).

El tipo de una constante debe ser al menos tan accesibles como la propia constante (restricciones de accesibilidad).The type of a constant must be at least as accessible as the constant itself (Accessibility constraints).

Se obtiene el valor de una constante en una expresión que utiliza un simple_name (nombres simples) o un member_access (acceso a miembros).The value of a constant is obtained in an expression using a simple_name (Simple names) or a member_access (Member access).

Una constante puede participar en una constant_expression.A constant can itself participate in a constant_expression. Por lo tanto, se puede utilizar una constante en cualquier constructor que requiere un constant_expression.Thus, a constant may be used in any construct that requires a constant_expression. Ejemplos de tales construcciones case etiquetas, goto case instrucciones, enum las declaraciones de miembros, atributos y otras declaraciones de constantes.Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

Como se describe en expresiones constantes, un constant_expression es una expresión que se puede evaluar por completo en tiempo de compilación.As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time. La única forma para crear un valor distinto de null de un reference_type distinto string consiste en aplicar el new operador y, puesto que el new operador no está permitido en un constant_ expresión, el único valor posible para las constantes de reference_types distinto string es null.Since the only way to create a non-null value of a reference_type other than string is to apply the new operator, and since the new operator is not permitted in a constant_expression, the only possible value for constants of reference_types other than string is null.

Cuando se desea un nombre simbólico para un valor constante, pero cuando el tipo de ese valor no se permite en una declaración de constante o cuando no se puede calcular el valor en tiempo de compilación por una constant_expression, un readonly campo () Campos de solo lectura) se puede usar en su lugar.When a symbolic name for a constant value is desired, but when the type of that value is not permitted in a constant declaration, or when the value cannot be computed at compile-time by a constant_expression, a readonly field (Readonly fields) may be used instead.

Una declaración de constante que declara varias constantes equivale a varias declaraciones de una sola constante con los mismos atributos, los modificadores y tipo.A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. Por ejemploFor example

class A
{
    public const double X = 1.0, Y = 2.0, Z = 3.0;
}

es equivalente ais equivalent to

class A
{
    public const double X = 1.0;
    public const double Y = 2.0;
    public const double Z = 3.0;
}

Las constantes pueden depender de otras constantes dentro del mismo programa siempre y cuando las dependencias no son de naturaleza circular.Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. El compilador organiza automáticamente evaluar las declaraciones de constante en el orden adecuado.The compiler automatically arranges to evaluate the constant declarations in the appropriate order. En el ejemploIn the example

class A
{
    public const int X = B.Z + 1;
    public const int Y = 10;
}

class B
{
    public const int Z = A.Y + 1;
}

el compilador evalúa primero A.Y, a continuación, se evalúa como B.Zy, por último, se evalúa como A.X, produciendo los valores 10, 11, y 12.the compiler first evaluates A.Y, then evaluates B.Z, and finally evaluates A.X, producing the values 10, 11, and 12. Las declaraciones de constantes pueden depender de constantes de otros programas, pero dichas dependencias sólo son posibles en una dirección.Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. Que hace referencia en el ejemplo anterior, si A y B han sido declarados en programas independientes, sería posible para A.X depender B.Z, pero B.Z , a continuación, podrían depender no de forma simultánea en A.Y.Referring to the example above, if A and B were declared in separate programs, it would be possible for A.X to depend on B.Z, but B.Z could then not simultaneously depend on A.Y.

CamposFields

Un campo es un miembro que representa una variable asociada con un objeto o clase.A field is a member that represents a variable associated with an object or class. Un field_declaration presenta uno o varios campos de un tipo determinado.A field_declaration introduces one or more fields of a given type.

field_declaration
    : attributes? field_modifier* type variable_declarators ';'
    ;

field_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'readonly'
    | 'volatile'
    | field_modifier_unsafe
    ;

variable_declarators
    : variable_declarator (',' variable_declarator)*
    ;

variable_declarator
    : identifier ('=' variable_initializer)?
    ;

variable_initializer
    : expression
    | array_initializer
    ;

Un field_declaration puede incluir un conjunto de atributos (atributos), un new modificador (el nuevo modificador), un una combinación válida de los cuatro modificadores de acceso (modificadores de acceso) y un static modificador (campos estáticos y de instancia).A field_declaration may include a set of attributes (Attributes), a new modifier (The new modifier), a valid combination of the four access modifiers (Access modifiers), and a static modifier (Static and instance fields). Además, un field_declaration puede incluir un readonly modificador (campos de solo lectura) o un volatile modificador (campos volátiles), pero no ambos.In addition, a field_declaration may include a readonly modifier (Readonly fields) or a volatile modifier (Volatile fields) but not both. Los atributos y modificadores se aplican a todos los miembros declarados por el field_declaration.The attributes and modifiers apply to all of the members declared by the field_declaration. Es un error para el mismo modificador aparezca varias veces en una declaración de campo.It is an error for the same modifier to appear multiple times in a field declaration.

El tipo de un field_declaration especifica el tipo de los miembros introducidos por la declaración.The type of a field_declaration specifies the type of the members introduced by the declaration. El tipo es seguido por una lista de variable_declarators, cada uno de los cuales incluye un nuevo miembro.The type is followed by a list of variable_declarators, each of which introduces a new member. Un variable_declarator consta de un identificador que los nombres de ese miembro, seguido opcionalmente por una "=" token y un variable_initializer ( Inicializadores de variables) que proporciona el valor inicial de ese miembro.A variable_declarator consists of an identifier that names that member, optionally followed by an "=" token and a variable_initializer (Variable initializers) that gives the initial value of that member.

El tipo de un campo debe ser al menos tan accesibles como el propio campo (restricciones de accesibilidad).The type of a field must be at least as accessible as the field itself (Accessibility constraints).

Se obtiene el valor de un campo en una expresión que utiliza un simple_name (nombres simples) o un member_access (acceso a miembros).The value of a field is obtained in an expression using a simple_name (Simple names) or a member_access (Member access). El valor de un campo que no son de sólo lectura se modifica mediante una asignación (operadores de asignación).The value of a non-readonly field is modified using an assignment (Assignment operators). El valor de un campo que no son de sólo lectura puede ser obtener y modificar utilizando postfijo de incremento y decremento operadores (operadores postfijos de incremento y decremento) y el incremento de prefijo y operadores de decremento (prefijo operadores de incremento y decremento).The value of a non-readonly field can be both obtained and modified using postfix increment and decrement operators (Postfix increment and decrement operators) and prefix increment and decrement operators (Prefix increment and decrement operators).

Una declaración de campo que declara varios campos equivale a varias declaraciones de campos solo con los mismos atributos, los modificadores y tipo.A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. Por ejemploFor example

class A
{
    public static int X = 1, Y, Z = 100;
}

es equivalente ais equivalent to

class A
{
    public static int X = 1;
    public static int Y;
    public static int Z = 100;
}

Campos estáticos y de instanciaStatic and instance fields

Cuando una declaración de campo incluye un static son los campos introducidos por la declaración de modificador, campos estáticos.When a field declaration includes a static modifier, the fields introduced by the declaration are static fields. Cuando no hay ninguna static modificador está presente, los campos introducidos por la declaración son campos de instancia.When no static modifier is present, the fields introduced by the declaration are instance fields. Los campos estáticos y los campos de instancia son dos de los distintos tipos de variables (Variables) compatible con C# y, a veces se conocen como variables estáticas y las variables de instancia , respectivamente.Static fields and instance fields are two of the several kinds of variables (Variables) supported by C#, and at times they are referred to as static variables and instance variables, respectively.

Un campo estático no forma parte de una instancia concreta; en su lugar, se comparte entre todas las instancias de un tipo cerrado (abierto y cerrado tipos).A static field is not part of a specific instance; instead, it is shared amongst all instances of a closed type (Open and closed types). Independientemente del número de instancias de un tipo de clase cerrado se crea, hay solo una copia de un campo estático para el dominio de aplicación asociada.No matter how many instances of a closed class type are created, there is only ever one copy of a static field for the associated application domain.

Por ejemplo:For example:

class C<V>
{
    static int count = 0;

    public C() {
        count++;
    }

    public static int Count {
        get { return count; }
    }
}

class Application
{
    static void Main() {
        C<int> x1 = new C<int>();
        Console.WriteLine(C<int>.Count);        // Prints 1

        C<double> x2 = new C<double>();
        Console.WriteLine(C<int>.Count);        // Prints 1

        C<int> x3 = new C<int>();
        Console.WriteLine(C<int>.Count);        // Prints 2
    }
}

Un campo de instancia pertenece a una instancia.An instance field belongs to an instance. En concreto, todas las instancias de una clase contiene un conjunto independiente de todos los campos de instancia de esa clase.Specifically, every instance of a class contains a separate set of all the instance fields of that class.

Cuando se hace referencia a un campo en un member_access (acceso a miembros) del formulario E.Msi M es un campo estático, E debe denotar un tipo que contenga M y si M es un campo de instancia, E debe denotar una instancia de un tipo que contenga M.When a field is referenced in a member_access (Member access) of the form E.M, if M is a static field, E must denote a type containing M, and if M is an instance field, E must denote an instance of a type containing M.

Las diferencias entre estático y los miembros de instancia se tratan con más detalle en miembros estáticos y de instancia.The differences between static and instance members are discussed further in Static and instance members.

Campos de solo lecturaReadonly fields

Cuando un field_declaration incluye un readonly son los campos introducidos por la declaración de modificador, campos de solo lectura.When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Las asignaciones directas a campos de sólo lectura sólo pueden producirse como parte de la declaración o en un constructor de instancia o un constructor estático de la misma clase.Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class. (Un campo de solo lectura puede asignarse a varias veces en estos contextos.) En concreto, las asignaciones directas a una readonly campo solo se permiten en los contextos siguientes:(A readonly field can be assigned to multiple times in these contexts.) Specifically, direct assignments to a readonly field are permitted only in the following contexts:

  • En el variable_declarator que presenta el campo (incluyendo un variable_initializer en la declaración).In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
  • Para un campo de instancia, en los constructores de instancia de la clase que contiene la declaración de campo; para un campo estático, en el constructor estático de la clase que contiene la declaración de campo.For an instance field, in the instance constructors of the class that contains the field declaration; for a static field, in the static constructor of the class that contains the field declaration. Estos también son los únicos contextos en los que es válido pasar un readonly campo como un out o ref parámetro.These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

Intenta asignar a un readonly campo o pasarla como un out o ref parámetro en cualquier otro contexto es un error en tiempo de compilación.Attempting to assign to a readonly field or pass it as an out or ref parameter in any other context is a compile-time error.

Uso de campos de solo lectura estáticos para constantesUsing static readonly fields for constants

Un static readonly campo resulta útil cuando se desea un nombre simbólico para un valor constante, pero cuando el tipo del valor no se permite en un const declaración, o cuando no se puede calcular el valor en tiempo de compilación.A static readonly field is useful when a symbolic name for a constant value is desired, but when the type of the value is not permitted in a const declaration, or when the value cannot be computed at compile-time. En el ejemploIn the example

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);

    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

el Black, White, Red, Green, y Blue miembros no pueden declararse como const miembros porque sus valores no se puede calcular en tiempo de compilación.the Black, White, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. Sin embargo, declararlas static readonly produce el mismo efecto.However, declaring them static readonly instead has much the same effect.

Control de versiones de constantes y campos de solo lectura estáticoVersioning of constants and static readonly fields

Constantes y campos de solo lectura tienen semántica de control de versiones binarias diferentes.Constants and readonly fields have different binary versioning semantics. Cuando una expresión hace referencia a una constante, se obtiene el valor de la constante en tiempo de compilación, pero cuando una expresión hace referencia a un campo de solo lectura, no se obtiene el valor del campo hasta el tiempo de ejecución.When an expression references a constant, the value of the constant is obtained at compile-time, but when an expression references a readonly field, the value of the field is not obtained until run-time. Considere la posibilidad de una aplicación que consta de dos programas independientes:Consider an application that consists of two separate programs:

using System;

namespace Program1
{
    public class Utils
    {
        public static readonly int X = 1;
    }
}

namespace Program2
{
    class Test
    {
        static void Main() {
            Console.WriteLine(Program1.Utils.X);
        }
    }
}

El Program1 y Program2 espacios de nombres indican dos programas que están compilados por separado.The Program1 and Program2 namespaces denote two programs that are compiled separately. Dado que Program1.Utils.X se declara como un campo estático de solo lectura, el resultado de la Console.WriteLine instrucción no se conoce en tiempo de compilación, sino que se obtiene en tiempo de ejecución.Because Program1.Utils.X is declared as a static readonly field, the value output by the Console.WriteLine statement is not known at compile-time, but rather is obtained at run-time. Por lo tanto, si el valor de X se cambia y Program1 se vuelve a compilar, el Console.WriteLine instrucción dará como resultado el nuevo valor incluso si Program2 no se vuelven a compilar.Thus, if the value of X is changed and Program1 is recompiled, the Console.WriteLine statement will output the new value even if Program2 isn't recompiled. Sin embargo, tenía X sido una constante, el valor de X habría obtenido en el momento Program2 se compiló y no se verán afectadas por cambios en Program1 hasta Program2 se vuelve a compilar.However, had X been a constant, the value of X would have been obtained at the time Program2 was compiled, and would remain unaffected by changes in Program1 until Program2 is recompiled.

Campos volátilesVolatile fields

Cuando un field_declaration incluye un volatile son los campos que aparecen en la declaración de modificador, campos volátiles.When a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields.

Para los campos volátiles, técnicas de optimización que reordenan las instrucciones pueden provocar resultados inesperados e impredecibles en programas multiproceso que tienen acceso a los campos sin sincronización como la proporcionada por el lock_statement (La instrucción lock).For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the lock_statement (The lock statement). Estas optimizaciones pueden ser realizadas por el compilador, el sistema de tiempo de ejecución o el hardware.These optimizations can be performed by the compiler, by the run-time system, or by hardware. Para los campos volátiles, dichas optimizaciones de reordenación están restringidos:For volatile fields, such reordering optimizations are restricted:

  • Se llama a una lectura de un campo volátil un lectura volátil.A read of a volatile field is called a volatile read. Una lectura volátil tiene "semántica de adquisición"; es decir, se garantiza que se produzca antes de cualquier referencia a la memoria que se produce después de él en la secuencia de instrucciones.A volatile read has "acquire semantics"; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence.
  • Se llama a una operación de escritura de un campo volátil un escritura volátil.A write of a volatile field is called a volatile write. Una escritura volátil tiene "semántica de liberación"; es decir, se garantiza que producirse después de todas las referencias de memoria antes de la instrucción de escritura en la secuencia de instrucciones.A volatile write has "release semantics"; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence.

Estas restricciones aseguran que todos los subprocesos observarán las operaciones de escritura volátiles realizadas por cualquier otro subproceso en el orden en que se realizaron.These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. Una implementación compatible no es necesario para proporcionar una ordenación total única de las escrituras volátiles como se muestra en todos los subprocesos de ejecución.A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. El tipo de un campo volátil debe ser uno de los siguientes:The type of a volatile field must be one of the following:

  • Un reference_type.A reference_type.
  • El tipo byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, oSystem.UIntPtr.The type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, orSystem.UIntPtr.
  • Un enum_type con un tipo de base de enumeración de byte, sbyte, short, ushort, int, o uint.An enum_type having an enum base type of byte, sbyte, short, ushort, int, or uint.

El ejemploThe example

using System;
using System.Threading;

class Test
{
    public static int result;   
    public static volatile bool finished;

    static void Thread2() {
        result = 143;    
        finished = true; 
    }

    static void Main() {
        finished = false;

        // Run Thread2() in a new thread
        new Thread(new ThreadStart(Thread2)).Start();

        // Wait for Thread2 to signal that it has a result by setting
        // finished to true.
        for (;;) {
            if (finished) {
                Console.WriteLine("result = {0}", result);
                return;
            }
        }
    }
}

genera el resultado:produces the output:

result = 143

En este ejemplo, el método Main inicia un nuevo subproceso que ejecuta el método Thread2.In this example, the method Main starts a new thread that runs the method Thread2. Este método almacena un valor en un campo no volátil denominado result, a continuación, almacena true en el campo volátil finished.This method stores a value into a non-volatile field called result, then stores true in the volatile field finished. El subproceso principal espera para el campo finished para establecerse en true, a continuación, lee el campo result.The main thread waits for the field finished to be set to true, then reads the field result. Puesto que finished se ha declarado volatile, el subproceso principal debe leer el valor 143 desde el campo result.Since finished has been declared volatile, the main thread must read the value 143 from the field result. Si el campo finished no se declararon volatile, estaría permitida para el almacén de result sea visible para el subproceso principal después de la tienda a finishedy por lo tanto, para el subproceso principal leer el valor 0 desde el campo result.If the field finished had not been declared volatile, then it would be permissible for the store to result to be visible to the main thread after the store to finished, and hence for the main thread to read the value 0 from the field result. Declarar finished como un volatile campo impide tales incoherencias.Declaring finished as a volatile field prevents any such inconsistency.

Inicialización de camposField initialization

El valor inicial de un campo, ya sea en un campo estático o un campo de instancia, es el valor predeterminado (los valores predeterminados) del tipo del campo.The initial value of a field, whether it be a static field or an instance field, is the default value (Default values) of the field's type. No es posible observar el valor de un campo antes de que se ha producido esta inicialización predeterminada y un campo, por tanto, nunca es "no inicializado".It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never "uninitialized". El ejemploThe example

using System;

class Test
{
    static bool b;
    int i;

    static void Main() {
        Test t = new Test();
        Console.WriteLine("b = {0}, i = {1}", b, t.i);
    }
}

genera el resultadoproduces the output

b = False, i = 0

Dado que b y i se inicializan automáticamente en valores predeterminados.because b and i are both automatically initialized to default values.

Inicializadores de variablesVariable initializers

Pueden incluir declaraciones de campo variable_initializers.Field declarations may include variable_initializers. Para los campos estáticos, los inicializadores de variables corresponden a instrucciones de asignación que se ejecutan durante la inicialización de clase.For static fields, variable initializers correspond to assignment statements that are executed during class initialization. Por ejemplo, campos, los inicializadores de variables corresponden a instrucciones de asignación que se ejecutan cuando se crea una instancia de la clase.For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

El ejemploThe example

using System;

class Test
{
    static double x = Math.Sqrt(2.0);
    int i = 100;
    string s = "Hello";

    static void Main() {
        Test a = new Test();
        Console.WriteLine("x = {0}, i = {1}, s = {2}", x, a.i, a.s);
    }
}

genera el resultadoproduces the output

x = 1.4142135623731, i = 100, s = Hello

Dado que una asignación a x se produce cuando se ejecutan los inicializadores de campo estáticos y las asignaciones a i y s se producen al ejecutan los inicializadores de campo de instancia.because an assignment to x occurs when static field initializers execute and assignments to i and s occur when the instance field initializers execute.

La inicialización de un valor predeterminado se describe en inicialización de campos se produce para todos los campos, incluidos los campos que tienen los inicializadores de variables.The default value initialization described in Field initialization occurs for all fields, including fields that have variable initializers. Por lo tanto, cuando se inicializa una clase, todos los campos estáticos en esa clase primero se inicializan a sus valores predeterminados y, a continuación, los inicializadores de campo estático se ejecutan en orden textual.Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order. Del mismo modo, cuando se crea una instancia de una clase, todos los campos de instancia de dicha instancia se inicializan por primera vez en sus valores predeterminados y, a continuación, los inicializadores de campo de instancia se ejecutan en orden textual.Likewise, when an instance of a class is created, all instance fields in that instance are first initialized to their default values, and then the instance field initializers are executed in textual order.

Es posible que los campos estáticos con inicializadores de variables que se deben observar en su estado de valor predeterminado.It is possible for static fields with variable initializers to be observed in their default value state. Sin embargo, esto no se recomienda como una cuestión de estilo.However, this is strongly discouraged as a matter of style. El ejemploThe example

using System;

class Test
{
    static int a = b + 1;
    static int b = a + 1;

    static void Main() {
        Console.WriteLine("a = {0}, b = {1}", a, b);
    }
}

muestra este comportamiento.exhibits this behavior. A pesar de las definiciones circulares de un y b, el programa es válido.Despite the circular definitions of a and b, the program is valid. Genera la salidaIt results in the output

a = 1, b = 2

Dado que los campos estáticos a y b se inicializan en 0 (el valor predeterminado de int) antes de que se ejecutan sus inicializadores.because the static fields a and b are initialized to 0 (the default value for int) before their initializers are executed. Cuando el inicializador para a se ejecuta, el valor de b es cero de modo que a se inicializa en 1.When the initializer for a runs, the value of b is zero, and so a is initialized to 1. Cuando el inicializador para b se ejecuta, el valor de a ya está 1de modo que b se inicializa en 2.When the initializer for b runs, the value of a is already 1, and so b is initialized to 2.

Inicialización de campos estáticosStatic field initialization

Los inicializadores de variable de campo estático de una clase corresponden a una secuencia de asignaciones que se ejecutan en el orden textual en que aparecen en la declaración de clase.The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. Si un constructor estático (constructores estáticos) existe en la clase, la ejecución de los inicializadores de campo estático, se produce inmediatamente antes de ejecutar ese constructor estático.If a static constructor (Static constructors) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. En caso contrario, los inicializadores de campo estático se ejecutan en el momento antes del primer uso de un campo estático de esa clase dependen de la implementación.Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. El ejemploThe example

using System;

class Test 
{ 
    static void Main() {
        Console.WriteLine("{0} {1}", B.Y, A.X);
    }

    public static int F(string s) {
        Console.WriteLine(s);
        return 1;
    }
}

class A
{
    public static int X = Test.F("Init A");
}

class B
{
    public static int Y = Test.F("Init B");
}

podría producir el resultado:might produce either the output:

Init A
Init B
1 1

o la salida:or the output:

Init B
Init A
1 1

Dado que la ejecución de Xdel inicializador y Ydel inicializador podría producir en cualquier orden; sólo están restringidos que se produzca antes de las referencias a esos campos.because the execution of X's initializer and Y's initializer could occur in either order; they are only constrained to occur before the references to those fields. Sin embargo, en el ejemplo:However, in the example:

using System;

class Test
{
    static void Main() {
        Console.WriteLine("{0} {1}", B.Y, A.X);
    }

    public static int F(string s) {
        Console.WriteLine(s);
        return 1;
    }
}

class A
{
    static A() {}

    public static int X = Test.F("Init A");
}

class B
{
    static B() {}

    public static int Y = Test.F("Init B");
}

el resultado debe ser:the output must be:

Init B
Init A
1 1

Dado que las reglas de ejecución de los constructores estáticos (tal como se define en constructores estáticos) que proporcionan Bdel constructor estático (y por lo tanto, Bdel inicializadores de campo estático) debe ejecutar antes de Adel constructor estático y los inicializadores de campo.because the rules for when static constructors execute (as defined in Static constructors) provide that B's static constructor (and hence B's static field initializers) must run before A's static constructor and field initializers.

Inicialización de campos de instanciaInstance field initialization

Los inicializadores de variable de un campo de instancia de una clase se corresponden con una secuencia de asignaciones que se ejecutan inmediatamente después de la entrada a cualquiera de los constructores de instancias (inicializadores de Constructor) de esa clase.The instance field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to any one of the instance constructors (Constructor initializers) of that class. Los inicializadores de variable se ejecutan en el orden textual en que aparecen en la declaración de clase.The variable initializers are executed in the textual order in which they appear in the class declaration. El proceso de creación e inicialización de la instancia de clase se describe más detalladamente en constructores de instancias.The class instance creation and initialization process is described further in Instance constructors.

Un inicializador de variable para un campo de instancia no puede hacer referencia a la instancia que se está creando.A variable initializer for an instance field cannot reference the instance being created. Por lo tanto, es un error en tiempo de compilación para hacer referencia a this en un inicializador de variable, tal como se produce un error de tiempo de compilación de un inicializador de variable hacer referencia a cualquier miembro de instancia a través de un simple_name.Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple_name. En el ejemploIn the example

class A
{
    int x = 1;
    int y = x + 1;        // Error, reference to instance member of this
}

el inicializador de variable para y da como resultado un error en tiempo de compilación porque hace referencia a un miembro de la instancia que se va a crear.the variable initializer for y results in a compile-time error because it references a member of the instance being created.

MétodosMethods

Un método es un miembro que implementa un cálculo o una acción que puede realizar un objeto o una clase.A method is a member that implements a computation or action that can be performed by an object or class. Los métodos se declaran mediante method_declarations:Methods are declared using method_declarations:

method_declaration
    : method_header method_body
    ;

method_header
    : attributes? method_modifier* 'partial'? return_type member_name type_parameter_list?
      '(' formal_parameter_list? ')' type_parameter_constraints_clause*
    ;

method_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | 'async'
    | method_modifier_unsafe
    ;

return_type
    : type
    | 'void'
    ;

member_name
    : identifier
    | interface_type '.' identifier
    ;

method_body
    : block
    | '=>' expression ';'
    | ';'
    ;

Un method_declaration puede incluir un conjunto de atributos (atributos) y una combinación válida de los cuatro modificadores de acceso (modificadores de acceso ), el new (el nuevo modificador), static (métodos estáticos y de instancia), virtual (métodos virtuales), override (Invalidar métodos), sealed (sellado métodos), abstract (métodos abstractos), y extern (Métodos externos) modificadores.A method_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), static (Static and instance methods), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

Una declaración tiene una combinación válida de modificadores si se cumplen todos los elementos siguientes:A declaration has a valid combination of modifiers if all of the following are true:

  • La declaración incluye una combinación válida de modificadores de acceso (modificadores de acceso).The declaration includes a valid combination of access modifiers (Access modifiers).
  • La declaración no incluye el mismo modificador varias veces.The declaration does not include the same modifier multiple times.
  • La declaración incluye al menos uno de los siguientes modificadores: static, virtual, y override.The declaration includes at most one of the following modifiers: static, virtual, and override.
  • La declaración incluye al menos uno de los siguientes modificadores: new y override.The declaration includes at most one of the following modifiers: new and override.
  • Si la declaración incluye el abstract modificador, a continuación, en la declaración no incluye ninguno de los siguientes modificadores: static, virtual, sealed o extern.If the declaration includes the abstract modifier, then the declaration does not include any of the following modifiers: static, virtual, sealed or extern.
  • Si la declaración incluye el private modificador, a continuación, en la declaración no incluye ninguno de los siguientes modificadores: virtual, override, o abstract.If the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual, override, or abstract.
  • Si la declaración incluye el sealed modificador, a continuación, en la declaración también incluye el override modificador.If the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • Si la declaración incluye el partial modificador, a continuación, que no incluye ninguno de los siguientes modificadores: new, public, protected, internal, private, virtual, sealed, override , abstract, o extern.If the declaration includes the partial modifier, then it does not include any of the following modifiers: new, public, protected, internal, private, virtual, sealed, override, abstract, or extern.

Un método que tiene el async modificador es una función asincrónica y sigue las reglas descritas en funciones asincrónicas.A method that has the async modifier is an async function and follows the rules described in Async functions.

El return_type declaración especifica el tipo del valor calculado y devuelto por el método de un método.The return_type of a method declaration specifies the type of the value computed and returned by the method. El return_type es void si el método no devuelve un valor.The return_type is void if the method does not return a value. Si la declaración incluye el partial modificador, el tipo de valor devuelto debe ser void.If the declaration includes the partial modifier, then the return type must be void.

El member_name especifica el nombre del método.The member_name specifies the name of the method. A menos que el método es una implementación de miembro de interfaz explícita (implementaciones de miembros de interfaz explícita), el member_name es simplemente un identificador.Unless the method is an explicit interface member implementation (Explicit interface member implementations), the member_name is simply an identifier. Para obtener una implementación de miembro de interfaz explícita, el member_name consta de un interface_type seguido de un "." y un identificador.For an explicit interface member implementation, the member_name consists of an interface_type followed by a "." and an identifier.

El elemento opcional type_parameter_list especifica los parámetros de tipo del método (parámetros de tipo).The optional type_parameter_list specifies the type parameters of the method (Type parameters). Si un type_parameter_list se especifica el método es un método genérico.If a type_parameter_list is specified the method is a generic method. Si el método tiene un extern modificador, un type_parameter_list no se puede especificar.If the method has an extern modifier, a type_parameter_list cannot be specified.

El elemento opcional formal_parameter_list especifica los parámetros del método (parámetros del método).The optional formal_parameter_list specifies the parameters of the method (Method parameters).

El elemento opcional type_parameter_constraints_clauses especificar restricciones en parámetros de tipo individuales (restricciones de parámetro de tipo) y solo se puede especificar si una type_parameter_ lista también se proporciona, y el método no tiene un override modificador.The optional type_parameter_constraints_clauses specify constraints on individual type parameters (Type parameter constraints) and may only be specified if a type_parameter_list is also supplied, and the method does not have an override modifier.

El return_type y cada uno de los tipos que se hace referenciados en el formal_parameter_list de un método debe ser al menos tan accesibles como el propio método (restricciones de accesibilidad).The return_type and each of the types referenced in the formal_parameter_list of a method must be at least as accessible as the method itself (Accessibility constraints).

El cuerpoMétodo ya sea un punto y coma, un cuerpo de instrucción o un cuerpo de expresión.The method_body is either a semicolon, a statement body or an expression body. Consta de un cuerpo de instrucción de un bloque, que especifica las instrucciones que se ejecutará cuando se invoca el método.A statement body consists of a block, which specifies the statements to execute when the method is invoked. Un cuerpo de expresión consta de => seguido por un expresión y un punto y coma y denota una expresión única para realizar cuando se invoca el método.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the method is invoked.

Para abstract y extern métodos, el method_body consiste simplemente en un punto y coma.For abstract and extern methods, the method_body consists simply of a semicolon. Para partial métodos el method_body puede constar de un punto y coma, un cuerpo de bloques o un cuerpo de expresión.For partial methods the method_body may consist of either a semicolon, a block body or an expression body. Para todos los demás métodos, el method_body es un cuerpo de bloques o un cuerpo de expresión.For all other methods, the method_body is either a block body or an expression body.

Si el cuerpoMétodo consta de un punto y coma, a continuación, la declaración no puede incluir el async modificador.If the method_body consists of a semicolon, then the declaration may not include the async modifier.

El nombre, la lista de parámetros de tipo y la lista de parámetros formales de un método de definen la firma (firmas y sobrecarga) del método.The name, the type parameter list and the formal parameter list of a method define the signature (Signatures and overloading) of the method. En concreto, la firma de un método está formada por su nombre, el número de parámetros de tipo y el número, modificadores y tipos de sus parámetros formales.Specifically, the signature of a method consists of its name, the number of type parameters and the number, modifiers, and types of its formal parameters. Para estos fines, se identifica cualquier parámetro de tipo del método que se produce en el tipo de un parámetro formal no por su nombre, pero por su posición ordinal en la lista de argumentos de tipo del método. El tipo de valor devuelto no forma parte de una firma de método ni es los nombres de los parámetros de tipo o los parámetros formales.For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type argument list of the method.The return type is not part of a method's signature, nor are the names of the type parameters or the formal parameters.

El nombre de un método debe ser diferente de los nombres de todos los demás de que no son métodos declarados en la misma clase.The name of a method must differ from the names of all other non-methods declared in the same class. Además, la firma de un método debe ser diferentes de las firmas de todos los demás métodos declarados en la misma clase, y dos métodos declarados en la misma clase no pueden tener firmas que se diferencien únicamente por ref y out.In addition, the signature of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out.

El método tipo_parámetroestán en el ámbito de la method_declarationy se puede usar para tipos de formulario a lo largo de ese ámbito en return_type, cuerpoMétodo, y type_parameter_constraints_clauses, pero no en atributos.The method's type_parameters are in scope throughout the method_declaration, and can be used to form types throughout that scope in return_type, method_body, and type_parameter_constraints_clauses but not in attributes.

Todos los parámetros formales y parámetros de tipo deben tener nombres diferentes.All formal parameters and type parameters must have different names.

Parámetros de métodoMethod parameters

Los parámetros de un método, si existe, se declaran en el método formal_parameter_list.The parameters of a method, if any, are declared by the method's formal_parameter_list.

formal_parameter_list
    : fixed_parameters
    | fixed_parameters ',' parameter_array
    | parameter_array
    ;

fixed_parameters
    : fixed_parameter (',' fixed_parameter)*
    ;

fixed_parameter
    : attributes? parameter_modifier? type identifier default_argument?
    ;

default_argument
    : '=' expression
    ;

parameter_modifier
    : 'ref'
    | 'out'
    | 'this'
    ;

parameter_array
    : attributes? 'params' array_type identifier
    ;

La lista de parámetros formales consta de uno o más parámetros separados por comas de los cuales sólo el último puede ser un parameter_array.The formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array.

Un fixed_parameter consta de un conjunto opcional de atributos (atributos), opcional ref, out o this modificador, un tipo, un identificador opcional default_argument.A fixed_parameter consists of an optional set of attributes (Attributes), an optional ref, out or this modifier, a type, an identifier and an optional default_argument. Cada fixed_parameter declara un parámetro del tipo especificado con el nombre especificado.Each fixed_parameter declares a parameter of the given type with the given name. El this modificador designa el método como método de extensión y solo se permite en el primer parámetro de un método estático.The this modifier designates the method as an extension method and is only allowed on the first parameter of a static method. Métodos de extensión se describen con más detalle en métodos de extensión.Extension methods are further described in Extension methods.

Un fixed_parameter con un default_argument se conoce como un parámetro opcional, mientras que un fixed_parameter sin un default_argument es un parámetro necesario.A fixed_parameter with a default_argument is known as an optional parameter, whereas a fixed_parameter without a default_argument is a required parameter. Un parámetro obligatorio no puede aparecer después de un parámetro opcional en un formal_parameter_list.A required parameter may not appear after an optional parameter in a formal_parameter_list.

Un ref o out parámetro no puede tener un default_argument.A ref or out parameter cannot have a default_argument. El expresión en un default_argument debe ser uno de los siguientes:The expression in a default_argument must be one of the following:

  • a constant_expressiona constant_expression
  • una expresión de formato new S() donde S es un tipo de valoran expression of the form new S() where S is a value type
  • una expresión de formato default(S) donde S es un tipo de valoran expression of the form default(S) where S is a value type

El expresión debe poder convertirse implícitamente por una identidad o una conversión que acepta valores NULL al tipo del parámetro.The expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.

Si los parámetros opcionales se producen en una declaración de método parcial de implementación (métodos parciales), una implementación de miembro de interfaz explícita (implementaciones de miembros de interfaz explícita) o en un declaración de indexador único parámetro (indizadores) el compilador debe mostrar una advertencia, ya que estos miembros nunca se pueden invocar de forma que permite argumentos que se omitirán.If optional parameters occur in an implementing partial method declaration (Partial methods) , an explicit interface member implementation (Explicit interface member implementations) or in a single-parameter indexer declaration (Indexers) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted.

Un parameter_array consta de un conjunto opcional de atributos (atributos), un params modificador, un array_type, y un identificador.A parameter_array consists of an optional set of attributes (Attributes), a params modifier, an array_type, and an identifier. Una matriz de parámetros declara un único parámetro del tipo de matriz determinado con el nombre especificado.A parameter array declares a single parameter of the given array type with the given name. El array_type de un parámetro de matriz debe ser un tipo de matriz unidimensional (tipos de matriz).The array_type of a parameter array must be a single-dimensional array type (Array types). En una invocación de método, una matriz de parámetros permite que un único argumento del tipo de matriz determinado que se especifique o permite cero o más argumentos de tipo de elemento de matriz que se especifique.In a method invocation, a parameter array permits either a single argument of the given array type to be specified, or it permits zero or more arguments of the array element type to be specified. Las matrices de parámetros se describen más detalladamente en las matrices de parámetros.Parameter arrays are described further in Parameter arrays.

Un parameter_array pueden aparecer después de un parámetro opcional, pero no puede tener un valor predeterminado, la omisión de argumentos para una parameter_array en su lugar, daría como resultado la creación de una matriz vacía.A parameter_array may occur after an optional parameter, but cannot have a default value -- the omission of arguments for a parameter_array would instead result in the creation of an empty array.

El ejemplo siguiente muestra los diferentes tipos de parámetros:The following example illustrates different kinds of parameters:

public void M(
    ref int      i,
    decimal      d,
    bool         b = false,
    bool?        n = false,
    string       s = "Hello",
    object       o = null,
    T            t = default(T),
    params int[] a
) { }

En el formal_parameter_list para M, i es un parámetro ref necesarios, d es un parámetro de valor requerido, b, s, o y t parámetros de valor opcional y a es una matriz de parámetros.In the formal_parameter_list for M, i is a required ref parameter, d is a required value parameter, b, s, o and t are optional value parameters and a is a parameter array.

Una declaración de método crea un espacio de declaración independiente para los parámetros, parámetros de tipo y las variables locales.A method declaration creates a separate declaration space for parameters, type parameters and local variables. Los nombres se incluyen en este espacio de declaración mediante la lista de parámetros de tipo y la lista de parámetros formales del método y las declaraciones de variable locales en el bloque del método.Names are introduced into this declaration space by the type parameter list and the formal parameter list of the method and by local variable declarations in the block of the method. Es un error para dos miembros de un espacio de declaración de método tienen el mismo nombre.It is an error for two members of a method declaration space to have the same name. Es un error para el espacio de declaración de método y el espacio de declaración de variable local de un espacio de declaración anidado contengan elementos con el mismo nombre.It is an error for the method declaration space and the local variable declaration space of a nested declaration space to contain elements with the same name.

Una invocación de método (las invocaciones de método) crea una copia, específica de esa invocación, de los parámetros formales y variables locales del método y la lista de argumentos de la invocación asigna valores o referencias a variables a la acaba de crearse parámetros formales.A method invocation (Method invocations) creates a copy, specific to that invocation, of the formal parameters and local variables of the method, and the argument list of the invocation assigns values or variable references to the newly created formal parameters. Dentro de la bloque de un método, se pueden hacer referencia a parámetros formales mediante sus identificadores en simple_name expresiones (nombres simples).Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (Simple names).

Hay cuatro tipos de parámetros formales:There are four kinds of formal parameters:

  • Parámetros de valor, que se declaran sin modificadores.Value parameters, which are declared without any modifiers.
  • Parámetros de referencia, que se declaran con la ref modificador.Reference parameters, which are declared with the ref modifier.
  • Los parámetros de salida, que se declaran con la out modificador.Output parameters, which are declared with the out modifier.
  • Las matrices de parámetros, que se declaran con la params modificador.Parameter arrays, which are declared with the params modifier.

Como se describe en firmas y sobrecarga, ref y out modificadores forman parte de una firma de método, pero la params modificador no es.As described in Signatures and overloading, the ref and out modifiers are part of a method's signature, but the params modifier is not.

Parámetros de valorValue parameters

Un parámetro declarado sin modificadores es un parámetro de valor.A parameter declared with no modifiers is a value parameter. Un parámetro de valor corresponde a una variable local que obtiene su valor inicial del argumento correspondiente proporcionado en la invocación del método.A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

Cuando un parámetro formal es un parámetro de valor, el argumento correspondiente en una invocación de método debe ser una expresión que se pueda convertir implícitamente (conversiones implícitas) para el tipo de parámetro formal.When a formal parameter is a value parameter, the corresponding argument in a method invocation must be an expression that is implicitly convertible (Implicit conversions) to the formal parameter type.

Se permite un método para asignar nuevos valores a un parámetro de valor.A method is permitted to assign new values to a value parameter. Estas asignaciones sólo afectan a la ubicación de almacenamiento local representada por el valor del parámetro, no tienen ningún efecto en el argumento real definido en la invocación del método.Such assignments only affect the local storage location represented by the value parameter—they have no effect on the actual argument given in the method invocation.

Parámetros de referenciaReference parameters

Un parámetro declarado con un ref modificador es un parámetro de referencia.A parameter declared with a ref modifier is a reference parameter. A diferencia de un parámetro de valor, un parámetro de referencia no crea una nueva ubicación de almacenamiento.Unlike a value parameter, a reference parameter does not create a new storage location. En su lugar, un parámetro de referencia representa la misma ubicación de almacenamiento que la variable especificada como argumento en la invocación del método.Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

Cuando un parámetro formal es un parámetro de referencia, el argumento correspondiente en una invocación de método debe constar de la palabra clave ref seguido por un variable_reference (reglas precisas para determinar asignación definitiva) del mismo tipo del parámetro formal.When a formal parameter is a reference parameter, the corresponding argument in a method invocation must consist of the keyword ref followed by a variable_reference (Precise rules for determining definite assignment) of the same type as the formal parameter. Una variable debe estar asignada definitivamente antes de que se puede pasar como un parámetro de referencia.A variable must be definitely assigned before it can be passed as a reference parameter.

Dentro de un método, un parámetro de referencia siempre se considera asignado definitivamente.Within a method, a reference parameter is always considered definitely assigned.

Un método declarado como iterador (iteradores) no puede tener parámetros de referencia.A method declared as an iterator (Iterators) cannot have reference parameters.

El ejemploThe example

using System;

class Test
{
    static void Swap(ref int x, ref int y) {
        int temp = x;
        x = y;
        y = temp;
    }

    static void Main() {
        int i = 1, j = 2;
        Swap(ref i, ref j);
        Console.WriteLine("i = {0}, j = {1}", i, j);
    }
}

genera el resultadoproduces the output

i = 2, j = 1

Para la invocación de Swap en Main, x representa i y y representa j.For the invocation of Swap in Main, x represents i and y represents j. Por lo tanto, la invocación tiene el efecto de intercambiar los valores de i y j.Thus, the invocation has the effect of swapping the values of i and j.

En un método que toma parámetros de referencia, es posible que varios nombres representar la misma ubicación de almacenamiento.In a method that takes reference parameters it is possible for multiple names to represent the same storage location. En el ejemploIn the example

class A
{
    string s;

    void F(ref string a, ref string b) {
        s = "One";
        a = "Two";
        b = "Three";
    }

    void G() {
        F(ref s, ref s);
    }
}

la invocación de F en G pasa una referencia al s para ambos a y b.the invocation of F in G passes a reference to s for both a and b. Por lo tanto, para esa invocación, los nombres s, a, y b todas hacen referencia a la misma ubicación de almacenamiento y las tres asignaciones modifican el campo de instancia s.Thus, for that invocation, the names s, a, and b all refer to the same storage location, and the three assignments all modify the instance field s.

Parámetros de salidaOutput parameters

Un parámetro declarado con un out modificador es un parámetro de salida.A parameter declared with an out modifier is an output parameter. Al igual que un parámetro de referencia, un parámetro de salida no crea una nueva ubicación de almacenamiento.Similar to a reference parameter, an output parameter does not create a new storage location. En su lugar, un parámetro de salida representa la misma ubicación de almacenamiento que la variable especificada como argumento en la invocación del método.Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

Cuando un parámetro formal es un parámetro de salida, el argumento correspondiente en una invocación de método debe constar de la palabra clave out seguido por un variable_reference (reglas precisas para determinar asignación definitiva) del mismo tipo del parámetro formal.When a formal parameter is an output parameter, the corresponding argument in a method invocation must consist of the keyword out followed by a variable_reference (Precise rules for determining definite assignment) of the same type as the formal parameter. Una variable no debe estar asignada definitivamente antes de que se puede pasar como un parámetro de salida, pero después de una invocación donde se pasó una variable como un parámetro de salida, la variable se considera asignada definitivamente.A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned.

Dentro de un método, al igual que una variable local variable, un parámetro de salida se considera inicialmente sin asignar y debe estar previamente asignada antes de que se utiliza su valor.Within a method, just like a local variable, an output parameter is initially considered unassigned and must be definitely assigned before its value is used.

Cada parámetro de salida de un método debe asignarse definitivamente antes de que el método devuelve.Every output parameter of a method must be definitely assigned before the method returns.

Un método declarado como un método parcial (métodos parciales) o un iterador (iteradores) no puede tener parámetros de salida.A method declared as a partial method (Partial methods) or an iterator (Iterators) cannot have output parameters.

Los parámetros de salida se usan normalmente en los métodos que devuelven varios valores.Output parameters are typically used in methods that produce multiple return values. Por ejemplo:For example:

using System;

class Test
{
    static void SplitPath(string path, out string dir, out string name) {
        int i = path.Length;
        while (i > 0) {
            char ch = path[i - 1];
            if (ch == '\\' || ch == '/' || ch == ':') break;
            i--;
        }
        dir = path.Substring(0, i);
        name = path.Substring(i);
    }

    static void Main() {
        string dir, name;
        SplitPath("c:\\Windows\\System\\hello.txt", out dir, out name);
        Console.WriteLine(dir);
        Console.WriteLine(name);
    }
}

El ejemplo genera el resultado:The example produces the output:

c:\Windows\System\
hello.txt

Tenga en cuenta que el dir y name pueden estar sin asignadas variables antes de pasarlos a SplitPath, y que se considera asignados definitivamente posterior a la llamada.Note that the dir and name variables can be unassigned before they are passed to SplitPath, and that they are considered definitely assigned following the call.

Matrices de parámetrosParameter arrays

Un parámetro declarado con un params modificador es una matriz de parámetros.A parameter declared with a params modifier is a parameter array. Si una lista de parámetros formales incluye una matriz de parámetros, debe ser el último parámetro en la lista y debe ser de un tipo de matriz unidimensional.If a formal parameter list includes a parameter array, it must be the last parameter in the list and it must be of a single-dimensional array type. Por ejemplo, los tipos string[] y string[][] puede usarse como el tipo de una matriz de parámetros, pero el tipo string[,] no puede.For example, the types string[] and string[][] can be used as the type of a parameter array, but the type string[,] can not. No es posible combinar la params modificador con los modificadores ref y out.It is not possible to combine the params modifier with the modifiers ref and out.

Una matriz de parámetros permite argumentos que se especifique en uno de dos formas de una invocación de método:A parameter array permits arguments to be specified in one of two ways in a method invocation:

  • El argumento especificado para una matriz de parámetros puede ser una sola expresión que es implícitamente convertible (conversiones implícitas) para el tipo de matriz de parámetros.The argument given for a parameter array can be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. En este caso, la matriz de parámetros actúa exactamente como un parámetro de valor.In this case, the parameter array acts precisely like a value parameter.
  • Como alternativa, la invocación puede especificar cero o más argumentos para la matriz de parámetros, donde cada argumento es una expresión que se pueda convertir implícitamente (conversiones implícitas) para el tipo de elemento de la matriz de parámetros.Alternatively, the invocation can specify zero or more arguments for the parameter array, where each argument is an expression that is implicitly convertible (Implicit conversions) to the element type of the parameter array. En este caso, la invocación crea una instancia del parámetro de tipo de matriz con una longitud correspondiente al número de argumentos, inicializa los elementos de la instancia de matriz con los valores de argumento especificado y usa la instancia de matriz recién creado como real argumento.In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, initializes the elements of the array instance with the given argument values, and uses the newly created array instance as the actual argument.

Excepto para permitir un número variable de argumentos en una invocación, una matriz de parámetros equivale exactamente a un parámetro de valor (parámetros del valor) del mismo tipo.Except for allowing a variable number of arguments in an invocation, a parameter array is precisely equivalent to a value parameter (Value parameters) of the same type.

El ejemploThe example

using System;

class Test
{
    static void F(params int[] args) {
        Console.Write("Array contains {0} elements:", args.Length);
        foreach (int i in args) 
            Console.Write(" {0}", i);
        Console.WriteLine();
    }

    static void Main() {
        int[] arr = {1, 2, 3};
        F(arr);
        F(10, 20, 30, 40);
        F();
    }
}

genera el resultadoproduces the output

Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:

La primera invocación de F simplemente pasa la matriz a como un parámetro de valor.The first invocation of F simply passes the array a as a value parameter. La segunda invocación de F crea automáticamente un elemento de cuatro int[] con los valores de elemento especificados y pasa ese instancia como un parámetro de valor de matriz.The second invocation of F automatically creates a four-element int[] with the given element values and passes that array instance as a value parameter. Del mismo modo, la tercera invocación de F crea un elemento de cero int[] y pasa esa instancia como un parámetro de valor.Likewise, the third invocation of F creates a zero-element int[] and passes that instance as a value parameter. Las invocaciones de segunda y terceros son exactamente equivalentes a escribir:The second and third invocations are precisely equivalent to writing:

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

Al realizar la resolución de sobrecarga, un método con una matriz de parámetros puede ser aplicable en su forma normal o en su forma expandida (miembro de función aplicable).When performing overload resolution, a method with a parameter array may be applicable either in its normal form or in its expanded form (Applicable function member). La forma expandida de un método está disponible sólo si la forma normal del método no es aplicable, y solo si un método aplicable con la misma firma que la forma expandida ya no está declarado en el mismo tipo.The expanded form of a method is available only if the normal form of the method is not applicable and only if an applicable method with the same signature as the expanded form is not already declared in the same type.

El ejemploThe example

using System;

class Test
{
    static void F(params object[] a) {
        Console.WriteLine("F(object[])");
    }

    static void F() {
        Console.WriteLine("F()");
    }

    static void F(object a0, object a1) {
        Console.WriteLine("F(object,object)");
    }

    static void Main() {
        F();
        F(1);
        F(1, 2);
        F(1, 2, 3);
        F(1, 2, 3, 4);
    }
}

genera el resultadoproduces the output

F();
F(object[]);
F(object,object);
F(object[]);
F(object[]);

En el ejemplo, dos de las posibles formas expandidas del método con una matriz de parámetros ya están incluidas en la clase con métodos periódicas.In the example, two of the possible expanded forms of the method with a parameter array are already included in the class as regular methods. Estas formas expandidas no por lo tanto, se consideran al realizar la resolución de sobrecarga y las invocaciones de método primero y tercero, por tanto, seleccionan los métodos regulares.These expanded forms are therefore not considered when performing overload resolution, and the first and third method invocations thus select the regular methods. Cuando una clase declara un método con una matriz de parámetros, no es raro que incluir también algunas de las formas expandidas como métodos regulares.When a class declares a method with a parameter array, it is not uncommon to also include some of the expanded forms as regular methods. Por lo que es posible evitar la asignación de una matriz se invoca la instancia que se produce cuando una forma expandida de un método con una matriz de parámetros.By doing so it is possible to avoid the allocation of an array instance that occurs when an expanded form of a method with a parameter array is invoked.

Cuando el tipo de una matriz de parámetros es object[], puede producirse una ambigüedad entre la forma normal del método y la forma expandida para un único object parámetro.When the type of a parameter array is object[], a potential ambiguity arises between the normal form of the method and the expended form for a single object parameter. La razón de la ambigüedad es que un object[] es implícitamente convertible al tipo object.The reason for the ambiguity is that an object[] is itself implicitly convertible to type object. La ambigüedad no presenta ningún problema, sin embargo, puesto que se puede resolver mediante la inserción de una conversión explícita si es necesario.The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.

El ejemploThe example

using System;

class Test
{
    static void F(params object[] args) {
        foreach (object o in args) {
            Console.Write(o.GetType().FullName);
            Console.Write(" ");
        }
        Console.WriteLine();
    }

    static void Main() {
        object[] a = {1, "Hello", 123.456};
        object o = a;
        F(a);
        F((object)a);
        F(o);
        F((object[])o);
    }
}

genera el resultadoproduces the output

System.Int32 System.String System.Double
System.Object[]
System.Object[]
System.Int32 System.String System.Double

En la primera y última invocación de F, la forma normal de F es aplicable porque existe una conversión implícita desde el tipo de argumento para el tipo de parámetro (ambos son de tipo object[]).In the first and last invocations of F, the normal form of F is applicable because an implicit conversion exists from the argument type to the parameter type (both are of type object[]). Por lo tanto, la resolución de sobrecarga selecciona la forma normal de F, y el argumento se pasa como un parámetro de valor normales.Thus, overload resolution selects the normal form of F, and the argument is passed as a regular value parameter. En las invocaciones segunda y terceros, la forma normal de F no es aplicable porque no existe ninguna conversión implícita desde el tipo de argumento para el tipo de parámetro (tipo object no se puede convertir implícitamente al tipo object[]).In the second and third invocations, the normal form of F is not applicable because no implicit conversion exists from the argument type to the parameter type (type object cannot be implicitly converted to type object[]). Sin embargo, la forma expandida de F es aplicable, por lo que se selecciona mediante la resolución de sobrecarga.However, the expanded form of F is applicable, so it is selected by overload resolution. Como resultado, un solo elemento object[] se crea mediante la invocación, y el único elemento de la matriz se inicializa con el valor del argumento especificado (que a su vez es una referencia a un object[]).As a result, a one-element object[] is created by the invocation, and the single element of the array is initialized with the given argument value (which itself is a reference to an object[]).

Métodos estáticos y de instanciaStatic and instance methods

Cuando una declaración de método incluye un static modificador, que el método se dice que es un método estático.When a method declaration includes a static modifier, that method is said to be a static method. Cuando no hay ninguna static modificador está presente, se dice que el método es un método de instancia.When no static modifier is present, the method is said to be an instance method.

Un método estático no opera en una instancia concreta, y es un error en tiempo de compilación para hacer referencia a this en un método estático.A static method does not operate on a specific instance, and it is a compile-time error to refer to this in a static method.

Un método de instancia opera en una instancia determinada de una clase, y puede tener acceso a esa instancia como this (este acceso).An instance method operates on a given instance of a class, and that instance can be accessed as this (This access).

Cuando se hace referencia a un método en un member_access (acceso a miembros) del formulario E.Msi M es un método estático, E debe denotar un tipo que contiene My si M es un método de instancia, E debe denotar una instancia de un tipo que contenga M.When a method is referenced in a member_access (Member access) of the form E.M, if M is a static method, E must denote a type containing M, and if M is an instance method, E must denote an instance of a type containing M.

Las diferencias entre estático y los miembros de instancia se tratan con más detalle en miembros estáticos y de instancia.The differences between static and instance members are discussed further in Static and instance members.

Métodos virtualesVirtual methods

Cuando una declaración de método de instancia incluye un virtual modificador, que el método se dice que es un método virtual.When an instance method declaration includes a virtual modifier, that method is said to be a virtual method. Cuando no hay ninguna virtual modificador está presente, se dice que el método es un método no virtual.When no virtual modifier is present, the method is said to be a non-virtual method.

La implementación de un método no virtual es invariable: La implementación es la misma, si se invoca el método en una instancia de la clase en que se declaró o una instancia de una clase derivada.The implementation of a non-virtual method is invariant: The implementation is the same whether the method is invoked on an instance of the class in which it is declared or an instance of a derived class. En cambio, las clases derivadas pueden reemplazar la implementación de un método virtual.In contrast, the implementation of a virtual method can be superseded by derived classes. El proceso de sustitución de la implementación de un método virtual heredado se conoce como reemplazar ese método (invalidar métodos).The process of superseding the implementation of an inherited virtual method is known as overriding that method (Override methods).

En una invocación de método virtual, el tipo en tiempo de ejecución de la instancia para el que tiene esa invocación lugar determina la implementación del método real a invocar.In a virtual method invocation, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke. En una invocación de método no virtual, el tipo en tiempo de compilación de la instancia es el factor determinante.In a non-virtual method invocation, the compile-time type of the instance is the determining factor. En términos precisos, cuando un método denominado N se invoca con una lista de argumentos A en una instancia con un tipo de tiempo de compilación C y un tipo de tiempo de ejecución R (donde R sea C o una clase derivada desde C), la invocación se procesa como sigue:In precise terms, when a method named N is invoked with an argument list A on an instance with a compile-time type C and a run-time type R (where R is either C or a class derived from C), the invocation is processed as follows:

  • En primer lugar, se aplica la resolución de sobrecarga a C, N, y Apara seleccionar un método específico M desde el conjunto de métodos declarada en y heredadas por C.First, overload resolution is applied to C, N, and A, to select a specific method M from the set of methods declared in and inherited by C. Esto se describe en las invocaciones de método.This is described in Method invocations.
  • A continuación, si M es un método no virtual, M se invoca.Then, if M is a non-virtual method, M is invoked.
  • En caso contrario, M es un método virtual y la implementación más derivada de M con respecto a R se invoca.Otherwise, M is a virtual method, and the most derived implementation of M with respect to R is invoked.

Para cada método virtual declarado en o heredado por una clase, existe una implementación más derivada del método con respecto a esa clase.For every virtual method declared in or inherited by a class, there exists a most derived implementation of the method with respect to that class. La implementación más derivada de un método virtual M con respecto a una clase R se determina como sigue:The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • Si R contiene la presentación de virtual declaración de M, a continuación, se trata de la implementación más derivada de M.If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • De lo contrario, si R contiene un override de M, a continuación, se trata de la implementación más derivada de M.Otherwise, if R contains an override of M, then this is the most derived implementation of M.
  • La mayor parte de lo contrario, la implementación de derivada M con respecto a R es igual que la implementación más derivada de M con respecto a la clase base directa de R.Otherwise, the most derived implementation of M with respect to R is the same as the most derived implementation of M with respect to the direct base class of R.

En el ejemplo siguiente se ilustra las diferencias entre los métodos virtuales y no virtuales:The following example illustrates the differences between virtual and non-virtual methods:

using System;

class A
{
    public void F() { Console.WriteLine("A.F"); }

    public virtual void G() { Console.WriteLine("A.G"); }
}

class B: A
{
    new public void F() { Console.WriteLine("B.F"); }

    public override void G() { Console.WriteLine("B.G"); }
}

class Test
{
    static void Main() {
        B b = new B();
        A a = b;
        a.F();
        b.F();
        a.G();
        b.G();
    }
}

En el ejemplo, A presenta un método no virtual F y un método virtual G.In the example, A introduces a non-virtual method F and a virtual method G. La clase B presenta un nuevo método no virtual F, ocultando así las heredadas Fy también invalida el método heredado G.The class B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. El ejemplo genera el resultado:The example produces the output:

A.F
B.F
B.G
B.G

Tenga en cuenta que la instrucción a.G() invoca B.G, no A.G.Notice that the statement a.G() invokes B.G, not A.G. Esto es porque el tipo de tiempo de ejecución de la instancia (que es B), no el tipo de tiempo de compilación de la instancia (que es A), determina la implementación del método real a invocar.This is because the run-time type of the instance (which is B), not the compile-time type of the instance (which is A), determines the actual method implementation to invoke.

Dado que se permiten métodos para ocultar los métodos heredados, es posible que una clase que contiene varios métodos virtuales con la misma firma.Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. Esto no presenta un problema de ambigüedad, puesto que se ocultan todos excepto el método más derivado.This does not present an ambiguity problem, since all but the most derived method are hidden. En el ejemploIn the example

using System;

class A
{
    public virtual void F() { Console.WriteLine("A.F"); }
}

class B: A
{
    public override void F() { Console.WriteLine("B.F"); }
}

class C: B
{
    new public virtual void F() { Console.WriteLine("C.F"); }
}

class D: C
{
    public override void F() { Console.WriteLine("D.F"); }
}

class Test
{
    static void Main() {
        D d = new D();
        A a = d;
        B b = d;
        C c = d;
        a.F();
        b.F();
        c.F();
        d.F();
    }
}

el C y D clases contienen dos métodos virtuales con la misma firma: El introducidos por A y otro introducido por C.the C and D classes contain two virtual methods with the same signature: The one introduced by A and the one introduced by C. El método introducido por C oculta el método heredado de A.The method introduced by C hides the method inherited from A. Por lo tanto, la declaración de reemplazo en D invalida el método introducido por C, y no es posible que D para invalidar el método introducido por A.Thus, the override declaration in D overrides the method introduced by C, and it is not possible for D to override the method introduced by A. El ejemplo genera el resultado:The example produces the output:

B.F
B.F
D.F
D.F

Tenga en cuenta que es posible invocar el método virtual oculto mediante el acceso a una instancia de D a través de una menor derivada tipo en el que el método no está oculto.Note that it is possible to invoke the hidden virtual method by accessing an instance of D through a less derived type in which the method is not hidden.

Invalidar métodosOverride methods

Cuando una declaración de método de instancia incluye un override modificador, se dice que el método sea un invalidar el método.When an instance method declaration includes an override modifier, the method is said to be an override method. Un método de invalidación invalida un método virtual heredado con la misma firma.An override method overrides an inherited virtual method with the same signature. Mientras que una declaración de método virtual introduce un método nuevo, una declaración de método de reemplazo especializa un método virtual heredado existente proporcionando una nueva implementación de ese método.Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method.

El método reemplazado por una override declaración se conoce como el se invalida el método base.The method overridden by an override declaration is known as the overridden base method. Para un método de invalidación M declarado en una clase C, el método base reemplazado se determina examinando cada tipo de clase base del C, empezando por el tipo de clase base directa de C y continuando con cada sucesivas tipo de clase base directa, hasta en un tipo de clase base determinada, al menos uno es el método accesible encuentra que tiene la misma firma que M después de la sustitución de argumentos de tipo.For an override method M declared in a class C, the overridden base method is determined by examining each base class type of C, starting with the direct base class type of C and continuing with each successive direct base class type, until in a given base class type at least one accessible method is located which has the same signature as M after substitution of type arguments. Para los fines de buscar el método base reemplazado, se considera accesible si es un método public, si es protected, si es protected internal, o si es internal y se ha declarado en el mismo programa como C.For the purposes of locating the overridden base method, a method is considered accessible if it is public, if it is protected, if it is protected internal, or if it is internal and declared in the same program as C.

Se produce un error de tiempo de compilación a menos que todos los elementos siguientes son true para una declaración de reemplazo:A compile-time error occurs unless all of the following are true for an override declaration:

  • Un método base invalidado puede encontrarse como se describió anteriormente.An overridden base method can be located as described above.
  • No hay exactamente un tal método base invalidado.There is exactly one such overridden base method. Esta restricción tiene efecto solo si el tipo de clase base es un tipo construido donde la sustitución de argumentos de tipo hace que la firma de los dos métodos de la misma.This restriction has effect only if the base class type is a constructed type where the substitution of type arguments makes the signature of two methods the same.
  • El método base reemplazado es un virtual, abstract o invalidar el método.The overridden base method is a virtual, abstract, or override method. En otras palabras, el método base reemplazado no puede ser estático o no virtual.In other words, the overridden base method cannot be static or non-virtual.
  • El método base reemplazado no es un método sellado.The overridden base method is not a sealed method.
  • El método de reemplazo y el método base reemplazado tienen el mismo tipo de valor devuelto.The override method and the overridden base method have the same return type.
  • La declaración de reemplazo y el método base reemplazado tienen la misma accesibilidad declarada.The override declaration and the overridden base method have the same declared accessibility. En otras palabras, una declaración de reemplazo no puede cambiar la accesibilidad del método virtual.In other words, an override declaration cannot change the accessibility of the virtual method. Sin embargo, si el método base reemplazado es protected internal y se declara en un ensamblado diferente que el ensamblado que contiene el método de invalidación, a continuación, el método de invalidación declarado accesibilidad debe estar protegida.However, if the overridden base method is protected internal and it is declared in a different assembly than the assembly containing the override method then the override method's declared accessibility must be protected.
  • La declaración de reemplazo no especifica el tipo de parámetro restricciones cláusulas.The override declaration does not specify type-parameter-constraints-clauses. En su lugar, las restricciones se heredan del método base invalidado.Instead the constraints are inherited from the overridden base method. Tenga en cuenta que las restricciones que son parámetros de tipo en el método invalidado pueden reemplazarse por argumentos de tipo en la restricción heredada.Note that constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. Esto puede conducir a las restricciones que no sean válidas cuando se especifica explícitamente, como tipos de valor o tipos sellados.This can lead to constraints that are not legal when explicitly specified, such as value types or sealed types.

El ejemplo siguiente muestra cómo funcionan las reglas de reemplazo para las clases genéricas:The following example demonstrates how the overriding rules work for generic classes:

abstract class C<T>
{
    public virtual T F() {...}
    public virtual C<T> G() {...}
    public virtual void H(C<T> x) {...}
}

class D: C<string>
{
    public override string F() {...}            // Ok
    public override C<string> G() {...}         // Ok
    public override void H(C<T> x) {...}        // Error, should be C<string>
}

class E<T,U>: C<U>
{
    public override U F() {...}                 // Ok
    public override C<U> G() {...}              // Ok
    public override void H(C<T> x) {...}        // Error, should be C<U>
}

Puede tener acceso a una declaración de reemplazo del método base invalidado utilizando un base_access (Base acceso).An override declaration can access the overridden base method using a base_access (Base access). En el ejemploIn the example

class A
{
    int x;

    public virtual void PrintFields() {
        Console.WriteLine("x = {0}", x);
    }
}

class B: A
{
    int y;

    public override void PrintFields() {
        base.PrintFields();
        Console.WriteLine("y = {0}", y);
    }
}

el base.PrintFields() invocación en B invoca el PrintFields método declarado en A.the base.PrintFields() invocation in B invokes the PrintFields method declared in A. Un base_access deshabilita el mecanismo de llamada virtual y simplemente se trata del método base como un método no virtual.A base_access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. Tenían la invocación de B sido escritos ((A)this).PrintFields(), lo haría de forma recursiva invocar el PrintFields método declarado en B, no uno que se declara en A, puesto que PrintFields es virtual y el tipo de tiempo de ejecución de ((A)this) es B.Had the invocation in B been written ((A)this).PrintFields(), it would recursively invoke the PrintFields method declared in B, not the one declared in A, since PrintFields is virtual and the run-time type of ((A)this) is B.

Mediante la inclusión de un override can modificador un método invalidan otro método.Only by including an override modifier can a method override another method. En todos los demás casos, un método con la misma firma que un método heredado simplemente oculta el método heredado.In all other cases, a method with the same signature as an inherited method simply hides the inherited method. En el ejemploIn the example

class A
{
    public virtual void F() {}
}

class B: A
{
    public virtual void F() {}        // Warning, hiding inherited F()
}

el F método B no incluye un override modificador y, por tanto, no reemplaza el F método A.the F method in B does not include an override modifier and therefore does not override the F method in A. En su lugar, el F método B oculta el método en A, y se notifica una advertencia porque la declaración no incluye un new modificador.Rather, the F method in B hides the method in A, and a warning is reported because the declaration does not include a new modifier.

En el ejemploIn the example

class A
{
    public virtual void F() {}
}

class B: A
{
    new private void F() {}        // Hides A.F within body of B
}

class C: B
{
    public override void F() {}    // Ok, overrides A.F
}

el F método B oculta virtual F método hereda A.the F method in B hides the virtual F method inherited from A. Desde el nuevo F en B tiene acceso privado, su ámbito sólo incluye el cuerpo de la clase de B y no se extiende a C.Since the new F in B has private access, its scope only includes the class body of B and does not extend to C. Por lo tanto, la declaración de F en C se permite reemplazar la F hereda A.Therefore, the declaration of F in C is permitted to override the F inherited from A.

Métodos selladosSealed methods

Cuando una declaración de método de instancia incluye un sealed modificador, que el método se dice que un sellado método.When an instance method declaration includes a sealed modifier, that method is said to be a sealed method. Si una declaración de método de instancia incluye la sealed modificador, también debe incluir el override modificador.If an instance method declaration includes the sealed modifier, it must also include the override modifier. El uso de la sealed modificador impide que una clase derivada siga reemplazando el método.Use of the sealed modifier prevents a derived class from further overriding the method.

En el ejemploIn the example

using System;

class A
{
    public virtual void F() {
        Console.WriteLine("A.F");
    }

    public virtual void G() {
        Console.WriteLine("A.G");
    }
}

class B: A
{
    sealed override public void F() {
        Console.WriteLine("B.F");
    } 

    override public void G() {
        Console.WriteLine("B.G");
    } 
}

class C: B
{
    override public void G() {
        Console.WriteLine("C.G");
    } 
}

la clase B proporciona dos métodos de reemplazo: un F método que tiene el sealed modificador y un G método que no lo hace.the class B provides two override methods: an F method that has the sealed modifier and a G method that does not. Buso del sellado modifier impide C invaliden aún más F.B's use of the sealed modifier prevents C from further overriding F.

Métodos abstractosAbstract methods

Cuando una declaración de método de instancia incluye un abstract modificador, que el método se dice que un método abstracto.When an instance method declaration includes an abstract modifier, that method is said to be an abstract method. Aunque un método abstracto es también implícitamente un método virtual, no puede tener el modificador virtual.Although an abstract method is implicitly also a virtual method, it cannot have the modifier virtual.

Una declaración de método abstracto introduce un nuevo método virtual pero no proporciona una implementación de ese método.An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. En su lugar, las clases derivadas no abstractas deben proporcionar su propia implementación invalidando este método.Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Dado que un método abstracto no proporciona ninguna implementación real, el cuerpoMétodo de un método abstracto consiste simplemente en un punto y coma.Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon.

Solo se permiten declaraciones de métodos abstractos en clases abstractas (clases abstractas).Abstract method declarations are only permitted in abstract classes (Abstract classes).

En el ejemploIn the example

public abstract class Shape
{
    public abstract void Paint(Graphics g, Rectangle r);
}

public class Ellipse: Shape
{
    public override void Paint(Graphics g, Rectangle r) {
        g.DrawEllipse(r);
    }
}

public class Box: Shape
{
    public override void Paint(Graphics g, Rectangle r) {
        g.DrawRect(r);
    }
}

la Shape clase define el concepto abstracto de un objeto de forma geométrica que puede dibujarse a sí mismo.the Shape class defines the abstract notion of a geometrical shape object that can paint itself. El Paint método es abstracto, porque no hay ninguna implementación predeterminada significativa.The Paint method is abstract because there is no meaningful default implementation. El Ellipse y Box clases son concretas Shape implementaciones.The Ellipse and Box classes are concrete Shape implementations. Dado que estas clases no son abstractas, es necesario reemplazar el Paint método y proporcionar una implementación real.Because these classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

Es un error en tiempo de compilación para un base_access (Base acceso) para hacer referencia a un método abstracto.It is a compile-time error for a base_access (Base access) to reference an abstract method. En el ejemploIn the example

abstract class A
{
    public abstract void F();
}

class B: A
{
    public override void F() {
        base.F();                        // Error, base.F is abstract
    }
}

se notifica un error en tiempo de compilación para el base.F() invocación porque hace referencia a un método abstracto.a compile-time error is reported for the base.F() invocation because it references an abstract method.

Una declaración de método abstracto se permite reemplazar un método virtual.An abstract method declaration is permitted to override a virtual method. Esto permite que una clase abstracta fuerce una nueva implementación del método en las clases derivadas y hace que la implementación original del método no está disponible.This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. En el ejemploIn the example

using System;

class A
{
    public virtual void F() {
        Console.WriteLine("A.F");
    }
}

abstract class B: A
{
    public abstract override void F();
}

class C: B
{
    public override void F() {
        Console.WriteLine("C.F");
    }
}

clase A declara un método virtual, la clase B invalida este método con un método abstracto y una clase C reemplaza el método abstracto para proporcionar su propia implementación.class A declares a virtual method, class B overrides this method with an abstract method, and class C overrides the abstract method to provide its own implementation.

Métodos externosExternal methods

Cuando una declaración de método incluye un extern modificador, que el método se dice que un método externo.When a method declaration includes an extern modifier, that method is said to be an external method. Métodos externos se implementan de forma externa, normalmente mediante un lenguaje distinto de C#.External methods are implemented externally, typically using a language other than C#. Dado que una declaración de método externo no proporciona ninguna implementación real, el cuerpoMétodo de un método externo consiste simplemente en un punto y coma.Because an external method declaration provides no actual implementation, the method_body of an external method simply consists of a semicolon. Un método externo no puede ser genérico.An external method may not be generic.

El extern modificador se usa normalmente junto con un DllImport atributo (interoperabilidad con componentes COM y Win32), lo que permite métodos externos que se implementa en los archivos DLL (bibliotecas de vínculos dinámicos).The extern modifier is typically used in conjunction with a DllImport attribute (Interoperation with COM and Win32 components), allowing external methods to be implemented by DLLs (Dynamic Link Libraries). El entorno de ejecución puede admitir otros mecanismos mediante el cual se pueden proporcionar implementaciones de métodos externos.The execution environment may support other mechanisms whereby implementations of external methods can be provided.

Cuando un método externo incluye un DllImport atributo, la declaración del método también debe incluir un static modificador.When an external method includes a DllImport attribute, the method declaration must also include a static modifier. En este ejemplo se muestra el uso de la extern modificador y DllImport atributo:This example demonstrates the use of the extern modifier and the DllImport attribute:

using System.Text;
using System.Security.Permissions;
using System.Runtime.InteropServices;

class Path
{
    [DllImport("kernel32", SetLastError=true)]
    static extern bool CreateDirectory(string name, SecurityAttribute sa);

    [DllImport("kernel32", SetLastError=true)]
    static extern bool RemoveDirectory(string name);

    [DllImport("kernel32", SetLastError=true)]
    static extern int GetCurrentDirectory(int bufSize, StringBuilder buf);

    [DllImport("kernel32", SetLastError=true)]
    static extern bool SetCurrentDirectory(string name);
}

Métodos parciales (resumen)Partial methods (recap)

Cuando una declaración de método incluye un partial modificador, que el método se dice que un método parcial.When a method declaration includes a partial modifier, that method is said to be a partial method. Los métodos parciales solo se pueden declarar como miembros de tipos parciales (tipos parciales) y están sujetos a un número de restricciones.Partial methods can only be declared as members of partial types (Partial types), and are subject to a number of restrictions. Los métodos parciales se describen con más detalle en métodos parciales.Partial methods are further described in Partial methods.

Métodos de extensiónExtension methods

Cuando el primer parámetro de un método incluye el this modificador, que el método se dice que un método de extensión.When the first parameter of a method includes the this modifier, that method is said to be an extension method. Métodos de extensión solo pueden declararse en clases estáticas no genérica y no anidados.Extension methods can only be declared in non-generic, non-nested static classes. El primer parámetro de un método de extensión no puede tener ningún modificador distinto this, y el tipo de parámetro no puede ser un tipo de puntero.The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type.

El siguiente es un ejemplo de una clase estática que declara dos métodos de extensión:The following is an example of a static class that declares two extension methods:

public static class Extensions
{
    public static int ToInt32(this string s) {
        return Int32.Parse(s);
    }

    public static T[] Slice<T>(this T[] source, int index, int count) {
        if (index < 0 || count < 0 || source.Length - index < count)
            throw new ArgumentException();
        T[] result = new T[count];
        Array.Copy(source, index, result, 0, count);
        return result;
    }
}

Un método de extensión es un método estático normal.An extension method is a regular static method. Además, cuando su clase estática envolvente en el ámbito, un método de extensión se puede invocar mediante sintaxis de invocación de método de instancia (las invocaciones de método de extensión), mediante la expresión del receptor como primer argumento.In addition, where its enclosing static class is in scope, an extension method can be invoked using instance method invocation syntax (Extension method invocations), using the receiver expression as the first argument.

El siguiente programa utiliza los métodos de extensión declarados anteriormente:The following program uses the extension methods declared above:

static class Program
{
    static void Main() {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in strings.Slice(1, 2)) {
            Console.WriteLine(s.ToInt32());
        }
    }
}

El Slice método está disponible en el string[]y el ToInt32 método está disponible en string, porque se ha declarado como métodos de extensión.The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as extension methods. El significado del programa es igual que las llamadas de método estático normal siguiente, mediante:The meaning of the program is the same as the following, using ordinary static method calls:

static class Program
{
    static void Main() {
        string[] strings = { "1", "22", "333", "4444" };
        foreach (string s in Extensions.Slice(strings, 1, 2)) {
            Console.WriteLine(Extensions.ToInt32(s));
        }
    }
}

Cuerpo del métodoMethod body

El cuerpoMétodo de un método de declaración consta de un cuerpo de bloques, un cuerpo de expresión o un punto y coma.The method_body of a method declaration consists of either a block body, an expression body or a semicolon.

El tipo de resultado de un método es void si el tipo de valor devuelto es void, o si el método es asincrónico y el tipo de valor devuelto es System.Threading.Tasks.Task.The result type of a method is void if the return type is void, or if the method is async and the return type is System.Threading.Tasks.Task. En caso contrario, el tipo de resultado de un método que no sea asincrónico es su tipo de valor devuelto y el tipo de resultado con tipo de valor devuelto de un método asincrónico System.Threading.Tasks.Task<T> es T.Otherwise, the result type of a non-async method is its return type, and the result type of an async method with return type System.Threading.Tasks.Task<T> is T.

Cuando un método que tiene un void como resultado de tipo y un cuerpo de bloques, return instrucciones (la instrucción return) no se permiten en el bloque al especificar una expresión.When a method has a void result type and a block body, return statements (The return statement) in the block are not permitted to specify an expression. Si la ejecución del bloque de un método void termina normalmente (es decir, el control llega al final del cuerpo del método), método regresa a su llamador actual.If execution of the block of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its current caller.

Cuando un método que tiene un void resultado y un cuerpo de expresión, la expresión E debe ser un statement_expression, y el cuerpo es equivalente exactamente a un cuerpo de bloques del formulario { E; }.When a method has a void result and an expression body, the expression E must be a statement_expression, and the body is exactly equivalent to a block body of the form { E; }.

Cuando un método tiene un tipo de resultado distinto de void y un bloque de cuerpo, cada uno de ellos return instrucción del bloque debe especificar una expresión que es implícitamente convertible al tipo de resultado.When a method has a non-void result type and a block body, each return statement in the block must specify an expression that is implicitly convertible to the result type. El punto de conexión de un cuerpo de bloques de un método devuelve un valor no debe ser accesible.The endpoint of a block body of a value-returning method must not be reachable. En otras palabras, en un método devuelve un valor con un cuerpo de bloques, no se permite que el control al alcanzar el final del cuerpo del método.In other words, in a value-returning method with a block body, control is not permitted to flow off the end of the method body.

Cuando un método que tiene un tipo de resultado distinto de void y un cuerpo de expresión, la expresión debe poder convertirse implícitamente al tipo de resultado y el cuerpo equivale exactamente a un cuerpo de bloques del formulario { return E; }.When a method has a non-void result type and an expression body, the expression must be implicitly convertible to the result type, and the body is exactly equivalent to a block body of the form { return E; }.

En el ejemploIn the example

class A
{
    public int F() {}            // Error, return value required

    public int G() {
        return 1;
    }

    public int H(bool b) {
        if (b) {
            return 1;
        }
        else {
            return 0;
        }
    }

    public int I(bool b) => b ? 1 : 0;
}

el valor de devolución F método produce un error de tiempo de compilación porque el control puede alcanzar el final del cuerpo del método.the value-returning F method results in a compile-time error because control can flow off the end of the method body. El G y H métodos son correctos, ya que todas las rutas de ejecución posibles terminan en una instrucción return que especifica un valor devuelto.The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value. El I método es correcto, porque el cuerpo es equivalente a un bloque de instrucciones con una sola instrucción return en ella.The I method is correct, because its body is equivalent to a statement block with just a single return statement in it.

Sobrecarga de métodosMethod overloading

Se describen las reglas de resolución de sobrecarga del método en inferencia de tipo.The method overload resolution rules are described in Type inference.

PropiedadesProperties

Un propiedad es un miembro que proporciona acceso a una característica de un objeto o una clase.A property is a member that provides access to a characteristic of an object or a class. Ejemplos de propiedades incluyen la longitud de una cadena, el tamaño de una fuente, el título de una ventana, el nombre de un cliente y así sucesivamente.Examples of properties include the length of a string, the size of a font, the caption of a window, the name of a customer, and so on. Las propiedades son una extensión natural de los campos, ambos son miembros denominados con tipos asociados, y la sintaxis para tener acceso a los campos y propiedades es la misma.Properties are a natural extension of fields—both are named members with associated types, and the syntax for accessing fields and properties is the same. Sin embargo, a diferencia de los campos, las propiedades no denotan ubicaciones de almacenamiento.However, unlike fields, properties do not denote storage locations. Las propiedades tienen descriptores de acceso que especifican las instrucciones que se ejecutan cuando se leen o escriben sus valores.Instead, properties have accessors that specify the statements to be executed when their values are read or written. Las propiedades proporcionan un mecanismo para asociar las acciones con la lectura y escritura de los atributos del objeto; Además, permiten esos atributos que se va a calcular.Properties thus provide a mechanism for associating actions with the reading and writing of an object's attributes; furthermore, they permit such attributes to be computed.

Las propiedades se declaran mediante property_declarations:Properties are declared using property_declarations:

property_declaration
    : attributes? property_modifier* type member_name property_body
    ;

property_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | property_modifier_unsafe
    ;

property_body
    : '{' accessor_declarations '}' property_initializer?
    | '=>' expression ';'
    ;

property_initializer
    : '=' variable_initializer ';'
    ;

Un property_declaration puede incluir un conjunto de atributos (atributos) y una combinación válida de los cuatro modificadores de acceso (modificadores de acceso ), el new (el nuevo modificador), static (métodos estáticos y de instancia), virtual (métodos virtuales), override (Invalidar métodos), sealed (sellado métodos), abstract (métodos abstractos), y extern (Métodos externos) modificadores.A property_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), static (Static and instance methods), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

Las declaraciones de propiedad están sujetos a las mismas reglas que las declaraciones de método (métodos) con respecto a las combinaciones válidas de modificadores.Property declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

El tipo de una propiedad de la declaración especifica el tipo de la propiedad que se incluyen en la declaración y el member_name especifica el nombre de la propiedad.The type of a property declaration specifies the type of the property introduced by the declaration, and the member_name specifies the name of the property. A menos que la propiedad es una implementación de miembro de interfaz explícita, el member_name es simplemente un identificador.Unless the property is an explicit interface member implementation, the member_name is simply an identifier. Para obtener una implementación de miembro de interfaz explícita (implementaciones de miembros de interfaz explícita), el member_name consta de un interface_type seguido de un " ."y un identificador.For an explicit interface member implementation (Explicit interface member implementations), the member_name consists of an interface_type followed by a "." and an identifier.

El tipo de una propiedad debe ser al menos tan accesibles como la propiedad propiamente dicha (restricciones de accesibilidad).The type of a property must be at least as accessible as the property itself (Accessibility constraints).

Un property_body puede constar de un cuerpo del descriptor de acceso o un cuerpo de expresión.A property_body may either consist of an accessor body or an expression body. En el cuerpo de un descriptor de acceso, accessor_declarations, que debe incluirse en "{"y"}" tokens, declare los descriptores de acceso (descriptores de acceso) de la propiedad.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Los descriptores de acceso especifican las instrucciones ejecutables asociadas con la lectura y escritura de la propiedad.The accessors specify the executable statements associated with reading and writing the property.

Un cuerpo de expresión formada por => seguido por un expresión E y un punto y coma es igual que en el cuerpo de instrucción { get { return E; } }y por lo tanto, sólo puede utilizarse para especificar solo captador propiedades donde se indica el resultado de Get por una sola expresión.An expression body consisting of => followed by an expression E and a semicolon is exactly equivalent to the statement body { get { return E; } }, and can therefore only be used to specify getter-only properties where the result of the getter is given by a single expression.

Un property_initializer sólo puede asignarse una propiedad implementada automáticamente (implementa automáticamente propiedades) y hace que la inicialización del campo subyacente de estos las propiedades con el valor especificado por el expresión.A property_initializer may only be given for an automatically implemented property (Automatically implemented properties), and causes the initialization of the underlying field of such properties with the value given by the expression.

Aunque la sintaxis para tener acceso a una propiedad es el mismo que para un campo, una propiedad no está clasificada como una variable.Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Por lo tanto, no es posible pasar una propiedad como un ref o out argumento.Thus, it is not possible to pass a property as a ref or out argument.

Cuando una declaración de propiedad incluye un extern modificador, la propiedad se dice que un propiedad externa.When a property declaration includes an extern modifier, the property is said to be an external property. Dado que una declaración de propiedad externa no proporciona ninguna implementación real, cada uno de sus accessor_declarations consta de un punto y coma.Because an external property declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

Propiedades de instancia y estáticosStatic and instance properties

Cuando una declaración de propiedad incluye un static modificador, la propiedad se dice que un propiedad estática.When a property declaration includes a static modifier, the property is said to be a static property. Cuando no hay ninguna static modificador está presente, la propiedad se dice que un propiedad de instancia.When no static modifier is present, the property is said to be an instance property.

Una propiedad estática no está asociada con una instancia específica y es un error en tiempo de compilación para hacer referencia a this en los descriptores de acceso de una propiedad estática.A static property is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static property.

Una propiedad de instancia está asociada con una instancia determinada de una clase, y puede tener acceso a esa instancia como this (este acceso) en los descriptores de acceso de esa propiedad.An instance property is associated with a given instance of a class, and that instance can be accessed as this (This access) in the accessors of that property.

Cuando se hace referencia a una propiedad en un member_access (acceso a miembros) del formulario E.Msi M es una propiedad estática, E debe denotar un tipo que contiene My si M es una propiedad de instancia, E debe denotar una instancia de un tipo que contenga M.When a property is referenced in a member_access (Member access) of the form E.M, if M is a static property, E must denote a type containing M, and if M is an instance property, E must denote an instance of a type containing M.

Las diferencias entre estático y los miembros de instancia se tratan con más detalle en miembros estáticos y de instancia.The differences between static and instance members are discussed further in Static and instance members.

Descriptores de accesoAccessors

El accessor_declarations de una propiedad, especifique las instrucciones ejecutables asociadas con la lectura y escritura de la propiedad.The accessor_declarations of a property specify the executable statements associated with reading and writing that property.

accessor_declarations
    : get_accessor_declaration set_accessor_declaration?
    | set_accessor_declaration get_accessor_declaration?
    ;

get_accessor_declaration
    : attributes? accessor_modifier? 'get' accessor_body
    ;

set_accessor_declaration
    : attributes? accessor_modifier? 'set' accessor_body
    ;

accessor_modifier
    : 'protected'
    | 'internal'
    | 'private'
    | 'protected' 'internal'
    | 'internal' 'protected'
    ;

accessor_body
    : block
    | ';'
    ;

Las declaraciones de descriptor de acceso que constan de un get_accessor_declaration, un set_accessor_declaration, o ambos.The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Cada declaración de descriptor de acceso está formado por el token get o set seguido de un elemento opcional accessor_modifier y un accessor_body.Each accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

El uso de accessor_modifiers se rige por las siguientes restricciones:The use of accessor_modifiers is governed by the following restrictions:

  • Un accessor_modifier no puede usarse en una interfaz o en una implementación de miembro de interfaz explícita.An accessor_modifier may not be used in an interface or in an explicit interface member implementation.
  • Para una propiedad o indizador que no tiene ningún override modificador, un accessor_modifier sólo está permitido si la propiedad o indizador tiene tanto un get y set descriptor de acceso y, a continuación, sólo se permite en una de ellas descriptores de acceso.For a property or indexer that has no override modifier, an accessor_modifier is permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors.
  • Para una propiedad o indizador que incluye un override modificador, un descriptor de acceso debe coincidir con el accessor_modifier, si lo hay, del descriptor de acceso que se va a invalidar.For a property or indexer that includes an override modifier, an accessor must match the accessor_modifier, if any, of the accessor being overridden.
  • El accessor_modifier debe declarar una accesibilidad que es estrictamente más restrictiva que la accesibilidad declarada de la propiedad o el propio indexador.The accessor_modifier must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. Para ser precisos:To be precise:
    • Si la propiedad o indizador tiene una accesibilidad declarada de public, accessor_modifier pueden ser protected internal, internal, protected, o private.If the property or indexer has a declared accessibility of public, the accessor_modifier may be either protected internal, internal, protected, or private.
    • Si la propiedad o indizador tiene una accesibilidad declarada de protected internal, accessor_modifier pueden ser internal, protected, o private.If the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • Si la propiedad o indizador tiene una accesibilidad declarada de internal o protected, accessor_modifier debe ser private.If the property or indexer has a declared accessibility of internal or protected, the accessor_modifier must be private.
    • Si la propiedad o indizador tiene una accesibilidad declarada de private, no accessor_modifier puede utilizarse.If the property or indexer has a declared accessibility of private, no accessor_modifier may be used.

Para abstract y extern propiedades, el accessor_body para cada descriptor de acceso especificado es simplemente un punto y coma.For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. Puede tener una propiedad no abstracta, no extern cada accessor_body sea un punto y coma, en cuyo caso es un propiedad implementada automáticamente (implementa automáticamente propiedades ).A non-abstract, non-extern property may have each accessor_body be a semicolon, in which case it is an automatically implemented property (Automatically implemented properties). Una propiedad implementada automáticamente debe tener al menos un descriptor de acceso get.An automatically implemented property must have at least a get accessor. Para los descriptores de acceso de otra propiedad no abstracta, no externos, el accessor_body es un bloque que especifica las instrucciones que se ejecuta cuando se invoca el descriptor de acceso correspondiente.For the accessors of any other non-abstract, non-extern property, the accessor_body is a block which specifies the statements to be executed when the corresponding accessor is invoked.

Un get descriptor de acceso corresponde a un método sin parámetros con un valor devuelto del tipo de propiedad.A get accessor corresponds to a parameterless method with a return value of the property type. Excepto como destino de una asignación, cuando se hace referencia a una propiedad en una expresión, el get descriptor de acceso de la propiedad se invoca para calcular el valor de la propiedad (valores de expresiones).Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property (Values of expressions). El cuerpo de un get descriptor de acceso debe ajustarse a las reglas de devolución por valor métodos descritos en cuerpo del método.The body of a get accessor must conform to the rules for value-returning methods described in Method body. En concreto, todos los return instrucciones en el cuerpo de un get descriptor de acceso debe especificar una expresión que es implícitamente convertible al tipo de propiedad.In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. Además, el punto de conexión de un get descriptor de acceso no debe ser accesible.Furthermore, the endpoint of a get accessor must not be reachable.

Un set descriptor de acceso corresponde a un método con un parámetro de valor único del tipo de propiedad y un void tipo de valor devuelto.A set accessor corresponds to a method with a single value parameter of the property type and a void return type. El parámetro implícito de un set descriptor de acceso siempre se denomina value.The implicit parameter of a set accessor is always named value. Cuando se hace referencia a una propiedad como el destino de una asignación (operadores de asignación), o como el operando de ++ o -- (operadores postfijos de incremento y decremento, Prefijo de incremento y decremento operadores), el set descriptor de acceso se invoca con un argumento (cuyo valor es el de la derecha de la asignación o el operando de la ++ o -- operador) que proporciona el nuevo valor (asignación Simple).When a property is referenced as the target of an assignment (Assignment operators), or as the operand of ++ or -- (Postfix increment and decrement operators, Prefix increment and decrement operators), the set accessor is invoked with an argument (whose value is that of the right-hand side of the assignment or the operand of the ++ or -- operator) that provides the new value (Simple assignment). El cuerpo de un set descriptor de acceso debe cumplir las reglas de void métodos descritos en cuerpo del método.The body of a set accessor must conform to the rules for void methods described in Method body. En concreto, return instrucciones en el set cuerpo del descriptor de acceso no se permiten especificar una expresión.In particular, return statements in the set accessor body are not permitted to specify an expression. Puesto que un set descriptor de acceso tiene implícitamente un parámetro denominado value, es un error de tiempo de compilación de una declaración de variable o una constante local en un set descriptor de acceso de ese nombre.Since a set accessor implicitly has a parameter named value, it is a compile-time error for a local variable or constant declaration in a set accessor to have that name.

Según la presencia o ausencia de la get y set descriptores de acceso, una propiedad se clasifica como sigue:Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • Una propiedad que incluye tanto una get descriptor de acceso y un set descriptor de acceso se dice que un lectura-escritura propiedad.A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • Una propiedad que tiene solo un get descriptor de acceso se dice que un de sólo lectura propiedad.A property that has only a get accessor is said to be a read-only property. Es un error en tiempo de compilación para una propiedad de solo lectura a ser el destino de una asignación.It is a compile-time error for a read-only property to be the target of an assignment.
  • Una propiedad que tiene solo un set descriptor de acceso se dice que un de solo escritura propiedad.A property that has only a set accessor is said to be a write-only property. Excepto como destino de una asignación, es un error en tiempo de compilación para hacer referencia a una propiedad de solo escritura en una expresión.Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression.

En el ejemploIn the example

public class Button: Control
{
    private string caption;

    public string Caption {
        get {
            return caption;
        }
        set {
            if (caption != value) {
                caption = value;
                Repaint();
            }
        }
    }

    public override void Paint(Graphics g, Rectangle r) {
        // Painting code goes here
    }
}

el Button control declara una pública Caption propiedad.the Button control declares a public Caption property. El get descriptor de acceso de la Caption propiedad devuelve la cadena almacenada en privado caption campo.The get accessor of the Caption property returns the string stored in the private caption field. El set comprueba si el nuevo valor es diferente del valor actual; en caso afirmativo, almacena el nuevo valor de descriptor de acceso y vuelve a dibujar el control.The set accessor checks if the new value is different from the current value, and if so, it stores the new value and repaints the control. Las propiedades a menudo siguen el patrón anterior: El get descriptor de acceso simplemente devuelve un valor almacenado en un campo privado y el set modifica el campo privado de descriptor de acceso y, a continuación, realiza acciones adicionales necesarias para actualizar completamente el estado del objeto.Properties often follow the pattern shown above: The get accessor simply returns a value stored in a private field, and the set accessor modifies that private field and then performs any additional actions required to fully update the state of the object.

Dada la Button clase anterior, el siguiente es un ejemplo de uso de la Caption propiedad:Given the Button class above, the following is an example of use of the Caption property:

Button okButton = new Button();
okButton.Caption = "OK";            // Invokes set accessor
string s = okButton.Caption;        // Invokes get accessor

En este caso, el set se invoca el descriptor de acceso asignando un valor a la propiedad y el get descriptor de acceso se invoca haciendo referencia a la propiedad en una expresión.Here, the set accessor is invoked by assigning a value to the property, and the get accessor is invoked by referencing the property in an expression.

El get y set descriptores de acceso de una propiedad no son miembros distintos, y no es posible declarar los descriptores de acceso de una propiedad por separado.The get and set accessors of a property are not distinct members, and it is not possible to declare the accessors of a property separately. Por lo tanto, no es posible que los dos descriptores de acceso de una propiedad de lectura y escritura tener una accesibilidad diferente.As such, it is not possible for the two accessors of a read-write property to have different accessibility. El ejemploThe example

class A
{
    private string name;

    public string Name {                // Error, duplicate member name
        get { return name; }
    }

    public string Name {                // Error, duplicate member name
        set { name = value; }
    }
}

no se declara una sola propiedad de lectura y escritura.does not declare a single read-write property. En su lugar, declaran dos propiedades con el mismo nombre, una de sólo lectura y otra de solo escritura.Rather, it declares two properties with the same name, one read-only and one write-only. Dado que dos miembros declarados en la misma clase no pueden tener el mismo nombre, el ejemplo hace que se produzca un error de tiempo de compilación.Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

Cuando una clase derivada declara una propiedad con el mismo nombre que una propiedad heredada, la propiedad derivada oculta la propiedad heredada con respecto a las operaciones de lectura y escritura.When a derived class declares a property by the same name as an inherited property, the derived property hides the inherited property with respect to both reading and writing. En el ejemploIn the example

class A
{
    public int P {
        set {...}
    }
}

class B: A
{
    new public int P {
        get {...}
    }
}

el P propiedad B oculta la P propiedad A con respecto a las operaciones de lectura y escritura.the P property in B hides the P property in A with respect to both reading and writing. Por lo tanto, en las instruccionesThus, in the statements

B b = new B();
b.P = 1;          // Error, B.P is read-only
((A)b).P = 1;     // Ok, reference to A.P

la asignación a b.P provoca un error de tiempo de compilación notificará desde sólo lectura P propiedad B oculta solo escritura P propiedad en A.the assignment to b.P causes a compile-time error to be reported, since the read-only P property in B hides the write-only P property in A. Sin embargo, tenga en cuenta que puede usarse una conversión para tener acceso a la oculta P propiedad.Note, however, that a cast can be used to access the hidden P property.

A diferencia de los campos públicos, las propiedades proporcionan una separación entre el estado interno de un objeto y su interfaz pública.Unlike public fields, properties provide a separation between an object's internal state and its public interface. Considere el ejemplo:Consider the example:

class Label
{
    private int x, y;
    private string caption;

    public Label(int x, int y, string caption) {
        this.x = x;
        this.y = y;
        this.caption = caption;
    }

    public int X {
        get { return x; }
    }

    public int Y {
        get { return y; }
    }

    public Point Location {
        get { return new Point(x, y); }
    }

    public string Caption {
        get { return caption; }
    }
}

En este caso, el Label clase utiliza dos int campos, x y y, para almacenar su ubicación.Here, the Label class uses two int fields, x and y, to store its location. La ubicación es públicamente expuestos como un X y un Y propiedad y, como un Location propiedad de tipo Point.The location is publicly exposed both as an X and a Y property and as a Location property of type Point. Si se encuentra, en una versión futura de Label, resulta más conveniente almacenar la ubicación como una Point internamente, el cambio se puede realizar sin que afecte a la interfaz pública de la clase:If, in a future version of Label, it becomes more convenient to store the location as a Point internally, the change can be made without affecting the public interface of the class:

class Label
{
    private Point location;
    private string caption;

    public Label(int x, int y, string caption) {
        this.location = new Point(x, y);
        this.caption = caption;
    }

    public int X {
        get { return location.x; }
    }

    public int Y {
        get { return location.y; }
    }

    public Point Location {
        get { return location; }
    }

    public string Caption {
        get { return caption; }
    }
}

Tenía x y y en su lugar se ha sido public readonly campos, habría sido imposible realizar este cambio a la Label clase.Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

Expone el estado a través de propiedades no es necesariamente menos eficiente que exponer directamente los campos.Exposing state through properties is not necessarily any less efficient than exposing fields directly. En concreto, cuando una propiedad no es virtual y contiene sólo una pequeña cantidad de código, el entorno de ejecución puede reemplazar las llamadas a los descriptores de acceso con el código real de los descriptores de acceso.In particular, when a property is non-virtual and contains only a small amount of code, the execution environment may replace calls to accessors with the actual code of the accessors. Este proceso se conoce como inserción, y facilita el acceso a la propiedad tan eficaz como el acceso a campos al mismo, conserva la mayor flexibilidad de las propiedades.This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

Desde la invocación de un get es conceptualmente equivalente a leer el valor de un campo de descriptor de acceso, se considera mal estilo de programación para get descriptores de acceso tienen efectos observables.Since invoking a get accessor is conceptually equivalent to reading the value of a field, it is considered bad programming style for get accessors to have observable side-effects. En el ejemploIn the example

class Counter
{
    private int next;

    public int Next {
        get { return next++; }
    }
}

el valor de la Next propiedad depende del número de veces que previamente ha accedido a la propiedad.the value of the Next property depends on the number of times the property has previously been accessed. Por lo tanto, el acceso a la propiedad produce un efecto secundario observable y la propiedad debe implementarse como un método en su lugar.Thus, accessing the property produces an observable side-effect, and the property should be implemented as a method instead.

La convención "sin efectos secundarios" para get descriptores de acceso no significa que get siempre se deben escribir los descriptores de acceso para devolver los valores almacenados en campos.The "no side-effects" convention for get accessors doesn't mean that get accessors should always be written to simply return values stored in fields. De hecho, get descriptores de acceso de proceso a menudo el valor de una propiedad mediante el acceso a varios campos o invocar métodos.Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. Sin embargo, diseñado correctamente get descriptor de acceso realiza ninguna acción que provocan cambios observables en el estado del objeto.However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

Las propiedades se pueden usar para retrasar la inicialización de un recurso hasta el momento en que se hace referencia en primer lugar.Properties can be used to delay initialization of a resource until the moment it is first referenced. Por ejemplo:For example:

using System.IO;

public class Console
{
    private static TextReader reader;
    private static TextWriter writer;
    private static TextWriter error;

    public static TextReader In {
        get {
            if (reader == null) {
                reader = new StreamReader(Console.OpenStandardInput());
            }
            return reader;
        }
    }

    public static TextWriter Out {
        get {
            if (writer == null) {
                writer = new StreamWriter(Console.OpenStandardOutput());
            }
            return writer;
        }
    }

    public static TextWriter Error {
        get {
            if (error == null) {
                error = new StreamWriter(Console.OpenStandardError());
            }
            return error;
        }
    }
}

El Console clase contiene tres propiedades In, Out, y Error, que representan la entrada estándar, salida y error de dispositivos, respectivamente.The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. Mediante la exposición de estos miembros como propiedades, la Console clase puede retrasar su inicialización hasta que se usan realmente.By exposing these members as properties, the Console class can delay their initialization until they are actually used. Por ejemplo, en la primera referencia a la Out propiedad, como enFor example, upon first referencing the Out property, as in

Console.Out.WriteLine("hello, world");

subyacente TextWriter para crear el dispositivo de salida.the underlying TextWriter for the output device is created. Sin embargo, si la aplicación no hace referencia a la In y Error propiedades y, a continuación, no hay ningún objeto que se crea para esos dispositivos.But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

Propiedades implementadas automáticamenteAutomatically implemented properties

Una propiedad implementada automáticamente (o autopropiedad para abreviar), es una propiedad de no abstracta no extern con cuerpos de descriptor de acceso de sólo punto y coma.An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. Propiedades automáticas deben tener un descriptor de acceso get y, opcionalmente, pueden tener un descriptor de acceso.Auto-properties must have a get accessor and can optionally have a set accessor.

Cuando se especifica una propiedad como una propiedad implementada automáticamente, un campo de respaldo oculto está automáticamente disponible para la propiedad y se implementan los descriptores de acceso para leer y escribir en ese campo de respaldo.When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, and the accessors are implemented to read from and write to that backing field. Si la propiedad automática no tiene ningún descriptor de acceso set, el campo de respaldo se considera readonly (campos de solo lectura).If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Al igual que un readonly campo, una propiedad automática solo captador puede asignarse a en el cuerpo de un constructor de la clase envolvente.Just like a readonly field, a getter-only auto-property can also be assigned to in the body of a constructor of the enclosing class. Esta asignación se asigna directamente en el campo de respaldo de solo lectura de la propiedad.Such an assignment assigns directly to the readonly backing field of the property.

Una propiedad automática podría tener opcionalmente un property_initializer, que se aplica directamente en el campo de respaldo como un variable_initializer (inicializadores de variables) .An auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (Variable initializers).

El ejemplo siguiente:The following example:

public class Point {
    public int X { get; set; } = 0;
    public int Y { get; set; } = 0;
}

es equivalente a la siguiente declaración:is equivalent to the following declaration:

public class Point {
    private int __x = 0;
    private int __y = 0;
    public int X { get { return __x; } set { __x = value; } }
    public int Y { get { return __y; } set { __y = value; } }
}

El ejemplo siguiente:The following example:

public class ReadOnlyPoint
{
    public int X { get; }
    public int Y { get; }
    public ReadOnlyPoint(int x, int y) { X = x; Y = y; }
}

es equivalente a la siguiente declaración:is equivalent to the following declaration:

public class ReadOnlyPoint
{
    private readonly int __x;
    private readonly int __y;
    public int X { get { return __x; } }
    public int Y { get { return __y; } }
    public ReadOnlyPoint(int x, int y) { __x = x; __y = y; }
}

Tenga en cuenta que las asignaciones para el campo de solo lectura son válidas, porque se producen dentro del constructor.Notice that the assignments to the readonly field are legal, because they occur within the constructor.

AccesibilidadAccessibility

Si tiene un descriptor de acceso una accessor_modifier, el dominio de accesibilidad (dominios de accesibilidad) del descriptor de acceso se determina mediante la accesibilidad declarada de la accessor_modifier .If an accessor has an accessor_modifier, the accessibility domain (Accessibility domains) of the accessor is determined using the declared accessibility of the accessor_modifier. Si no tiene un descriptor de acceso una accessor_modifier, el dominio de accesibilidad del descriptor de acceso viene determinada por la accesibilidad declarada de la propiedad o indizador.If an accessor does not have an accessor_modifier, the accessibility domain of the accessor is determined from the declared accessibility of the property or indexer.

La presencia de un accessor_modifier nunca afecta a la búsqueda de miembros (operadores) o de resolución de sobrecarga (de resolución de sobrecarga).The presence of an accessor_modifier never affects member lookup (Operators) or overload resolution (Overload resolution). Los modificadores de la propiedad o indizador siempre determinan qué propiedad o indizador está enlazado, independientemente del contexto del acceso.The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access.

Una vez que se ha seleccionado una propiedad o indizador concreto, los dominios de accesibilidad de los descriptores de acceso específicos implicados se utilizan para determinar si el uso es válido:Once a particular property or indexer has been selected, the accessibility domains of the specific accessors involved are used to determine if that usage is valid:

En el ejemplo siguiente, la propiedad A.Text está oculto por la propiedad B.Text, incluso en contextos en los que sólo el set se denomina descriptor de acceso.In the following example, the property A.Text is hidden by the property B.Text, even in contexts where only the set accessor is called. En cambio, la propiedad B.Count no es accesible para la clase M, por lo que la propiedad accesible A.Count se usa en su lugar.In contrast, the property B.Count is not accessible to class M, so the accessible property A.Count is used instead.

class A
{
    public string Text {
        get { return "hello"; }
        set { }
    }

    public int Count {
        get { return 5; }
        set { }
    }
}

class B: A
{
    private string text = "goodbye"; 
    private int count = 0;

    new public string Text {
        get { return text; }
        protected set { text = value; }
    }

    new protected int Count { 
        get { return count; }
        set { count = value; }
    }
}

class M
{
    static void Main() {
        B b = new B();
        b.Count = 12;             // Calls A.Count set accessor
        int i = b.Count;          // Calls A.Count get accessor
        b.Text = "howdy";         // Error, B.Text set accessor not accessible
        string s = b.Text;        // Calls B.Text get accessor
    }
}

Un descriptor de acceso que se usa para implementar una interfaz no puede tener un accessor_modifier.An accessor that is used to implement an interface may not have an accessor_modifier. Si solo un descriptor de acceso se utiliza para implementar una interfaz, el otro descriptor de acceso se puede declarar con un accessor_modifier:If only one accessor is used to implement an interface, the other accessor may be declared with an accessor_modifier:

public interface I
{
    string Prop { get; }
}

public class C: I
{
    public Prop {
        get { return "April"; }       // Must not have a modifier here
        internal set {...}            // Ok, because I.Prop has no set accessor
    }
}

Virtual, sellado, de reemplazo y descriptores de acceso de propiedad abstractaVirtual, sealed, override, and abstract property accessors

Un virtual declaración de propiedad especifica que los descriptores de acceso de la propiedad son virtuales.A virtual property declaration specifies that the accessors of the property are virtual. El virtual modificador se aplica a ambos descriptores de acceso de una propiedad de lectura y escritura, no es posible que solo un descriptor de acceso de una propiedad de lectura y escritura sea virtual.The virtual modifier applies to both accessors of a read-write property—it is not possible for only one accessor of a read-write property to be virtual.

Un abstract declaración de propiedad especifica que los descriptores de acceso de la propiedad son virtuales, pero no proporciona una implementación real de los descriptores de acceso.An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. En su lugar, las clases derivadas no abstractas deben proporcionar su propia implementación para los descriptores de acceso mediante la invalidación de la propiedad.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. Dado que un descriptor de acceso para una declaración de propiedad abstracta no proporciona ninguna implementación real, su accessor_body consiste simplemente en un punto y coma.Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon.

Una declaración de propiedad que incluya tanto el abstract y override modificadores especifica que la propiedad es abstracta y reemplaza una propiedad de base.A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. Los descriptores de acceso de esa propiedad también son abstractas.The accessors of such a property are also abstract.

Solo se permiten declaraciones de propiedad abstracta en clases abstractas (clases abstractas). Los descriptores de acceso de una propiedad virtual heredada se pueden invalidar en una clase derivada incluyendo una declaración de propiedad que especifica un override directiva.Abstract property declarations are only permitted in abstract classes (Abstract classes).The accessors of an inherited virtual property can be overridden in a derived class by including a property declaration that specifies an override directive. Esto se conoce como un reemplazar la declaración de propiedad.This is known as an overriding property declaration. Una declaración de propiedad reemplazada no declara una nueva propiedad.An overriding property declaration does not declare a new property. En su lugar, simplemente especializa las implementaciones de los descriptores de acceso de una propiedad virtual existente.Instead, it simply specializes the implementations of the accessors of an existing virtual property.

Una declaración de propiedad reemplazada debe especificar los mismos modificadores de accesibilidad exacto, tipo y nombre que la propiedad heredada.An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. Si la propiedad heredada tiene solo un descriptor de acceso (por ejemplo, si la propiedad heredada es de solo lectura o solo escritura), la propiedad de reemplazo debe incluir sólo ese descriptor de acceso.If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property must include only that accessor. Si la propiedad heredada incluye ambos descriptores de acceso (es decir, si la propiedad heredada es de lectura y escritura), la propiedad de reemplazo puede incluir un descriptor de acceso o ambos descriptores de acceso.If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors.

Puede incluir una declaración de propiedad reemplazada la sealed modificador.An overriding property declaration may include the sealed modifier. Uso de este modificador impide que una clase derivada siga reemplazando la propiedad.Use of this modifier prevents a derived class from further overriding the property. Los descriptores de acceso de una propiedad sellada también están sellados.The accessors of a sealed property are also sealed.

Salvo por diferencias en la declaración e invocación sintaxis, virtual, sellado, de reemplazo y abstractos descriptores de acceso se comportan exactamente igual virtual, sellado, de reemplazo y métodos abstractos.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. En concreto, las reglas se describen en métodos virtuales, invalidar métodos, sellado métodos, y métodos abstractos aplicar como si los descriptores de acceso fueran métodos de la forma correspondiente:Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form:

  • Un get descriptor de acceso corresponde a un método sin parámetros con un valor devuelto del tipo de propiedad y los mismos modificadores que la propiedad contenedora.A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property.
  • Un set descriptor de acceso corresponde a un método con un parámetro de valor único del tipo de propiedad, un void devolver el tipo y los mismos modificadores que la propiedad contenedora.A set accessor corresponds to a method with a single value parameter of the property type, a void return type, and the same modifiers as the containing property.

En el ejemploIn the example

abstract class A
{
    int y;

    public virtual int X {
        get { return 0; }
    }

    public virtual int Y {
        get { return y; }
        set { y = value; }
    }

    public abstract int Z { get; set; }
}

X es una propiedad de solo lectura virtual Y es una propiedad de lectura y escritura virtual, y Z es una propiedad abstracta de lectura y escritura.X is a virtual read-only property, Y is a virtual read-write property, and Z is an abstract read-write property. Dado que Z es abstracto, la clase contenedora A debe declararse como abstracta.Because Z is abstract, the containing class A must also be declared abstract.

Una clase que derive de A es muestra a continuación:A class that derives from A is show below:

class B: A
{
    int z;

    public override int X {
        get { return base.X + 1; }
    }

    public override int Y {
        set { base.Y = value < 0? 0: value; }
    }

    public override int Z {
        get { return z; }
        set { z = value; }
    }
}

Aquí, las declaraciones de X, Y, y Z invalida las declaraciones de propiedad.Here, the declarations of X, Y, and Z are overriding property declarations. Cada declaración de propiedad coincide exactamente con los modificadores de accesibilidad, tipo y nombre de la propiedad heredada correspondiente.Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. El get descriptor de acceso de X y set descriptor de acceso de Y utilizar el base palabra clave para tener acceso a los descriptores de acceso heredadas.The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. La declaración de Z reemplaza ambos descriptores de acceso, por lo tanto, no hay ningún miembro de función abstracta pendientes en B, y B puede ser una clase no abstracta.The declaration of Z overrides both abstract accessors—thus, there are no outstanding abstract function members in B, and B is permitted to be a non-abstract class.

Cuando se declara una propiedad como un override, los descriptores de acceso invalidados deben ser accesibles para el código de invalidación.When a property is declared as an override, any overridden accessors must be accessible to the overriding code. Además, la accesibilidad declarada de la propiedad o el indexador y de los descriptores de acceso, debe coincidir con el miembro reemplazado y descriptores de acceso.In addition, the declared accessibility of both the property or indexer itself, and of the accessors, must match that of the overridden member and accessors. Por ejemplo:For example:

public class B
{
    public virtual int P {
        protected set {...}
        get {...}
    }
}

public class D: B
{
    public override int P {
        protected set {...}            // Must specify protected here
        get {...}                      // Must not have a modifier here
    }
}

EventosEvents

Un eventos es un miembro que permite a un objeto o clase para proporcionar notificaciones.An event is a member that enables an object or class to provide notifications. Los clientes pueden adjuntar código ejecutable para eventos proporcionando controladores de eventos.Clients can attach executable code for events by supplying event handlers.

Los eventos se declaran mediante event_declarations:Events are declared using event_declarations:

event_declaration
    : attributes? event_modifier* 'event' type variable_declarators ';'
    | attributes? event_modifier* 'event' type member_name '{' event_accessor_declarations '}'
    ;

event_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'static'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | event_modifier_unsafe
    ;

event_accessor_declarations
    : add_accessor_declaration remove_accessor_declaration
    | remove_accessor_declaration add_accessor_declaration
    ;

add_accessor_declaration
    : attributes? 'add' block
    ;

remove_accessor_declaration
    : attributes? 'remove' block
    ;

Un event_declaration puede incluir un conjunto de atributos (atributos) y una combinación válida de los cuatro modificadores de acceso (modificadores de acceso ), el new (el nuevo modificador), static (métodos estáticos y de instancia), virtual (métodos virtuales), override (Invalidar métodos), sealed (sellado métodos), abstract (métodos abstractos), y extern (Métodos externos) modificadores.An event_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), static (Static and instance methods), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

Declaraciones de eventos están sujetos a las mismas reglas que las declaraciones de método (métodos) con respecto a las combinaciones válidas de modificadores.Event declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

El tipo de un evento debe especificarse un delegate_type (hacen referencia a tipos) y que delegate_type debe ser al menos como accesible como el propio evento (restricciones de accesibilidad).The type of an event declaration must be a delegate_type (Reference types), and that delegate_type must be at least as accessible as the event itself (Accessibility constraints).

Puede incluir una declaración de evento event_accessor_declarations.An event declaration may include event_accessor_declarations. Sin embargo, si no es así, para que no son externos y no abstractas eventos, el compilador proporciona automáticamente (eventos como campos); para los eventos externos, los descriptores de acceso se proporcionan externamente.However, if it does not, for non-extern, non-abstract events, the compiler supplies them automatically (Field-like events); for extern events, the accessors are provided externally.

Una declaración de evento que omite event_accessor_declarations define uno o varios eventos: uno para cada uno de los variable_declarators.An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarators. Los atributos y modificadores se aplican a todos los miembros que se declara como un event_declaration.The attributes and modifiers apply to all of the members declared by such an event_declaration.

Es un error en tiempo de compilación para un event_declaration para incluir tanto la abstract modificador y delimitado por llaves event_accessor_declarations.It is a compile-time error for an event_declaration to include both the abstract modifier and brace-delimited event_accessor_declarations.

Cuando una declaración de evento incluye un extern modificador, el evento se dice que un evento externo.When an event declaration includes an extern modifier, the event is said to be an external event. Dado que una declaración de evento externo no proporciona ninguna implementación real, es un error para que incluya tanto el extern modificador y event_accessor_declarations.Because an external event declaration provides no actual implementation, it is an error for it to include both the extern modifier and event_accessor_declarations.

Es un error en tiempo de compilación para un variable_declarator de una declaración de evento con un abstract o external modificador debe incluir un variable_initializer.It is a compile-time error for a variable_declarator of an event declaration with an abstract or external modifier to include a variable_initializer.

Un evento puede utilizarse como operando izquierdo de la += y -= operadores (asignación de evento).An event can be used as the left-hand operand of the += and -= operators (Event assignment). Estos operadores se utilizan, respectivamente, para adjuntar controladores de eventos a o quitar controladores de eventos de un evento, y los modificadores de acceso del evento controlan los contextos en los que se permiten estas operaciones.These operators are used, respectively, to attach event handlers to or to remove event handlers from an event, and the access modifiers of the event control the contexts in which such operations are permitted.

Puesto que += y -= son las únicas operaciones permitidas en un evento fuera del tipo que declara el evento, el código externo pueden agregar y quitar controladores de evento, pero no en ninguna otra forma de obtener o modificar la lista subyacente del evento controladores.Since += and -= are the only operations that are permitted on an event outside the type that declares the event, external code can add and remove handlers for an event, but cannot in any other way obtain or modify the underlying list of event handlers.

En una operación del formulario x += y o x -= y, cuando x es un evento y la referencia tiene lugar fuera del tipo que contiene la declaración de x, el resultado de la operación tiene el tipo void (en lugar de tener el tipo de x, con el valor de x después de la asignación).In an operation of the form x += y or x -= y, when x is an event and the reference takes place outside the type that contains the declaration of x, the result of the operation has type void (as opposed to having the type of x, with the value of x after the assignment). Esta regla evita que código externo examine indirectamente el delegado subyacente de un evento.This rule prohibits external code from indirectly examining the underlying delegate of an event.

El ejemplo siguiente muestra cómo se adjuntan los controladores de eventos a instancias de la Button clase:The following example shows how event handlers are attached to instances of the Button class:

public delegate void EventHandler(object sender, EventArgs e);

public class Button: Control
{
    public event EventHandler Click;
}

public class LoginDialog: Form
{
    Button OkButton;
    Button CancelButton;

    public LoginDialog() {
        OkButton = new Button(...);
        OkButton.Click += new EventHandler(OkButtonClick);
        CancelButton = new Button(...);
        CancelButton.Click += new EventHandler(CancelButtonClick);
    }

    void OkButtonClick(object sender, EventArgs e) {
        // Handle OkButton.Click event
    }

    void CancelButtonClick(object sender, EventArgs e) {
        // Handle CancelButton.Click event
    }
}

En este caso, el LoginDialog constructor de instancia, crea dos Button instancias y adjunta los controladores de eventos para el Click eventos.Here, the LoginDialog instance constructor creates two Button instances and attaches event handlers to the Click events.

Eventos como si fueran camposField-like events

En el texto de programa de la clase o estructura que contiene la declaración de un evento, ciertos eventos pueden usarse como campos.Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. Para su uso en este modo, no debe ser un evento abstract o externy no debe incluir explícitamente event_accessor_declarations.To be used in this way, an event must not be abstract or extern, and must not explicitly include event_accessor_declarations. Este evento puede utilizarse en cualquier contexto que permite que un campo.Such an event can be used in any context that permits a field. El campo contiene un delegado (delegados) que hace referencia a la lista de controladores de eventos que se han agregado al evento.The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event. Si no hay controladores de eventos se han agregado, el campo contiene null.If no event handlers have been added, the field contains null.

En el ejemploIn the example

public delegate void EventHandler(object sender, EventArgs e);

public class Button: Control
{
    public event EventHandler Click;

    protected void OnClick(EventArgs e) {
        if (Click != null) Click(this, e);
    }

    public void Reset() {
        Click = null;
    }
}

Click se utiliza como un campo dentro de la Button clase.Click is used as a field within the Button class. Como se muestra en el ejemplo, el campo puede examinar, modificar y utilizado en expresiones de invocación del delegado.As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. El OnClick método en el Button clase "genera" el Click eventos.The OnClick method in the Button class "raises" the Click event. La noción de generar un evento es equivalente exactamente a invocar el delegado representado por el evento; por lo tanto, no hay ninguna construcción especial de lenguaje para generar eventos.The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events. Tenga en cuenta que la invocación del delegado está precedida por una comprobación que garantiza que el delegado no es null.Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

Fuera de la declaración de la Button (clase), el Click miembro solo puede usarse en el lado izquierdo de la += y -= operadores, como enOutside the declaration of the Button class, the Click member can only be used on the left-hand side of the += and -= operators, as in

b.Click += new EventHandler(...);

que anexa un delegado a la lista de invocación de la Click eventos, ywhich appends a delegate to the invocation list of the Click event, and

b.Click -= new EventHandler(...);

que quita un delegado de la lista de invocaciones del Click eventos.which removes a delegate from the invocation list of the Click event.

Cuando se compila un campo como un evento, el compilador crea el almacenamiento para almacenar al delegado y crea los descriptores de acceso para el evento que agregan o quitan controladores de eventos para el campo de delegado.When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. Las operaciones de adición y eliminación son subprocesos y puede (pero no es necesarias) se haya hecho mientras mantiene el bloqueo (la instrucción lock) en el objeto contenedor para un evento de instancia o el objeto de tipo (anónimo expresiones de creación de objetos) para un evento estático.The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock (The lock statement) on the containing object for an instance event, or the type object (Anonymous object creation expressions) for a static event.

Por lo tanto, una instancia declaración de evento del formulario:Thus, an instance event declaration of the form:

class X
{
    public event D Ev;
}

se compilará en un valor equivalente al:will be compiled to something equivalent to:

class X
{
    private D __Ev;  // field to hold the delegate

    public event D Ev {
        add {
            /* add the delegate in a thread safe way */
        }

        remove {
            /* remove the delegate in a thread safe way */
        }
    }
}

Dentro de la clase X, las referencias a Ev en el lado izquierdo de la += y -= operadores ocasionar el agregar y quitar los descriptores de acceso que se debe invocar.Within the class X, references to Ev on the left-hand side of the += and -= operators cause the add and remove accessors to be invoked. Todas las referencias a Ev se compilan para hacer referencia al campo oculto __Ev en su lugar (acceso a miembros).All other references to Ev are compiled to reference the hidden field __Ev instead (Member access). El nombre "__Ev" es arbitrario; el campo oculto no puede tener cualquier nombre o en absoluto.The name "__Ev" is arbitrary; the hidden field could have any name or no name at all.

Descriptores de acceso de un eventoEvent accessors

Declaraciones de eventos normalmente se omiten event_accessor_declarations, como en el Button ejemplo anterior.Event declarations typically omit event_accessor_declarations, as in the Button example above. Una situación para hacerlo es en el caso en que el costo de almacenamiento de un campo por el evento no es aceptable.One situation for doing so involves the case in which the storage cost of one field per event is not acceptable. En tales casos, puede incluir una clase event_accessor_declarations y utilizar un mecanismo privado para almacenar la lista de controladores de eventos.In such cases, a class can include event_accessor_declarations and use a private mechanism for storing the list of event handlers.

El event_accessor_declarations de un evento, especifique las instrucciones ejecutables asociadas con la adición y eliminación de controladores de eventos.The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers.

Las declaraciones de descriptor de acceso que constan de un add_accessor_declaration y un remove_accessor_declaration.The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Cada declaración de descriptor de acceso está formado por el token add o remove seguido por un bloque.Each accessor declaration consists of the token add or remove followed by a block. El bloque asociado con un add_accessor_declaration especifica las instrucciones que se ejecutará cuando se agrega un controlador de eventos y el bloque asociado con un remove_accessor_declaration especifica las instrucciones que se ejecutará cuando se quita un controlador de eventos.The block associated with an add_accessor_declaration specifies the statements to execute when an event handler is added, and the block associated with a remove_accessor_declaration specifies the statements to execute when an event handler is removed.

Cada add_accessor_declaration y remove_accessor_declaration corresponde a un método con un parámetro de valor único del tipo de evento y un void tipo de valor devuelto.Each add_accessor_declaration and remove_accessor_declaration corresponds to a method with a single value parameter of the event type and a void return type. El parámetro implícito de un descriptor de acceso de eventos se denomina value.The implicit parameter of an event accessor is named value. Cuando se usa un evento en una asignación de evento, se utiliza el descriptor de acceso de eventos adecuado.When an event is used in an event assignment, the appropriate event accessor is used. En concreto, si el operador de asignación es += , a continuación, se utiliza el descriptor de acceso add y si el operador de asignación es -= , a continuación, se utiliza el descriptor de acceso remove.Specifically, if the assignment operator is += then the add accessor is used, and if the assignment operator is -= then the remove accessor is used. En cualquier caso, el operando derecho del operador de asignación se usa como argumento para el descriptor de acceso de eventos.In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. El bloque de un add_accessor_declaration o un remove_accessor_declaration debe cumplir las reglas de void métodos descritos en cuerpo del método.The block of an add_accessor_declaration or a remove_accessor_declaration must conform to the rules for void methods described in Method body. En concreto, return no se permiten las instrucciones de dicho bloque al especificar una expresión.In particular, return statements in such a block are not permitted to specify an expression.

Puesto que un descriptor de acceso de eventos tiene implícitamente un parámetro denominado value, es un error en tiempo de compilación para una variable o constante local declarados en un descriptor de acceso de eventos para que ese nombre.Since an event accessor implicitly has a parameter named value, it is a compile-time error for a local variable or constant declared in an event accessor to have that name.

En el ejemploIn the example

class Control: Component
{
    // Unique keys for events
    static readonly object mouseDownEventKey = new object();
    static readonly object mouseUpEventKey = new object();

    // Return event handler associated with key
    protected Delegate GetEventHandler(object key) {...}

    // Add event handler associated with key
    protected void AddEventHandler(object key, Delegate handler) {...}

    // Remove event handler associated with key
    protected void RemoveEventHandler(object key, Delegate handler) {...}

    // MouseDown event
    public event MouseEventHandler MouseDown {
        add { AddEventHandler(mouseDownEventKey, value); }
        remove { RemoveEventHandler(mouseDownEventKey, value); }
    }

    // MouseUp event
    public event MouseEventHandler MouseUp {
        add { AddEventHandler(mouseUpEventKey, value); }
        remove { RemoveEventHandler(mouseUpEventKey, value); }
    }

    // Invoke the MouseUp event
    protected void OnMouseUp(MouseEventArgs args) {
        MouseEventHandler handler; 
        handler = (MouseEventHandler)GetEventHandler(mouseUpEventKey);
        if (handler != null)
            handler(this, args);
    }
}

la Control clase implementa un mecanismo de almacenamiento interno para los eventos.the Control class implements an internal storage mechanism for events. El AddEventHandler método asocia un valor de delegado con una clave, el GetEventHandler método devuelve el delegado asociado actualmente a una clave y el RemoveEventHandler método quita un delegado como un controlador de eventos para el evento especificado.The AddEventHandler method associates a delegate value with a key, the GetEventHandler method returns the delegate currently associated with a key, and the RemoveEventHandler method removes a delegate as an event handler for the specified event. Presumiblemente, el mecanismo de almacenamiento subyacente está diseñado, como que no hay ningún costo para asociar un null delegado de valor con una clave y, por tanto, los eventos no controlados no consumen almacenamiento.Presumably, the underlying storage mechanism is designed such that there is no cost for associating a null delegate value with a key, and thus unhandled events consume no storage.

Eventos estáticos y de instanciaStatic and instance events

Cuando una declaración de evento incluye un static modificador, el evento se dice que un evento estático.When an event declaration includes a static modifier, the event is said to be a static event. Cuando no hay ninguna static modificador está presente, el evento se dice que un eventos de instancia.When no static modifier is present, the event is said to be an instance event.

Un evento estático no está asociado a una instancia concreta, y es un error en tiempo de compilación para hacer referencia a this en los descriptores de acceso de un evento estático.A static event is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static event.

Un evento de instancia está asociado con una instancia determinada de una clase, y puede tener acceso a esta instancia como this (este acceso) en los descriptores de acceso de ese evento.An instance event is associated with a given instance of a class, and this instance can be accessed as this (This access) in the accessors of that event.

Cuando se hace referencia a un evento en un member_access (acceso a miembros) del formulario E.Msi M es un evento estático, E debe denotar un tipo que contiene My si M es un evento de instancia, E debe denotar una instancia de un tipo que contenga M.When an event is referenced in a member_access (Member access) of the form E.M, if M is a static event, E must denote a type containing M, and if M is an instance event, E must denote an instance of a type containing M.

Las diferencias entre estático y los miembros de instancia se tratan con más detalle en miembros estáticos y de instancia.The differences between static and instance members are discussed further in Static and instance members.

Virtual, sellado, de reemplazo y descriptores de acceso de un evento abstractoVirtual, sealed, override, and abstract event accessors

Un virtual declaración de evento especifica que los descriptores de acceso de ese evento son virtuales.A virtual event declaration specifies that the accessors of that event are virtual. El virtual modificador se aplica a ambos descriptores de acceso de un evento.The virtual modifier applies to both accessors of an event.

Un abstract declaración de evento especifica que los descriptores de acceso del evento son virtuales, pero no proporciona una implementación real de los descriptores de acceso.An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. En su lugar, las clases derivadas no abstractas deben proporcionar su propia implementación para los descriptores de acceso invalidando el evento.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. Dado que una declaración de evento abstracto no proporciona una implementación, no puede proporcionar delimitado por llaves event_accessor_declarations.Because an abstract event declaration provides no actual implementation, it cannot provide brace-delimited event_accessor_declarations.

Una declaración de evento que incluye tanto el abstract y override modificadores especifica que el evento es abstracto y reemplaza un evento base.An event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. Los descriptores de acceso de este evento también son abstractas.The accessors of such an event are also abstract.

Solo se permiten declaraciones de eventos abstractos en clases abstractas (clases abstractas).Abstract event declarations are only permitted in abstract classes (Abstract classes).

Los descriptores de acceso de un evento virtual heredado pueden invalidarse en una clase derivada incluyendo una declaración de evento que especifica un override modificador.The accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an override modifier. Esto se conoce como un reemplazar la declaración de evento.This is known as an overriding event declaration. Una declaración de evento reemplazado no declara un nuevo evento.An overriding event declaration does not declare a new event. En su lugar, simplemente especializa las implementaciones de los descriptores de acceso de un evento virtual existente.Instead, it simply specializes the implementations of the accessors of an existing virtual event.

Una declaración de evento reemplazado debe especificar los mismos modificadores de accesibilidad exacto, tipo y nombre que el evento reemplazado.An overriding event declaration must specify the exact same accessibility modifiers, type, and name as the overridden event.

Puede incluir una declaración de evento reemplazado el sealed modificador.An overriding event declaration may include the sealed modifier. Uso de este modificador impide que una clase derivada siga reemplazando el evento.Use of this modifier prevents a derived class from further overriding the event. Los descriptores de acceso de un evento sellado también están sellados.The accessors of a sealed event are also sealed.

Es un error en tiempo de compilación para una declaración de evento reemplazado incluir un new modificador.It is a compile-time error for an overriding event declaration to include a new modifier.

Salvo por diferencias en la declaración e invocación sintaxis, virtual, sellado, de reemplazo y abstractos descriptores de acceso se comportan exactamente igual virtual, sellado, de reemplazo y métodos abstractos.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. En concreto, las reglas se describen en métodos virtuales, invalidar métodos, sellado métodos, y métodos abstractos aplicar como si los descriptores de acceso fueran métodos de la forma correspondiente.Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form. Cada descriptor de acceso corresponde a un método con un parámetro de valor único del tipo de evento, un void devolver el tipo y los mismos modificadores que el evento que lo contiene.Each accessor corresponds to a method with a single value parameter of the event type, a void return type, and the same modifiers as the containing event.

IndizadoresIndexers

Un indizador es un miembro que permite que un objeto que se debe indexar en la misma manera que una matriz.An indexer is a member that enables an object to be indexed in the same way as an array. Los indizadores se declaran mediante indexer_declarations:Indexers are declared using indexer_declarations:

indexer_declaration
    : attributes? indexer_modifier* indexer_declarator indexer_body
    ;

indexer_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'virtual'
    | 'sealed'
    | 'override'
    | 'abstract'
    | 'extern'
    | indexer_modifier_unsafe
    ;

indexer_declarator
    : type 'this' '[' formal_parameter_list ']'
    | type interface_type '.' 'this' '[' formal_parameter_list ']'
    ;

indexer_body
    : '{' accessor_declarations '}' 
    | '=>' expression ';'
    ;

Un indexer_declaration puede incluir un conjunto de atributos (atributos) y una combinación válida de los cuatro modificadores de acceso (modificadores de acceso ), el new (el nuevo modificador), virtual (métodos virtuales), override (invalidar métodos ), sealed (Sellado métodos), abstract (métodos abstractos), y extern (métodos externos) modificadores.An indexer_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

Las declaraciones de indizador están sujetos a las mismas reglas que las declaraciones de método (métodos) con respecto a las combinaciones válidas de modificadores, con la única excepción es que el modificador "static" no se permite en una declaración de indizador.Indexer declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers, with the one exception being that the static modifier is not permitted on an indexer declaration.

Los modificadores virtual, override, y abstract se excluyen mutuamente, excepto en un caso.The modifiers virtual, override, and abstract are mutually exclusive except in one case. El abstract y override modificadores pueden usarse conjuntamente para que un indizador abstracto puede reemplazar a uno virtual.The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one.

El tipo declaración especifica el tipo de elemento del indizador que se incluyen en la declaración de un indizador.The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. A menos que el indizador es una implementación de miembro de interfaz explícita, el tipo va seguido de la palabra clave this.Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. Para obtener una implementación de miembro de interfaz explícita, el tipo va seguido de un interface_type, un "." y la palabra clave this.For an explicit interface member implementation, the type is followed by an interface_type, a ".", and the keyword this. A diferencia de otros miembros, los indizadores no tienen nombres definidos por el usuario.Unlike other members, indexers do not have user-defined names.

El formal_parameter_list especifica los parámetros del indizador.The formal_parameter_list specifies the parameters of the indexer. La lista de parámetros formales de un indizador corresponde a la de un método (parámetros del método), excepto en que se debe especificar al menos un parámetro y que la ref y out no se permiten modificadores de parámetro .The formal parameter list of an indexer corresponds to that of a method (Method parameters), except that at least one parameter must be specified, and that the ref and out parameter modifiers are not permitted.

El tipo de un indizador y cada uno de los tipos que se hace referenciados en el formal_parameter_list deben ser al menos tan accesibles como el propio indexador (restricciones de accesibilidad).The type of an indexer and each of the types referenced in the formal_parameter_list must be at least as accessible as the indexer itself (Accessibility constraints).

Un indexer_body puede constar de un cuerpo del descriptor de acceso o un cuerpo de expresión.An indexer_body may either consist of an accessor body or an expression body. En el cuerpo de un descriptor de acceso, accessor_declarations, que debe incluirse en "{"y"}" tokens, declare los descriptores de acceso (descriptores de acceso) de la propiedad.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Los descriptores de acceso especifican las instrucciones ejecutables asociadas con la lectura y escritura de la propiedad.The accessors specify the executable statements associated with reading and writing the property.

Un cuerpo de expresión que consta de "=>" seguido de una expresión E y un punto y coma es igual que en el cuerpo de instrucción { get { return E; } }y por lo tanto, sólo puede utilizarse para especificar indizadores de solo captador donde es el resultado de Get proporcionado por una sola expresión.An expression body consisting of "=>" followed by an expression E and a semicolon is exactly equivalent to the statement body { get { return E; } }, and can therefore only be used to specify getter-only indexers where the result of the getter is given by a single expression.

Aunque la sintaxis para tener acceso a un elemento de indizador es el mismo que para un elemento de matriz, un elemento de indizador no está clasificado como una variable.Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Por lo tanto, no es posible pasar un elemento de indexador como un ref o out argumento.Thus, it is not possible to pass an indexer element as a ref or out argument.

La lista de parámetros formales de un indizador define la firma (firmas y sobrecarga) del indizador.The formal parameter list of an indexer defines the signature (Signatures and overloading) of the indexer. En concreto, la firma de un indexador consta del número y tipos de sus parámetros formales.Specifically, the signature of an indexer consists of the number and types of its formal parameters. El tipo de elemento y los nombres de los parámetros formales no forman parte de la firma de un indizador.The element type and names of the formal parameters are not part of an indexer's signature.

La firma de un indizador debe ser diferente de las firmas de todos los demás indexadores declarados en la misma clase.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

Los indizadores y propiedades son muy similares en concepto, pero difieren en lo siguiente:Indexers and properties are very similar in concept, but differ in the following ways:

  • Una propiedad se identifica por su nombre, mientras que un indizador se identifica por su firma.A property is identified by its name, whereas an indexer is identified by its signature.
  • Se accede a una propiedad a través de un simple_name (nombres simples) o un member_access (acceso a miembros), mientras que un indizador elemento que se accede a través de un element_access (acceso al indizador).A property is accessed through a simple_name (Simple names) or a member_access (Member access), whereas an indexer element is accessed through an element_access (Indexer access).
  • Una propiedad puede ser un static miembro, mientras que un indizador siempre es un miembro de instancia.A property can be a static member, whereas an indexer is always an instance member.
  • Un get descriptor de acceso de una propiedad que corresponde a un método sin parámetros, mientras que un get descriptor de acceso de un indizador corresponde a un método con la misma lista de parámetros formales que el indizador.A get accessor of a property corresponds to a method with no parameters, whereas a get accessor of an indexer corresponds to a method with the same formal parameter list as the indexer.
  • Un set descriptor de acceso de una propiedad que corresponde a un método con un solo parámetro denominado value, mientras que un set descriptor de acceso de un indizador corresponde a un método con la misma lista de parámetros formales que el indizador, además de un parámetro adicional denominado value.A set accessor of a property corresponds to a method with a single parameter named value, whereas a set accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named value.
  • Es un error en tiempo de compilación para un descriptor de acceso de indexador declarar una variable local con el mismo nombre que un parámetro del indizador.It is a compile-time error for an indexer accessor to declare a local variable with the same name as an indexer parameter.
  • En una declaración de propiedad reemplazada, la propiedad heredada se accede mediante la sintaxis base.P, donde P es el nombre de propiedad.In an overriding property declaration, the inherited property is accessed using the syntax base.P, where P is the property name. En una declaración de indexador de reemplazo, el indizador heredado se tiene acceso a la sintaxis base[E], donde E es una lista separada por comas de expresiones.In an overriding indexer declaration, the inherited indexer is accessed using the syntax base[E], where E is a comma separated list of expressions.
  • No hay ningún concepto de "un indizador implementado automáticamente".There is no concept of an "automatically implemented indexer". Es un error tener un indizador no abstracta, no externo con descriptores de acceso de punto y coma.It is an error to have a non-abstract, non-external indexer with semicolon accessors.

Aparte de estas diferencias, todas las reglas se definen en descriptores de acceso y implementa automáticamente propiedades se aplican a los descriptores de acceso, así como para los descriptores de acceso de propiedad.Aside from these differences, all rules defined in Accessors and Automatically implemented properties apply to indexer accessors as well as to property accessors.

Cuando se incluye una declaración de indexador una extern modificador, el indizador se dice que un indizador externo.When an indexer declaration includes an extern modifier, the indexer is said to be an external indexer. Dado que la declaración de un indizador externo no proporciona ninguna implementación real, cada uno de sus accessor_declarations consta de un punto y coma.Because an external indexer declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

El ejemplo siguiente declara un BitArray clase que implementa un indizador para tener acceso a los bits individuales de la matriz de bits.The example below declares a BitArray class that implements an indexer for accessing the individual bits in the bit array.

using System;

class BitArray
{
    int[] bits;
    int length;

    public BitArray(int length) {
        if (length < 0) throw new ArgumentException();
        bits = new int[((length - 1) >> 5) + 1];
        this.length = length;
    }

    public int Length {
        get { return length; }
    }

    public bool this[int index] {
        get {
            if (index < 0 || index >= length) {
                throw new IndexOutOfRangeException();
            }
            return (bits[index >> 5] & 1 << index) != 0;
        }
        set {
            if (index < 0 || index >= length) {
                throw new IndexOutOfRangeException();
            }
            if (value) {
                bits[index >> 5] |= 1 << index;
            }
            else {
                bits[index >> 5] &= ~(1 << index);
            }
        }
    }
}

Una instancia de la BitArray clase consume mucha menos memoria que correspondiente bool[] (ya que cada valor de la primera ocupa un solo bit en lugar de la última de un byte), pero permite las mismas operaciones que un bool[].An instance of the BitArray class consumes substantially less memory than a corresponding bool[] (since each value of the former occupies only one bit instead of the latter's one byte), but it permits the same operations as a bool[].

La siguiente CountPrimes clase utiliza un BitArray y el algoritmo de "criba" clásica para calcular el número de primos entre 1 y un máximo determinado:The following CountPrimes class uses a BitArray and the classical "sieve" algorithm to compute the number of primes between 1 and a given maximum:

class CountPrimes
{
    static int Count(int max) {
        BitArray flags = new BitArray(max + 1);
        int count = 1;
        for (int i = 2; i <= max; i++) {
            if (!flags[i]) {
                for (int j = i * 2; j <= max; j += i) flags[j] = true;
                count++;
            }
        }
        return count;
    }

    static void Main(string[] args) {
        int max = int.Parse(args[0]);
        int count = Count(max);
        Console.WriteLine("Found {0} primes between 1 and {1}", count, max);
    }
}

Tenga en cuenta que la sintaxis para tener acceso a los elementos de la BitArray es precisamente la misma que para un bool[].Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].

El ejemplo siguiente muestra una clase de 26 * 10 cuadrícula que tiene un indizador con dos parámetros.The following example shows a 26 * 10 grid class that has an indexer with two parameters. El primer parámetro debe ser una mayúscula o minúscula en el intervalo A-z y el segundo debe ser un entero en el intervalo 0-9.The first parameter is required to be an upper- or lowercase letter in the range A-Z, and the second is required to be an integer in the range 0-9.

using System;

class Grid
{
    const int NumRows = 26;
    const int NumCols = 10;

    int[,] cells = new int[NumRows, NumCols];

    public int this[char c, int col] {
        get {
            c = Char.ToUpper(c);
            if (c < 'A' || c > 'Z') {
                throw new ArgumentException();
            }
            if (col < 0 || col >= NumCols) {
                throw new IndexOutOfRangeException();
            }
            return cells[c - 'A', col];
        }

        set {
            c = Char.ToUpper(c);
            if (c < 'A' || c > 'Z') {
                throw new ArgumentException();
            }
            if (col < 0 || col >= NumCols) {
                throw new IndexOutOfRangeException();
            }
            cells[c - 'A', col] = value;
        }
    }
}

Sobrecarga de indizadoresIndexer overloading

Se describen las reglas de resolución de sobrecarga de indizador en inferencia de tipo.The indexer overload resolution rules are described in Type inference.

OperadoresOperators

Un operador es un miembro que define el significado de un operador de expresión que se puede aplicar a las instancias de la clase.An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. Los operadores se declaran mediante operator_declarations:Operators are declared using operator_declarations:

operator_declaration
    : attributes? operator_modifier+ operator_declarator operator_body
    ;

operator_modifier
    : 'public'
    | 'static'
    | 'extern'
    | operator_modifier_unsafe
    ;

operator_declarator
    : unary_operator_declarator
    | binary_operator_declarator
    | conversion_operator_declarator
    ;

unary_operator_declarator
    : type 'operator' overloadable_unary_operator '(' type identifier ')'
    ;

overloadable_unary_operator
    : '+' | '-' | '!' | '~' | '++' | '--' | 'true' | 'false'
    ;

binary_operator_declarator
    : type 'operator' overloadable_binary_operator '(' type identifier ',' type identifier ')'
    ;

overloadable_binary_operator
    : '+'   | '-'   | '*'   | '/'   | '%'   | '&'   | '|'   | '^'   | '<<'
    | right_shift | '=='  | '!='  | '>'   | '<'   | '>='  | '<='
    ;

conversion_operator_declarator
    : 'implicit' 'operator' type '(' type identifier ')'
    | 'explicit' 'operator' type '(' type identifier ')'
    ;

operator_body
    : block
    | '=>' expression ';'
    | ';'
    ;

Hay tres categorías de operadores sobrecargables: Operadores unarios (operadores unarios), los operadores binarios (operadores binarios) y los operadores de conversión (operadores de conversión).There are three categories of overloadable operators: Unary operators (Unary operators), binary operators (Binary operators), and conversion operators (Conversion operators).

El operator_body ya sea un punto y coma, un cuerpo de instrucción o un cuerpo de expresión.The operator_body is either a semicolon, a statement body or an expression body. Consta de un cuerpo de instrucción de un bloque, que especifica las instrucciones que se ejecutará cuando se invoca el operador.A statement body consists of a block, which specifies the statements to execute when the operator is invoked. El bloque debe cumplir las reglas de devolución por valor métodos descritos en cuerpo del método.The block must conform to the rules for value-returning methods described in Method body. Un cuerpo de expresión consta de => seguido de una expresión y un punto y coma y denota una expresión única para realizar cuando se invoca el operador.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked.

Para extern operadores, el operator_body consiste simplemente en un punto y coma.For extern operators, the operator_body consists simply of a semicolon. Para todos los demás operadores, el operator_body es un cuerpo de bloques o un cuerpo de expresión.For all other operators, the operator_body is either a block body or an expression body.

Las siguientes reglas se aplican a todas las declaraciones de operador:The following rules apply to all operator declarations:

  • Una declaración de un operador debe incluir tanto un public y un static modificador.An operator declaration must include both a public and a static modifier.
  • Los parámetros de un operador deben ser parámetros de valor (parámetros del valor).The parameter(s) of an operator must be value parameters (Value parameters). Es un error en tiempo de compilación para una declaración de un operador especificar ref o out parámetros.It is a compile-time error for an operator declaration to specify ref or out parameters.
  • La firma de un operador (operadores unarios, operadores binarios, operadores de conversión) deben ser diferentes de las firmas de todos los demás operadores declarados en el misma clase.The signature of an operator (Unary operators, Binary operators, Conversion operators) must differ from the signatures of all other operators declared in the same class.
  • Todos los tipos que se hace referencia en una declaración de un operador deben ser al menos tan accesibles como el propio operador (restricciones de accesibilidad).All types referenced in an operator declaration must be at least as accessible as the operator itself (Accessibility constraints).
  • Es un error para el mismo modificador aparece varias veces en una declaración de operador.It is an error for the same modifier to appear multiple times in an operator declaration.

Cada categoría de operador impone restricciones adicionales, como se describe en las secciones siguientes.Each operator category imposes additional restrictions, as described in the following sections.

Al igual que otros miembros, las clases derivadas heredan los operadores declarados en una clase base.Like other members, operators declared in a base class are inherited by derived classes. Dado que las declaraciones de operador siempre requieren la clase o estructura en la que se declara el operador para participar en la firma del operador, no es posible que un operador declarado en una clase derivada ocultar un operador declarado en una clase base.Because operator declarations always require the class or struct in which the operator is declared to participate in the signature of the operator, it is not possible for an operator declared in a derived class to hide an operator declared in a base class. Por lo tanto, el new modificador nunca es necesaria y, por lo tanto, no se permite en una declaración de operador.Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

Encontrará información adicional sobre los operadores unarios y binarios en operadores.Additional information on unary and binary operators can be found in Operators.

Se puede encontrar información adicional sobre operadores de conversión en conversiones definidas por el usuario.Additional information on conversion operators can be found in User-defined conversions.

Operadores unariosUnary operators

Las reglas siguientes se aplican a las declaraciones de operador unario, donde T denota el tipo de instancia de la clase o estructura que contiene la declaración del operador:The following rules apply to unary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Unario +, -, !, o ~ operador debe tomar un único parámetro de tipo T o T? y puede devolver cualquier tipo.A unary +, -, !, or ~ operator must take a single parameter of type T or T? and can return any type.
  • Unario ++ o -- operador debe tomar un único parámetro de tipo T o T? y debe devolver que mismo tipo o un tipo derivado de éste.A unary ++ or -- operator must take a single parameter of type T or T? and must return that same type or a type derived from it.
  • Unario true o false operador debe tomar un único parámetro de tipo T o T? y debe devolver un tipo bool.A unary true or false operator must take a single parameter of type T or T? and must return type bool.

La firma de un operador unario consiste en el símbolo de operador (+, -, !, ~, ++, --, true, o false) y el tipo de parámetro formal.The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. El tipo de valor devuelto no es parte de la firma de un operador unario, ni es el nombre del parámetro formal.The return type is not part of a unary operator's signature, nor is the name of the formal parameter.

El true y false operadores unarios requieren declaración par a par.The true and false unary operators require pair-wise declaration. Si una clase declara uno de estos operadores sin declarar el otro, se produce un error de tiempo de compilación.A compile-time error occurs if a class declares one of these operators without also declaring the other. El true y false operadores se describen más detalladamente en operadores lógicos condicionales definidos por el usuario y expresiones booleanas.The true and false operators are described further in User-defined conditional logical operators and Boolean expressions.

El ejemplo siguiente muestra una implementación y el uso posterior de operator ++ para una clase de vector de enteros:The following example shows an implementation and subsequent usage of operator ++ for an integer vector class:

public class IntVector
{
    public IntVector(int length) {...}

    public int Length {...}                 // read-only property

    public int this[int index] {...}        // read-write indexer

    public static IntVector operator ++(IntVector iv) {
        IntVector temp = new IntVector(iv.Length);
        for (int i = 0; i < iv.Length; i++)
            temp[i] = iv[i] + 1;
        return temp;
    }
}

class Test
{
    static void Main() {
        IntVector iv1 = new IntVector(4);    // vector of 4 x 0
        IntVector iv2;

        iv2 = iv1++;    // iv2 contains 4 x 0, iv1 contains 4 x 1
        iv2 = ++iv1;    // iv2 contains 4 x 2, iv1 contains 4 x 2
    }
}

Tenga en cuenta cómo el método de operador devuelve el valor producido por sumar 1 al operando, al igual que el incremento de postfijo y operadores de decremento (operadores postfijos de incremento y decremento) y el prefijo de incremento y decremento operadores (prefijo de incremento y decremento operadores).Note how the operator method returns the value produced by adding 1 to the operand, just like the postfix increment and decrement operators (Postfix increment and decrement operators), and the prefix increment and decrement operators (Prefix increment and decrement operators). A diferencia de en C++, este método necesita no modifica el valor de su operando directamente.Unlike in C++, this method need not modify the value of its operand directly. De hecho, modificar el valor del operando infringiría la semántica estándar del operador de incremento de postfijo.In fact, modifying the operand value would violate the standard semantics of the postfix increment operator.

Operadores binariosBinary operators

Las reglas siguientes se aplican a las declaraciones de operador binario, donde T denota el tipo de instancia de la clase o estructura que contiene la declaración del operador:The following rules apply to binary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Un operador de desplazamiento que no sea binario debe tomar dos parámetros, al menos uno de los cuales debe tener tipo T o T?y puede devolver cualquier tipo.A binary non-shift operator must take two parameters, at least one of which must have type T or T?, and can return any type.
  • Un archivo binario << o >> operador debe tomar dos parámetros, el primero de los cuales debe tener tipo T o T? y el segundo de los cuales debe tener tipo int o int?y puede devolver cualquier tipo.A binary << or >> operator must take two parameters, the first of which must have type T or T? and the second of which must have type int or int?, and can return any type.

La firma de un operador binario consiste en el símbolo de operador (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, o <=) y los tipos de los dos parámetros formales.The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. El tipo de valor devuelto y los nombres de los parámetros formales no forman parte de la firma de un operador binario.The return type and the names of the formal parameters are not part of a binary operator's signature.

Algunos operadores binarios requieren declaración par a par.Certain binary operators require pair-wise declaration. Para todas las declaraciones de cualquier operador de un par, debe haber una declaración del operador del par coincidente.For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. Coincide con dos declaraciones de operador cuando tienen el mismo tipo de valor devuelto y el mismo tipo para cada parámetro.Two operator declarations match when they have the same return type and the same type for each parameter. Los siguientes operadores requieren declaración por pares:The following operators require pair-wise declaration:

  • operator == y operator !=operator == and operator !=
  • operator > y operator <operator > and operator <
  • operator >= y operator <=operator >= and operator <=

Operadores de conversiónConversion operators

Una declaración de operador de conversión introduce un conversión definida por el usuario (conversiones definidas por el usuario) que aumenta las conversiones explícitas e implícitas predefinidas.A conversion operator declaration introduces a user-defined conversion (User-defined conversions) which augments the pre-defined implicit and explicit conversions.

Una declaración de operador de conversión que incluye la implicit palabra clave define una conversión implícita de definido por el usuario.A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Conversiones implícitas pueden ocurrir en una variedad de situaciones, incluidas las llamadas a funciones miembro, expresiones de conversión y asignaciones.Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. Esto se describe más detalladamente en conversiones implícitas.This is described further in Implicit conversions.

Una declaración de operador de conversión que incluye la explicit palabra clave define una conversión explícita de definido por el usuario.A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Las conversiones explícitas pueden producir en expresiones de conversión y se describe más detalladamente en las conversiones explícitas.Explicit conversions can occur in cast expressions, and are described further in Explicit conversions.

Un operador de conversión convierte de un tipo de origen, indicado por el tipo de parámetro del operador de conversión a un tipo de destino indicado por el tipo de valor devuelto del operador de conversión.A conversion operator converts from a source type, indicated by the parameter type of the conversion operator, to a target type, indicated by the return type of the conversion operator.

Para un tipo de origen dado S y tipo de destino Tsi S o T son tipos que aceptan valores NULL, permiten S0 y T0 hacen referencia a sus tipos subyacentes, de lo contrario S0 y T0 son igual que S y T respectivamente.For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. Una clase o struct se puede declarar una conversión de un tipo de origen S a un tipo de destino T sólo si se cumplen todas las opciones siguientes:A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

  • S0 y T0 son tipos diferentes.S0 and T0 are different types.
  • Ya sea S0 o T0 es el tipo de clase o estructura en la que realiza la declaración del operador.Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • Ni S0 ni T0 es un interface_type.Neither S0 nor T0 is an interface_type.
  • Excluyendo las conversiones definidas por el usuario, no existe una conversión desde S a T o desde T a S.Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

Para los fines de estas reglas, cualquier tipo de parámetros asociados con S o T se consideran tipos únicos que tienen ninguna relación de herencia con otros tipos y todas las restricciones de tipo de esos parámetros se omiten.For the purposes of these rules, any type parameters associated with S or T are considered to be unique types that have no inheritance relationship with other types, and any constraints on those type parameters are ignored.

En el ejemploIn the example

class C<T> {...}

class D<T>: C<T>
{
    public static implicit operator C<int>(D<T> value) {...}      // Ok
    public static implicit operator C<string>(D<T> value) {...}   // Ok
    public static implicit operator C<T>(D<T> value) {...}        // Error
}

se permiten las declaraciones de dos operador primera porque, para los fines de indizadores.3, T y int y string respectivamente se consideran tipos únicos sin ninguna relación.the first two operator declarations are permitted because, for the purposes of Indexers.3, T and int and string respectively are considered unique types with no relationship. Sin embargo, el tercer operador es un error porque C<T> es la clase base de D<T>.However, the third operator is an error because C<T> is the base class of D<T>.

Desde la segunda regla que sigue un operador de conversión debe convertir hacia o desde el tipo de clase o estructura en la que se declara el operador.From the second rule it follows that a conversion operator must convert either to or from the class or struct type in which the operator is declared. Por ejemplo, es posible que un tipo de clase o struct C para definir una conversión de C a int y desde int a C, pero no desde int a bool.For example, it is possible for a class or struct type C to define a conversion from C to int and from int to C, but not from int to bool.

No es posible volver a definir directamente una conversión predefinida.It is not possible to directly redefine a pre-defined conversion. Por lo tanto, no se permiten los operadores de conversión para convertir de o a object porque ya existen conversiones implícitas y explícitas entre object y todos los demás tipos.Thus, conversion operators are not allowed to convert from or to object because implicit and explicit conversions already exist between object and all other types. Del mismo modo, ni el origen ni los tipos de destino de una conversión pueden ser un tipo base del otro, ya que entonces ya existiría una conversión.Likewise, neither the source nor the target types of a conversion can be a base type of the other, since a conversion would then already exist.

Sin embargo, es posible declarar los operadores en tipos genéricos que, para los argumentos de tipo determinado, especifican las conversiones que ya existen como conversiones predefinidas.However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions. En el ejemploIn the example

struct Convertible<T>
{
    public static implicit operator Convertible<T>(T value) {...}
    public static explicit operator T(Convertible<T> value) {...}
}

Cuando escriba object se especifica como un argumento de tipo para T, el segundo operador declara una conversión que ya existe (implícita y por lo tanto también explícito, no existe conversión de cualquier tipo object).when type object is specified as a type argument for T, the second operator declares a conversion that already exists (an implicit, and therefore also an explicit, conversion exists from any type to type object).

En casos donde no existe una conversión predefinida entre dos tipos, se omiten cualquier conversiones definidas por el usuario entre esos tipos.In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. De manera específica:Specifically:

  • Si una conversión implícita predefinida (conversiones implícitas) existe desde tipo S al tipo T, todos los definidos por el usuario conversiones (implícitas o explícitas) de S a T se omiten.If a pre-defined implicit conversion (Implicit conversions) exists from type S to type T, all user-defined conversions (implicit or explicit) from S to T are ignored.
  • Si una conversión explícita predefinida (las conversiones explícitas) existe desde tipo S al tipo T, las conversiones explícitas definidas por el usuario de S a T se omiten.If a pre-defined explicit conversion (Explicit conversions) exists from type S to type T, any user-defined explicit conversions from S to T are ignored. Furthermore:Furthermore:

Si T es un tipo de interfaz definido por el usuario conversiones implícitas de S a T se omiten.If T is an interface type, user-defined implicit conversions from S to T are ignored.

De lo contrario, definido por el usuario conversiones implícitas de S a T todavía se consideran.Otherwise, user-defined implicit conversions from S to T are still considered.

Para todos los tipos, pero object, los operadores declarados por el Convertible<T> tipo anterior no entren en conflicto con las conversiones predefinidas.For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions. Por ejemplo:For example:

void F(int i, Convertible<int> n) {
    i = n;                          // Error
    i = (int)n;                     // User-defined explicit conversion
    n = i;                          // User-defined implicit conversion
    n = (Convertible<int>)i;        // User-defined implicit conversion
}

Sin embargo, para el tipo object, conversiones predefinidas ocultar las conversiones definidas por el usuario en todos los casos, pero uno:However, for type object, pre-defined conversions hide the user-defined conversions in all cases but one:

void F(object o, Convertible<object> n) {
    o = n;                         // Pre-defined boxing conversion
    o = (object)n;                 // Pre-defined boxing conversion
    n = o;                         // User-defined implicit conversion
    n = (Convertible<object>)o;    // Pre-defined unboxing conversion
}

No se permiten conversiones definidas por el usuario para convertir de o a interface_types.User-defined conversions are not allowed to convert from or to interface_types. En concreto, esta restricción garantiza que ninguna transformación definido por el usuario se produce cuando se convierte en un interface_typey que una conversión a un interface_type se realizará correctamente solo si el objeto que se está convirtiendo implementa realmente especificado interface_type.In particular, this restriction ensures that no user-defined transformations occur when converting to an interface_type, and that a conversion to an interface_type succeeds only if the object being converted actually implements the specified interface_type.

La firma de un operador de conversión está formada por el tipo de origen y el tipo de destino.The signature of a conversion operator consists of the source type and the target type. (Tenga en cuenta que esta es la única forma de miembro para el que el tipo de valor devuelto participa en la firma.) El implicit o explicit clasificación de un operador de conversión no forma parte de la firma del operador.(Note that this is the only form of member for which the return type participates in the signature.) The implicit or explicit classification of a conversion operator is not part of the operator's signature. Por lo tanto, una clase o struct no puede declarar tanto un implicit y un explicit operador de conversión con los mismos tipos de origen y de destino.Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

En general, las conversiones implícitas definido por el usuario deben diseñarse para nunca produzcan excepciones ni pérdida de información.In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. Si una conversión definida por el usuario puede dar lugar a excepciones (por ejemplo, porque el argumento de origen está fuera del intervalo) o pérdida de información (por ejemplo, descartar los bits de orden superior), dicha conversión debe definirse como una conversión explícita.If a user-defined conversion can give rise to exceptions (for example, because the source argument is out of range) or loss of information (such as discarding high-order bits), then that conversion should be defined as an explicit conversion.

En el ejemploIn the example

using System;

public struct Digit
{
    byte value;

    public Digit(byte value) {
        if (value < 0 || value > 9) throw new ArgumentException();
        this.value = value;
    }

    public static implicit operator byte(Digit d) {
        return d.value;
    }

    public static explicit operator Digit(byte b) {
        return new Digit(b);
    }
}

la conversión de Digit a byte es implícita porque nunca produce excepciones o pierde información, pero la conversión de byte a Digit es explícito desde Digit sólo se puede representar un subconjunto de los posibles los valores de un byte.the conversion from Digit to byte is implicit because it never throws exceptions or loses information, but the conversion from byte to Digit is explicit since Digit can only represent a subset of the possible values of a byte.

Constructores de instanciasInstance constructors

Un constructor de instancia es un miembro que implementa las acciones necesarias para inicializar una instancia de una clase.An instance constructor is a member that implements the actions required to initialize an instance of a class. Constructores de instancia se declaran mediante constructor_declarations:Instance constructors are declared using constructor_declarations:

constructor_declaration
    : attributes? constructor_modifier* constructor_declarator constructor_body
    ;

constructor_modifier
    : 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'extern'
    | constructor_modifier_unsafe
    ;

constructor_declarator
    : identifier '(' formal_parameter_list? ')' constructor_initializer?
    ;

constructor_initializer
    : ':' 'base' '(' argument_list? ')'
    | ':' 'this' '(' argument_list? ')'
    ;

constructor_body
    : block
    | ';'
    ;

Un constructor_declaration puede incluir un conjunto de atributos (atributos), una combinación válida de los cuatro modificadores de acceso (modificadores de acceso ) y un extern (métodos externos) modificador.A constructor_declaration may include a set of attributes (Attributes), a valid combination of the four access modifiers (Access modifiers), and an extern (External methods) modifier. No se permite una declaración de constructor para incluir el mismo modificador varias veces.A constructor declaration is not permitted to include the same modifier multiple times.

El identificador de un constructor_declarator debe nombrar la clase en el que se declara el constructor de instancia.The identifier of a constructor_declarator must name the class in which the instance constructor is declared. Si se especifica ningún otro nombre, se produce un error en tiempo de compilación.If any other name is specified, a compile-time error occurs.

El elemento opcional formal_parameter_list de una instancia del constructor está sujeto a las mismas reglas que el formal_parameter_list de un método (métodos).The optional formal_parameter_list of an instance constructor is subject to the same rules as the formal_parameter_list of a method (Methods). La lista de parámetros formales define la firma (firmas y sobrecarga) de un constructor de instancia y se controla mediante el cual el proceso de resolución de sobrecarga (inferencia) selecciona un determinado constructor de instancia en una invocación.The formal parameter list defines the signature (Signatures and overloading) of an instance constructor and governs the process whereby overload resolution (Type inference) selects a particular instance constructor in an invocation.

Cada uno de los tipos que se hace referenciados en el formal_parameter_list de una instancia del constructor debe ser al menos tan accesible como el propio constructor (restricciones de accesibilidad).Each of the types referenced in the formal_parameter_list of an instance constructor must be at least as accessible as the constructor itself (Accessibility constraints).

El elemento opcional constructor_initializer especifica otro constructor de instancia para invocar antes de ejecutar las instrucciones que aparecen la constructor_body de este constructor de instancia.The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. Esto se describe más detalladamente en inicializadores de Constructor.This is described further in Constructor initializers.

Cuando una declaración de constructor incluye un extern modificador, el constructor se dice que un constructor externo.When a constructor declaration includes an extern modifier, the constructor is said to be an external constructor. Dado que una declaración de constructor externo no proporciona ninguna implementación real, su constructor_body consta de un punto y coma.Because an external constructor declaration provides no actual implementation, its constructor_body consists of a semicolon. Para todos los demás constructores, el constructor_body consta de un bloque que especifica las instrucciones para inicializar una nueva instancia de la clase.For all other constructors, the constructor_body consists of a block which specifies the statements to initialize a new instance of the class. Esto corresponde exactamente a la bloque de un método de instancia con un void tipo de valor devuelto (cuerpo del método).This corresponds exactly to the block of an instance method with a void return type (Method body).

No se heredan los constructores de instancia.Instance constructors are not inherited. Por lo tanto, una clase no tiene ningún constructor de instancia distinto de aquéllos declarados realmente en la clase.Thus, a class has no instance constructors other than those actually declared in the class. Si una clase no contiene ninguna declaración de constructor de instancia, automáticamente se proporciona un constructor de instancia predeterminado (constructores predeterminados).If a class contains no instance constructor declarations, a default instance constructor is automatically provided (Default constructors).

Los constructores de instancia se invocan mediante object_creation_expressions (expresiones de creación de objetos) y constructor_initializers.Instance constructors are invoked by object_creation_expressions (Object creation expressions) and through constructor_initializers.

Inicializadores del constructorConstructor initializers

Todos los constructores de instancias (los de la clase excepto object) incluyen implícitamente una invocación de otro constructor de instancia inmediatamente antes del constructor_body.All instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. El constructor debe invocar implícitamente viene determinada por la constructor_initializer:The constructor to implicitly invoke is determined by the constructor_initializer:

  • Un inicializador de constructor de instancia del formulario base(argument_list) o base() hace que un constructor de instancia de la clase base directa.An instance constructor initializer of the form base(argument_list) or base() causes an instance constructor from the direct base class to be invoked. Este constructor se selecciona mediante argument_list si existe y las reglas de resolución de sobrecarga de de resolución de sobrecarga.That constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. El conjunto de constructores de instancias de candidato consta de todos los constructores de instancia accesible contenidos en la clase base directa o el constructor predeterminado (constructores predeterminados), si se declara ningún constructor de instancia en el clase base directa.The set of candidate instance constructors consists of all accessible instance constructors contained in the direct base class, or the default constructor (Default constructors), if no instance constructors are declared in the direct base class. Si este conjunto está vacío, o si no se puede identificar un mejor constructor de instancia, se produce un error en tiempo de compilación.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.
  • Un inicializador de constructor de instancia del formulario this(argument-list) o this() hace que un constructor de instancia de la clase en sí que se debe invocar.An instance constructor initializer of the form this(argument-list) or this() causes an instance constructor from the class itself to be invoked. El constructor se selecciona utilizando argument_list si existe y las reglas de resolución de sobrecarga de de resolución de sobrecarga.The constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. El conjunto de constructores de instancias de candidato consta de todos los constructores de instancia accesible declarados en la propia clase.The set of candidate instance constructors consists of all accessible instance constructors declared in the class itself. Si este conjunto está vacío, o si no se puede identificar un mejor constructor de instancia, se produce un error en tiempo de compilación.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. Si una declaración de constructor de instancia incluye a un inicializador de constructor que invoca el constructor en Sí, se produce un error en tiempo de compilación.If an instance constructor declaration includes a constructor initializer that invokes the constructor itself, a compile-time error occurs.

Si un constructor de instancia no tiene ningún inicializador de constructor, un inicializador de constructor del formulario base() se proporciona implícitamente.If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. Por lo tanto, una declaración de constructor de instancia del formularioThus, an instance constructor declaration of the form

C(...) {...}

equivale exactamente ais exactly equivalent to

C(...): base() {...}

El ámbito de los parámetros proporcionados por el formal_parameter_list de un constructor de instancia declaración incluye el inicializador de constructor de dicha declaración.The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. Por lo tanto, se permite un inicializador de constructor para tener acceso a los parámetros del constructor.Thus, a constructor initializer is permitted to access the parameters of the constructor. Por ejemplo:For example:

class A
{
    public A(int x, int y) {}
}

class B: A
{
    public B(int x, int y): base(x + y, x - y) {}
}

Un inicializador de constructor de instancia no puede tener acceso a la instancia que se está creando.An instance constructor initializer cannot access the instance being created. Por lo tanto, es un error en tiempo de compilación para hacer referencia a this en una expresión de argumento del inicializador del constructor, como es un error en tiempo de compilación para una expresión de argumento hacer referencia a cualquier miembro de instancia a través de un simple_name.Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple_name.

Inicializadores de variables de instanciaInstance variable initializers

Cuando un constructor de instancia no tiene ningún inicializador de constructor, o tiene un inicializador de constructor del formulario base(...), implícitamente el constructor realiza las inicializaciones especificadas por el variable_initializers de los campos de instancia declaran en su clase.When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable_initializers of the instance fields declared in its class. Esto corresponde a una secuencia de asignaciones que se ejecutan inmediatamente al entrar en el constructor y antes de la invocación del constructor de clase base directa implícita.This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. Los inicializadores de variable se ejecutan en el orden textual en que aparecen en la declaración de clase.The variable initializers are executed in the textual order in which they appear in the class declaration.

Ejecución de un constructorConstructor execution

Inicializadores de variables se transforman en instrucciones de asignación y se ejecutan estas instrucciones de asignación antes de la invocación del constructor de instancia de clase base.Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. Este orden garantiza que todos los campos de instancia se inicializan por sus inicializadores de variable antes de que se ejecutan las instrucciones que tienen acceso a esa instancia.This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.

Dado el ejemplo:Given the example

using System;

class A
{
    public A() {
        PrintFields();
    }

    public virtual void PrintFields() {}
}

class B: A
{
    int x = 1;
    int y;

    public B() {
        y = -1;
    }

    public override void PrintFields() {
        Console.WriteLine("x = {0}, y = {1}", x, y);
    }
}

Cuando new B() se utiliza para crear una instancia de B, se produce el siguiente resultado:when new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

El valor de x es 1 porque el inicializador de variable se ejecuta antes de invoca el constructor de instancia de la clase base.The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. Sin embargo, el valor de y es 0 (el valor predeterminado de un int) porque la asignación a y no se ejecuta hasta que el constructor de clase base devuelve.However, the value of y is 0 (the default value of an int) because the assignment to y is not executed until after the base class constructor returns.

Resulta útil pensar en los inicializadores de variables de instancia y los inicializadores de constructor como instrucciones que se insertan automáticamente antes de la constructor_body.It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor_body. El ejemploThe example

using System;
using System.Collections;

class A
{
    int x = 1, y = -1, count;

    public A() {
        count = 0;
    }

    public A(int n) {
        count = n;
    }
}

class B: A
{
    double sqrt2 = Math.Sqrt(2.0);
    ArrayList items = new ArrayList(100);
    int max;

    public B(): this(100) {
        items.Add("default");
    }

    public B(int n): base(n - 1) {
        max = n;
    }
}

contiene a varios inicializadores de variables; También contiene los inicializadores de constructor de ambas formas (base y this).contains several variable initializers; it also contains constructor initializers of both forms (base and this). El ejemplo se corresponde con el código se muestra a continuación, donde cada comentario indica una instrucción que se inserta automáticamente (la sintaxis utilizada para invocar el constructor insertado automáticamente no es válido, pero sirve para ilustrar el mecanismo).The example corresponds to the code shown below, where each comment indicates an automatically inserted statement (the syntax used for the automatically inserted constructor invocations isn't valid, but merely serves to illustrate the mechanism).

using System.Collections;

class A
{
    int x, y, count;

    public A() {
        x = 1;                       // Variable initializer
        y = -1;                      // Variable initializer
        object();                    // Invoke object() constructor
        count = 0;
    }

    public A(int n) {
        x = 1;                       // Variable initializer
        y = -1;                      // Variable initializer
        object();                    // Invoke object() constructor
        count = n;
    }
}

class B: A
{
    double sqrt2;
    ArrayList items;
    int max;

    public B(): this(100) {
        B(100);                      // Invoke B(int) constructor
        items.Add("default");
    }

    public B(int n): base(n - 1) {
        sqrt2 = Math.Sqrt(2.0);      // Variable initializer
        items = new ArrayList(100);  // Variable initializer
        A(n - 1);                    // Invoke A(int) constructor
        max = n;
    }
}

Constructores predeterminadosDefault constructors

Si una clase no contiene ninguna declaración de constructor de instancia, se proporciona automáticamente un constructor de instancia predeterminado.If a class contains no instance constructor declarations, a default instance constructor is automatically provided. El constructor predeterminado simplemente invoca el constructor sin parámetros de la clase base directa.That default constructor simply invokes the parameterless constructor of the direct base class. Si la clase es abstracta está protegida la accesibilidad declarada para el constructor predeterminado.If the class is abstract then the declared accessibility for the default constructor is protected. En caso contrario, la accesibilidad declarada para el constructor predeterminado es pública.Otherwise, the declared accessibility for the default constructor is public. Por lo tanto, es siempre el constructor predeterminado del formularioThus, the default constructor is always of the form

protected C(): base() {}

oor

public C(): base() {}

donde C es el nombre de la clase.where C is the name of the class. Si la resolución de sobrecarga no puede determinar a un único mejores candidatos para el inicializador de constructor de clase base, a continuación, se produce un error en tiempo de compilación.If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs.

En el ejemploIn the example

class Message
{
    object sender;
    string text;
}

se proporciona un constructor predeterminado porque la clase no contiene ninguna declaración de constructor de instancia.a default constructor is provided because the class contains no instance constructor declarations. Por lo tanto, el ejemplo es equivalente aThus, the example is precisely equivalent to

class Message
{
    object sender;
    string text;

    public Message(): base() {}
}

Constructores privadosPrivate constructors

Cuando una clase T declara sólo los constructores de instancia privados, no es posible para las clases fuera el texto del programa T derivar T o crear directamente instancias de T.When a class T declares only private instance constructors, it is not possible for classes outside the program text of T to derive from T or to directly create instances of T. Por lo tanto, si una clase contiene a sólo miembros estáticos y no está diseñada para ejecutarse, agregar un constructor de instancia privada vacía impedirá creación de instancias.Thus, if a class contains only static members and isn't intended to be instantiated, adding an empty private instance constructor will prevent instantiation. Por ejemplo:For example:

public class Trig
{
    private Trig() {}        // Prevent instantiation

    public const double PI = 3.14159265358979323846;

    public static double Sin(double x) {...}
    public static double Cos(double x) {...}
    public static double Tan(double x) {...}
}

La Trig clase grupos de constantes y métodos relacionados, pero no está pensada para ejecutarse.The Trig class groups related methods and constants, but is not intended to be instantiated. Por lo tanto, declara un constructor de instancia única de privado vacío.Therefore it declares a single empty private instance constructor. Debe declararse al menos un constructor de instancia para suprimir la generación automática de un constructor predeterminado.At least one instance constructor must be declared to suppress the automatic generation of a default constructor.

Parámetros del constructor de instancia opcionalOptional instance constructor parameters

El this(...) forma de inicializador de constructor se suele utilizar junto con la sobrecarga para implementar los parámetros del constructor de instancia opcional.The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. En el ejemploIn the example

class Text
{
    public Text(): this(0, 0, null) {}

    public Text(int x, int y): this(x, y, null) {}

    public Text(int x, int y, string s) {
        // Actual constructor implementation
    }
}

los primeros constructores de instancia de dos simplemente proporcionan los valores predeterminados para los argumentos que faltan.the first two instance constructors merely provide the default values for the missing arguments. Ambos usan un this(...) inicializador de constructor para invocar el tercer constructor de instancia, lo que realmente se encarga de inicializar la nueva instancia.Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. El efecto es el de los parámetros del constructor opcional:The effect is that of optional constructor parameters:

Text t1 = new Text();                    // Same as Text(0, 0, null)
Text t2 = new Text(5, 10);               // Same as Text(5, 10, null)
Text t3 = new Text(5, 20, "Hello");

Constructores estáticosStatic constructors

Un constructor estático es un miembro que implementa las acciones necesarias para inicializar un tipo de clase cerrado.A static constructor is a member that implements the actions required to initialize a closed class type. Los constructores estáticos se declaran mediante static_constructor_declarations:Static constructors are declared using static_constructor_declarations:

static_constructor_declaration
    : attributes? static_constructor_modifiers identifier '(' ')' static_constructor_body
    ;

static_constructor_modifiers
    : 'extern'? 'static'
    | 'static' 'extern'?
    | static_constructor_modifiers_unsafe
    ;

static_constructor_body
    : block
    | ';'
    ;

Un static_constructor_declaration puede incluir un conjunto de atributos (atributos) y un extern modificador (métodos externos).A static_constructor_declaration may include a set of attributes (Attributes) and an extern modifier (External methods).

El identificador de un static_constructor_declaration debe nombrar la clase en el que se declara el constructor estático.The identifier of a static_constructor_declaration must name the class in which the static constructor is declared. Si se especifica ningún otro nombre, se produce un error en tiempo de compilación.If any other name is specified, a compile-time error occurs.

Cuando una declaración de constructor estático incluye un extern modificador, el constructor estático se dice que un constructor estático.When a static constructor declaration includes an extern modifier, the static constructor is said to be an external static constructor. Dado que una declaración de constructor estático no proporciona ninguna implementación real, su static_constructor_body consta de un punto y coma.Because an external static constructor declaration provides no actual implementation, its static_constructor_body consists of a semicolon. Para todas las declaraciones de constructor estático, el static_constructor_body consta de un bloque que especifica las instrucciones que se ejecutarán para inicializar la clase.For all other static constructor declarations, the static_constructor_body consists of a block which specifies the statements to execute in order to initialize the class. Esto corresponde exactamente a la cuerpoMétodo de un método estático con un void tipo de valor devuelto (cuerpo del método).This corresponds exactly to the method_body of a static method with a void return type (Method body).

Los constructores estáticos no se heredan y no se puede llamar directamente.Static constructors are not inherited, and cannot be called directly.

El constructor estático de un tipo de clase cerrado como máximo una vez se ejecuta en un dominio de aplicación determinado.The static constructor for a closed class type executes at most once in a given application domain. El primero de los siguientes eventos que se producen dentro de un dominio de aplicación, se desencadena la ejecución de un constructor estático:The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • Se crea una instancia del tipo de clase.An instance of the class type is created.
  • Se hace referencia a cualquiera de los miembros estáticos del tipo de clase.Any of the static members of the class type are referenced.

Si una clase contiene la Main método (inicio de la aplicación) en que comienza la ejecución, el constructor estático para esa clase se ejecuta antes que la Main se llama al método.If a class contains the Main method (Application Startup) in which execution begins, the static constructor for that class executes before the Main method is called.

Para inicializar un nuevo tipo de clase cerrados, en primer lugar un nuevo conjunto de campos estáticos (campos estáticos y de instancia) para ese tipo cerrado determinado se ha creado.To initialize a new closed class type, first a new set of static fields (Static and instance fields) for that particular closed type is created. Cada uno de los campos estáticos se inicializa en su valor predeterminado (los valores predeterminados).Each of the static fields is initialized to its default value (Default values). A continuación, los inicializadores de campo estático (inicialización de campos estáticos) se ejecutan para esos campos estáticos.Next, the static field initializers (Static field initialization) are executed for those static fields. Por último, se ejecuta el constructor estático.Finally, the static constructor is executed.

El ejemploThe example

using System;

class Test
{
    static void Main() {
        A.F();
        B.F();
    }
}

class A
{
    static A() {
        Console.WriteLine("Init A");
    }
    public static void F() {
        Console.WriteLine("A.F");
    }
}

class B
{
    static B() {
        Console.WriteLine("Init B");
    }
    public static void F() {
        Console.WriteLine("B.F");
    }
}

el resultado debe ser:must produce the output:

Init A
A.F
Init B
B.F

Dado que la ejecución de Adel constructor estático se desencadena por la llamada a A.Fcomo la ejecución de Bdel constructor estático se desencadena por la llamada a B.F.because the execution of A's static constructor is triggered by the call to A.F, and the execution of B's static constructor is triggered by the call to B.F.

Es posible construir dependencias circulares que permiten a los campos estáticos con inicializadores de variables que se deben observar en su estado de valor predeterminado.It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

El ejemploThe example

using System;

class A
{
    public static int X;

    static A() {
        X = B.Y + 1;
    }
}

class B
{
    public static int Y = A.X + 1;

    static B() {}

    static void Main() {
        Console.WriteLine("X = {0}, Y = {1}", A.X, B.Y);
    }
}

genera el resultadoproduces the output

X = 1, Y = 2

Para ejecutar el Main método, el sistema ejecuta por primera vez el inicializador para B.Y, antes de la clase Bdel constructor estático.To execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Ydel inicializador hace Adel constructor estático para que se puede ejecutar porque el valor de A.X se hace referencia.Y's initializer causes A's static constructor to be run because the value of A.X is referenced. El constructor estático de A a su vez va a calcular el valor de Xy hacer búsquedas por lo que el valor predeterminado de Y, que es cero.The static constructor of A in turn proceeds to compute the value of X, and in doing so fetches the default value of Y, which is zero. A.X por lo tanto se inicializa a 1.A.X is thus initialized to 1. El proceso de ejecución Ainicializadores de campo estático de constructor estático y, a continuación, finaliza, volviendo al cálculo del valor inicial de Y, el resultado se convierte en 2.The process of running A's static field initializers and static constructor then completes, returning to the calculation of the initial value of Y, the result of which becomes 2.

Dado que el constructor estático se ejecuta exactamente una vez para cada tipo de clase construida de cerrado, es un lugar conveniente para exigir comprobaciones de tiempo de ejecución en el parámetro de tipo que no se puede comprobar en tiempo de compilación a través de restricciones (parámetro de tipo restricciones).Because the static constructor is executed exactly once for each closed constructed class type, it is a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile-time via constraints (Type parameter constraints). Por ejemplo, el siguiente tipo utiliza un constructor estático para exigir que el argumento de tipo es una enumeración:For example, the following type uses a static constructor to enforce that the type argument is an enum:

class Gen<T> where T: struct
{
    static Gen() {
        if (!typeof(T).IsEnum) {
            throw new ArgumentException("T must be an enum");
        }
    }
}

DestructoresDestructors

Un destructor es un miembro que implementa las acciones necesarias para destruir una instancia de una clase.A destructor is a member that implements the actions required to destruct an instance of a class. Se declara un destructor con un destructor_declaration:A destructor is declared using a destructor_declaration:

destructor_declaration
    : attributes? 'extern'? '~' identifier '(' ')' destructor_body
    | destructor_declaration_unsafe
    ;

destructor_body
    : block
    | ';'
    ;

Un destructor_declaration puede incluir un conjunto de atributos (atributos).A destructor_declaration may include a set of attributes (Attributes).

El identificador de un destructor_declaration debe nombrar la clase en el que se declara el destructor.The identifier of a destructor_declaration must name the class in which the destructor is declared. Si se especifica ningún otro nombre, se produce un error en tiempo de compilación.If any other name is specified, a compile-time error occurs.

Cuando una declaración de destructor incluye un extern modificador, el destructor se dice que un destructor externo.When a destructor declaration includes an extern modifier, the destructor is said to be an external destructor. Dado que una declaración de destructor externo no proporciona ninguna implementación real, su destructor_body consta de un punto y coma.Because an external destructor declaration provides no actual implementation, its destructor_body consists of a semicolon. Para el resto de los destructores, la destructor_body consta de un bloque que especifica las instrucciones que se ejecutarán para destruir una instancia de la clase.For all other destructors, the destructor_body consists of a block which specifies the statements to execute in order to destruct an instance of the class. Un destructor_body corresponde exactamente a la cuerpoMétodo de un método de instancia con un void tipo de valor devuelto (cuerpo del método).A destructor_body corresponds exactly to the method_body of an instance method with a void return type (Method body).

No se heredan los destructores.Destructors are not inherited. Por lo tanto, una clase tiene el destructor que se puede declarar en esa clase.Thus, a class has no destructors other than the one which may be declared in that class.

Puesto que un destructor debe no tener ningún parámetro, no puede sobrecargarse, por lo que puede tener una clase, como máximo, un destructor.Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.

Los destructores se invocan automáticamente y no se puede invocar explícitamente.Destructors are invoked automatically, and cannot be invoked explicitly. Una instancia se vuelve apta para su destrucción cuando ya no es posible que cualquier código para que use esa instancia.An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. La ejecución del destructor para la instancia puede producirse en cualquier momento después de la instancia se vuelve apta para su destrucción.Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. Cuando se destruye una instancia, se llaman a los destructores de cadena de herencia de la instancia en orden, de la más derivada a la menos derivada.When an instance is destructed, the destructors in that instance's inheritance chain are called, in order, from most derived to least derived. Un destructor se puede ejecutar en cualquier subproceso.A destructor may be executed on any thread. Para obtener más información sobre las reglas que rigen cuándo y cómo se ejecuta un destructor, vea administración de memoria automática.For further discussion of the rules that govern when and how a destructor is executed, see Automatic memory management.

La salida del ejemploThe output of the example

using System;

class A
{
    ~A() {
        Console.WriteLine("A's destructor");
    }
}

class B: A
{
    ~B() {
        Console.WriteLine("B's destructor");
    }
}

class Test
{
   static void Main() {
        B b = new B();
        b = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
   }
}

isis

B's destructor
A's destructor

Puesto que los destructores de una cadena de herencia se llaman en orden, de la más derivada a la menos derivada.since destructors in an inheritance chain are called in order, from most derived to least derived.

Los destructores se implementan mediante la invalidación del método virtual Finalize en System.Object.Destructors are implemented by overriding the virtual method Finalize on System.Object. Programas de C# no pueden invalidar este método o llame al (o invalidaciones del mismo) directamente.C# programs are not permitted to override this method or call it (or overrides of it) directly. Por ejemplo, el programaFor instance, the program

class A 
{
    override protected void Finalize() {}    // error

    public void F() {
        this.Finalize();                     // error
    }
}

contiene dos errores.contains two errors.

El compilador se comporta como si este método y reemplazos, no existen en absoluto.The compiler behaves as if this method, and overrides of it, do not exist at all. Por lo tanto, este programa:Thus, this program:

class A 
{
    void Finalize() {}                            // permitted
}

es válido, y muestra el método oculta System.Objectdel Finalize método.is valid, and the method shown hides System.Object's Finalize method.

Para obtener una explicación del comportamiento cuando se produce una excepción desde un destructor, vea cómo se controlan las excepciones.For a discussion of the behavior when an exception is thrown from a destructor, see How exceptions are handled.

IteratorsIterators

Un miembro de función (miembros de función) implementa mediante un bloque de iteradores (bloques) se denomina un iterador.A function member (Function members) implemented using an iterator block (Blocks) is called an iterator.

Un bloque de iteradores puede utilizarse como el cuerpo de un miembro de función como el tipo de valor devuelto del miembro de función correspondiente es una de las interfaces de enumerador (interfaces de enumerador) o una de las interfaces enumerables (Interfaces enumerables).An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces (Enumerator interfaces) or one of the enumerable interfaces (Enumerable interfaces). Se puede producir como un cuerpoMétodo, operator_body o accessor_body, mientras que los eventos, constructores de instancias, los constructores estáticos y destructores no pueden ser se implementa como iteradores.It can occur as a method_body, operator_body or accessor_body, whereas events, instance constructors, static constructors and destructors cannot be implemented as iterators.

Cuando un miembro de función se implementa mediante un bloque de iteradores, es un error en tiempo de compilación para la lista de parámetros formales del miembro de función para especificar cualquier ref o out parámetros.When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any ref or out parameters.

Interfaces de enumeradorEnumerator interfaces

El interfaces de enumerador son la interfaz no genérica System.Collections.IEnumerator y todas las instancias de la interfaz genérica System.Collections.Generic.IEnumerator<T>.The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. Para mayor brevedad, en este capítulo estas interfaces se denominarán IEnumerator y IEnumerator<T>, respectivamente.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively.

Interfaces enumerablesEnumerable interfaces

El interfaces enumerables son la interfaz no genérica System.Collections.IEnumerable y todas las instancias de la interfaz genérica System.Collections.Generic.IEnumerable<T>.The enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. Para mayor brevedad, en este capítulo estas interfaces se denominarán IEnumerable y IEnumerable<T>, respectivamente.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively.

Tipo yieldYield type

Un iterador genera una secuencia de valores, todos del mismo tipo.An iterator produces a sequence of values, all of the same type. Este tipo se denomina el tipo yield del iterador.This type is called the yield type of the iterator.

  • El tipo de rendimiento de un iterador que devuelve IEnumerator o IEnumerable es object.The yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • El tipo de rendimiento de un iterador que devuelve IEnumerator<T> o IEnumerable<T> es T.The yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T.

Objetos del enumeradorEnumerator objects

Cuando un miembro de función devuelve un enumerador de tipo de interfaz se implementa mediante un bloque de iteradores, invocar al miembro de función no se ejecuta inmediatamente el código del bloque de iteradores.When a function member returning an enumerator interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. En su lugar, un objeto enumerador se crea y devuelve.Instead, an enumerator object is created and returned. Este objeto encapsula el código especificado en el bloque de iteradores y la ejecución del código del bloque de iteradores se produce cuando el objeto de enumerador MoveNext se invoca el método.This object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object's MoveNext method is invoked. Un objeto de enumerador tiene las siguientes características:An enumerator object has the following characteristics:

  • Implementa IEnumerator y IEnumerator<T>, donde T es el tipo yield del iterador.It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator.
  • Implementa System.IDisposable.It implements System.IDisposable.
  • Se inicializa con una copia de los valores de argumento (si existe) y pasa el valor de instancia para el miembro de función.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
  • Tiene cuatro estados posibles, antes, ejecutando, suspendido, y despuésy está inicialmente en el antes estado.It has four potential states, before, running, suspended, and after, and is initially in the before state.

Un objeto de enumerador es normalmente una instancia de una clase de enumerador generado por el compilador que encapsula el código del bloque de iteradores e implementa las interfaces de enumerador, pero son posibles otros métodos de implementación.An enumerator object is typically an instance of a compiler-generated enumerator class that encapsulates the code in the iterator block and implements the enumerator interfaces, but other methods of implementation are possible. Si el compilador genera una clase de enumerador, esa clase se anidarán, directa o indirectamente, en la clase que contiene el miembro de función, tendrá accesibilidad privada, y tendrá un nombre reservado para uso del compilador (identificadores ).If an enumerator class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (Identifiers).

Un objeto de enumerador puede implementar más interfaces que las especificadas anteriormente.An enumerator object may implement more interfaces than those specified above.

Las secciones siguientes describen el comportamiento exacto de la MoveNext, Current, y Dispose los miembros de la IEnumerable y IEnumerable<T> proporcionadas por un objeto de enumerador de implementaciones de interfaz.The following sections describe the exact behavior of the MoveNext, Current, and Dispose members of the IEnumerable and IEnumerable<T> interface implementations provided by an enumerator object.

Tenga en cuenta que los objetos del enumerador no admiten la IEnumerator.Reset método.Note that enumerator objects do not support the IEnumerator.Reset method. Invocar este método hace que un System.NotSupportedException que se produzca.Invoking this method causes a System.NotSupportedException to be thrown.

El método MoveNextThe MoveNext method

El MoveNext método de un objeto de enumerador encapsula el código de un bloque de iteradores.The MoveNext method of an enumerator object encapsulates the code of an iterator block. Invocar el MoveNext método ejecuta código en el bloque de iteradores y establece el Current propiedad del objeto enumerador según corresponda.Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. La acción exacta realizada por MoveNext depende del estado del objeto del enumerador cuando MoveNext se invoca:The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • Si el estado del objeto del enumerador es antes, al invocar MoveNext:If the state of the enumerator object is before, invoking MoveNext:
    • Cambia el estado a ejecutando.Changes the state to running.
    • Inicializa los parámetros (incluido this) del bloque de iteradores para los valores de argumento y el valor de instancia guarda cuando se inicializó el objeto de enumerador.Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized.
    • Ejecuta el bloque de iteradores desde el principio hasta que se interrumpe la ejecución (como se describe a continuación).Executes the iterator block from the beginning until execution is interrupted (as described below).
  • Si el estado del objeto del enumerador es ejecutando, el resultado de invocar MoveNext no se ha especificado.If the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • Si el estado del objeto del enumerador es suspendido, al invocar MoveNext:If the state of the enumerator object is suspended, invoking MoveNext:
    • Cambia el estado a ejecutando.Changes the state to running.
    • Restaura los valores de todas las variables locales y parámetros (incluido this) a los valores guardados cuando por última vez se suspende la ejecución del bloque de iteradores.Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. Tenga en cuenta que el contenido de todos los objetos que hacen referencia estas variables puede haber cambiado desde la anterior llamada a MoveNext.Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext.
    • Reanuda la ejecución del bloque de iteradores inmediatamente después de la yield return instrucción que causó la suspensión de la ejecución y continúa hasta que se interrumpe la ejecución (como se describe a continuación).Resumes execution of the iterator block immediately following the yield return statement that caused the suspension of execution and continues until execution is interrupted (as described below).
  • Si el estado del objeto del enumerador es después, al invocar MoveNext devuelve false.If the state of the enumerator object is after, invoking MoveNext returns false.

Cuando MoveNext ejecuta el bloque de iteradores, puede interrumpir la ejecución de cuatro maneras: Por un yield return instrucción, por un yield break instrucción cuando se encuentra al final del bloque de iteradores y debido a una excepción que se produce y se propaga fuera del bloque de iteradores.When MoveNext executes the iterator block, execution can be interrupted in four ways: By a yield return statement, by a yield break statement, by encountering the end of the iterator block, and by an exception being thrown and propagated out of the iterator block.

  • Cuando un yield return se encuentra la instrucción (la instrucción yield):When a yield return statement is encountered (The yield statement):
    • La expresión proporcionada en la instrucción se evalúa implícitamente convierte al tipo yield y asignada a la Current propiedad del objeto enumerador.The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
    • Se suspende la ejecución del cuerpo del iterador.Execution of the iterator body is suspended. Los valores de todas las variables locales y parámetros (incluidos this) se guardan, tal como es la ubicación de este yield return instrucción.The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. Si el yield return instrucción está dentro de uno o varios try bloquea asociado finally bloques no se ejecutan en este momento.If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
    • Se cambia el estado del objeto del enumerador a suspendido.The state of the enumerator object is changed to suspended.
    • El MoveNext devuelve del método true a su llamador, que indica que la iteración avanzó correctamente al siguiente valor.The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • Cuando un yield break se encuentra la instrucción (la instrucción yield):When a yield break statement is encountered (The yield statement):
    • Si el yield break instrucción está dentro de uno o varios try bloquea asociado finally bloques se ejecutan.If the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • Se cambia el estado del objeto del enumerador a después.The state of the enumerator object is changed to after.
    • El MoveNext devuelve del método false a su llamador, que indica que la iteración está completa.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Cuando se encuentra el final del cuerpo del iterador:When the end of the iterator body is encountered:
    • Se cambia el estado del objeto del enumerador a después.The state of the enumerator object is changed to after.
    • El MoveNext devuelve del método false a su llamador, que indica que la iteración está completa.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Cuando se produce una excepción y se propaga fuera del bloque de iteradores:When an exception is thrown and propagated out of the iterator block:
    • Adecuado finally bloques en el cuerpo del iterador habrá ejecutados por la propagación de excepciones.Appropriate finally blocks in the iterator body will have been executed by the exception propagation.
    • Se cambia el estado del objeto del enumerador a después.The state of the enumerator object is changed to after.
    • Continúa la propagación de excepciones al llamador de la MoveNext método.The exception propagation continues to the caller of the MoveNext method.

La propiedad actualThe Current property

Un objeto de enumerador Current propiedad se ve afectada por yield return las instrucciones del bloque de iteradores.An enumerator object's Current property is affected by yield return statements in the iterator block.

Cuando un objeto de enumerador está en el suspendido de estado, el valor de Current es el valor establecido por la llamada anterior a MoveNext.When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. Cuando un objeto de enumerador está en el antes, ejecutando, o después indica, el resultado del acceso a Current no se ha especificado.When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified.

Para un iterador con un rendimiento escriba distinto object, el resultado del acceso a Current a través del objeto de enumerador IEnumerable implementación corresponde al acceso a Current a través del objeto de enumerador IEnumerator<T> implementación y convirtiendo el resultado a object.For an iterator with a yield type other than object, the result of accessing Current through the enumerator object's IEnumerable implementation corresponds to accessing Current through the enumerator object's IEnumerator<T> implementation and casting the result to object.

El método DisposeThe Dispose method

El Dispose método se utiliza para limpiar la iteración al traer el objeto de enumerador a la después estado.The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • Si el estado del objeto del enumerador es antes, al invocar Dispose cambia el estado a después.If the state of the enumerator object is before, invoking Dispose changes the state to after.
  • Si el estado del objeto del enumerador es ejecutando, el resultado de invocar Dispose no se ha especificado.If the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • Si el estado del objeto del enumerador es suspendido, al invocar Dispose:If the state of the enumerator object is suspended, invoking Dispose:
    • Cambia el estado a ejecutando.Changes the state to running.
    • Ejecute cualquiera, por último, bloques como si ejecuta la última yield return instrucción fuera un yield break instrucción.Executes any finally blocks as if the last executed yield return statement were a yield break statement. Si esto produce una excepción se produce y se propaga fuera del cuerpo del iterador, el estado del objeto de enumerador se establece en después y la excepción se propaga al llamador de la Dispose método.If this causes an exception to be thrown and propagated out of the iterator body, the state of the enumerator object is set to after and the exception is propagated to the caller of the Dispose method.
    • Cambia el estado a después.Changes the state to after.
  • Si el estado del objeto del enumerador es después, al invocar Dispose no tiene ningún efecto.If the state of the enumerator object is after, invoking Dispose has no affect.

Objetos enumerablesEnumerable objects

Cuando un miembro de función devuelve un tipo de interfaz enumerable se implementa mediante un bloque de iteradores, invocar al miembro de función no se ejecuta inmediatamente el código del bloque de iteradores.When a function member returning an enumerable interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. En su lugar, un objeto enumerable se crea y devuelve.Instead, an enumerable object is created and returned. El objeto enumerable GetEnumerator método devuelve un objeto de enumerador que encapsula el código especificado en el bloque de iteradores y la ejecución del código del bloque de iteradores se produce cuando el objeto de enumerador MoveNext se invoca el método.The enumerable object's GetEnumerator method returns an enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object's MoveNext method is invoked. Un objeto enumerable tiene las siguientes características:An enumerable object has the following characteristics:

  • Implementa IEnumerable y IEnumerable<T>, donde T es el tipo yield del iterador.It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator.
  • Se inicializa con una copia de los valores de argumento (si existe) y pasa el valor de instancia para el miembro de función.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.

Un objeto enumerable normalmente es una instancia de una clase enumerable generado por el compilador que encapsula el código del bloque de iteradores e implementa las interfaces enumerables, pero son posibles otros métodos de implementación.An enumerable object is typically an instance of a compiler-generated enumerable class that encapsulates the code in the iterator block and implements the enumerable interfaces, but other methods of implementation are possible. Si el compilador genera una clase enumerable, esa clase se anidarán, directa o indirectamente, en la clase que contiene el miembro de función, tendrá accesibilidad privada, y tendrá un nombre reservado para uso del compilador (identificadores ).If an enumerable class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (Identifiers).

Un objeto enumerable puede implementar más interfaces que las especificadas anteriormente.An enumerable object may implement more interfaces than those specified above. En concreto, también puede implementar un objeto enumerable IEnumerator y IEnumerator<T>, habilitarla para que actúe como un objeto enumerable y un enumerador.In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. En ese tipo de implementación, la primera vez que un objeto enumerable GetEnumerator se invoca al método, se devuelve el mismo objeto enumerable.In that type of implementation, the first time an enumerable object's GetEnumerator method is invoked, the enumerable object itself is returned. Las siguientes invocaciones del objeto enumerable GetEnumerator, si existe, devuelve una copia del objeto enumerable.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Por lo tanto, cada uno devuelve enumerador tiene su propio estado y los cambios de un enumerador no afectarán a otro.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another.

El método GetEnumeratorThe GetEnumerator method

Un objeto enumerable proporciona una implementación de la GetEnumerator métodos de la IEnumerable y IEnumerable<T> interfaces.An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. Los dos GetEnumerator métodos comparten una implementación común que adquiere y devuelve un objeto de enumerador disponible.The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. El objeto de enumerador se inicializa con los valores de argumento y la instancia valor guardado cuando el objeto enumerable que se ha inicializado, pero en caso contrario, las funciones del objeto de enumerador tal como se describe en objetos enumerador.The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in Enumerator objects.

Ejemplo de implementaciónImplementation example

Esta sección describe una posible implementación de iteradores en cuanto a las construcciones C# estándar.This section describes a possible implementation of iterators in terms of standard C# constructs. La implementación que se describen aquí se basa en los mismos principios utilizados por el compilador de C# de Microsoft, pero en ningún caso es una implementación obligatoria o el único posible.The implementation described here is based on the same principles used by the Microsoft C# compiler, but it is by no means a mandated implementation or the only one possible.

La siguiente Stack<T> la clase implementa su GetEnumerator método mediante un iterador.The following Stack<T> class implements its GetEnumerator method using an iterator. El iterador enumera los elementos de la pila en orden descendente.The iterator enumerates the elements of the stack in top to bottom order.

using System;
using System.Collections;
using System.Collections.Generic;

class Stack<T>: IEnumerable<T>
{
    T[] items;
    int count;

    public void Push(T item) {
        if (items == null) {
            items = new T[4];
        }
        else if (items.Length == count) {
            T[] newItems = new T[count * 2];
            Array.Copy(items, 0, newItems, 0, count);
            items = newItems;
        }
        items[count++] = item;
    }

    public T Pop() {
        T result = items[--count];
        items[count] = default(T);
        return result;
    }

    public IEnumerator<T> GetEnumerator() {
        for (int i = count - 1; i >= 0; --i) yield return items[i];
    }
}

El GetEnumerator método se puede traducir en una instancia de una clase de enumerador generado por el compilador que encapsula el código en el bloque de iteradores, tal como se muestra en la siguiente.The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following.

class Stack<T>: IEnumerable<T>
{
    ...

    public IEnumerator<T> GetEnumerator() {
        return new __Enumerator1(this);
    }

    class __Enumerator1: IEnumerator<T>, IEnumerator
    {
        int __state;
        T __current;
        Stack<T> __this;
        int i;

        public __Enumerator1(Stack<T> __this) {
            this.__this = __this;
        }

        public T Current {
            get { return __current; }
        }

        object IEnumerator.Current {
            get { return __current; }
        }

        public bool MoveNext() {
            switch (__state) {
                case 1: goto __state1;
                case 2: goto __state2;
            }
            i = __this.count - 1;
        __loop:
            if (i < 0) goto __state2;
            __current = __this.items[i];
            __state = 1;
            return true;
        __state1:
            --i;
            goto __loop;
        __state2:
            __state = 2;
            return false;
        }

        public void Dispose() {
            __state = 2;
        }

        void IEnumerator.Reset() {
            throw new NotSupportedException();
        }
    }
}

En la traducción anterior, se convierte en un equipo de estado y se coloca en el código del bloque de iteradores el MoveNext método de la clase de enumerador.In the preceding translation, the code in the iterator block is turned into a state machine and placed in the MoveNext method of the enumerator class. Además, la variable local i se convierte en un campo en el objeto de enumerador para que pueda seguir existen entre las distintas invocaciones de MoveNext.Furthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext.

El ejemplo siguiente imprime una sencilla tabla de multiplicación de enteros del 1 al 10.The following example prints a simple multiplication table of the integers 1 through 10. El FromTo método en el ejemplo devuelve un objeto enumerable y se implementa mediante un iterador.The FromTo method in the example returns an enumerable object and is implemented using an iterator.

using System;
using System.Collections.Generic;

class Test
{
    static IEnumerable<int> FromTo(int from, int to) {
        while (from <= to) yield return from++;
    }

    static void Main() {
        IEnumerable<int> e = FromTo(1, 10);
        foreach (int x in e) {
            foreach (int y in e) {
                Console.Write("{0,3} ", x * y);
            }
            Console.WriteLine();
        }
    }
}

El FromTo método se puede traducir en una instancia de una clase enumerable generado por el compilador que encapsula el código del bloque de iteradores, tal como se muestra en la siguiente.The FromTo method can be translated into an instantiation of a compiler-generated enumerable class that encapsulates the code in the iterator block, as shown in the following.

using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;

class Test
{
    ...

    static IEnumerable<int> FromTo(int from, int to) {
        return new __Enumerable1(from, to);
    }

    class __Enumerable1:
        IEnumerable<int>, IEnumerable,
        IEnumerator<int>, IEnumerator
    {
        int __state;
        int __current;
        int __from;
        int from;
        int to;
        int i;

        public __Enumerable1(int __from, int to) {
            this.__from = __from;
            this.to = to;
        }

        public IEnumerator<int> GetEnumerator() {
            __Enumerable1 result = this;
            if (Interlocked.CompareExchange(ref __state, 1, 0) != 0) {
                result = new __Enumerable1(__from, to);
                result.__state = 1;
            }
            result.from = result.__from;
            return result;
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return (IEnumerator)GetEnumerator();
        }

        public int Current {
            get { return __current; }
        }

        object IEnumerator.Current {
            get { return __current; }
        }

        public bool MoveNext() {
            switch (__state) {
            case 1:
                if (from > to) goto case 2;
                __current = from++;
                __state = 1;
                return true;
            case 2:
                __state = 2;
                return false;
            default:
                throw new InvalidOperationException();
            }
        }

        public void Dispose() {
            __state = 2;
        }

        void IEnumerator.Reset() {
            throw new NotSupportedException();
        }
    }
}

La clase enumerable implementa las interfaces enumerables y las interfaces de enumerador, habilitarla para que actúe como un objeto enumerable y un enumerador.The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. La primera vez el GetEnumerator se invoca al método, se devuelve el mismo objeto enumerable.The first time the GetEnumerator method is invoked, the enumerable object itself is returned. Las siguientes invocaciones del objeto enumerable GetEnumerator, si existe, devuelve una copia del objeto enumerable.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Por lo tanto, cada uno devuelve enumerador tiene su propio estado y los cambios de un enumerador no afectarán a otro.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. El Interlocked.CompareExchange método se utiliza para garantizar un funcionamiento seguro para subprocesos.The Interlocked.CompareExchange method is used to ensure thread-safe operation.

El from y to parámetros se convierten en campos de la clase enumerable.The from and to parameters are turned into fields in the enumerable class. Dado que from se modifica en el bloque de iteradores adicional __from campo se introdujo para contener el valor inicial dado a from en cada enumerador.Because from is modified in the iterator block, an additional __from field is introduced to hold the initial value given to from in each enumerator.

El MoveNext método produce una InvalidOperationException si se llama cuando __state es 0.The MoveNext method throws an InvalidOperationException if it is called when __state is 0. Esto protege contra el uso del objeto enumerable como un objeto de enumerador sin llamar primero a GetEnumerator.This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

El ejemplo siguiente muestra una clase simple de árbol.The following example shows a simple tree class. El Tree<T> la clase implementa su GetEnumerator método mediante un iterador.The Tree<T> class implements its GetEnumerator method using an iterator. El iterador enumera los elementos del árbol en orden infijo.The iterator enumerates the elements of the tree in infix order.

using System;
using System.Collections.Generic;

class Tree<T>: IEnumerable<T>
{
    T value;
    Tree<T> left;
    Tree<T> right;

    public Tree(T value, Tree<T> left, Tree<T> right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }

    public IEnumerator<T> GetEnumerator() {
        if (left != null) foreach (T x in left) yield x;
        yield value;
        if (right != null) foreach (T x in right) yield x;
    }
}

class Program
{
    static Tree<T> MakeTree<T>(T[] items, int left, int right) {
        if (left > right) return null;
        int i = (left + right) / 2;
        return new Tree<T>(items[i], 
            MakeTree(items, left, i - 1),
            MakeTree(items, i + 1, right));
    }

    static Tree<T> MakeTree<T>(params T[] items) {
        return MakeTree(items, 0, items.Length - 1);
    }

    // The output of the program is:
    // 1 2 3 4 5 6 7 8 9
    // Mon Tue Wed Thu Fri Sat Sun

    static void Main() {
        Tree<int> ints = MakeTree(1, 2, 3, 4, 5, 6, 7, 8, 9);
        foreach (int i in ints) Console.Write("{0} ", i);
        Console.WriteLine();

        Tree<string> strings = MakeTree(
            "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun");
        foreach (string s in strings) Console.Write("{0} ", s);
        Console.WriteLine();
    }
}

El GetEnumerator método se puede traducir en una instancia de una clase de enumerador generado por el compilador que encapsula el código en el bloque de iteradores, tal como se muestra en la siguiente.The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following.

class Tree<T>: IEnumerable<T>
{
    ...

    public IEnumerator<T> GetEnumerator() {
        return new __Enumerator1(this);
    }

    class __Enumerator1 : IEnumerator<T>, IEnumerator
    {
        Node<T> __this;
        IEnumerator<T> __left, __right;
        int __state;
        T __current;

        public __Enumerator1(Node<T> __this) {
            this.__this = __this;
        }

        public T Current {
            get { return __current; }
        }

        object IEnumerator.Current {
            get { return __current; }
        }

        public bool MoveNext() {
            try {
                switch (__state) {

                case 0:
                    __state = -1;
                    if (__this.left == null) goto __yield_value;
                    __left = __this.left.GetEnumerator();
                    goto case 1;

                case 1:
                    __state = -2;
                    if (!__left.MoveNext()) goto __left_dispose;
                    __current = __left.Current;
                    __state = 1;
                    return true;

                __left_dispose:
                    __state = -1;
                    __left.Dispose();

                __yield_value:
                    __current = __this.value;
                    __state = 2;
                    return true;

                case 2:
                    __state = -1;
                    if (__this.right == null) goto __end;
                    __right = __this.right.GetEnumerator();
                    goto case 3;

                case 3:
                    __state = -3;
                    if (!__right.MoveNext()) goto __right_dispose;
                    __current = __right.Current;
                    __state = 3;
                    return true;

                __right_dispose:
                    __state = -1;
                    __right.Dispose();

                __end:
                    __state = 4;
                    break;

                }
            }
            finally {
                if (__state < 0) Dispose();
            }
            return false;
        }

        public void Dispose() {
            try {
                switch (__state) {

                case 1:
                case -2:
                    __left.Dispose();
                    break;

                case 3:
                case -3:
                    __right.Dispose();
                    break;

                }
            }
            finally {
                __state = 4;
            }
        }

        void IEnumerator.Reset() {
            throw new NotSupportedException();
        }
    }
}

Los objetos temporales generados por el compilador usados en el foreach se levantan instrucciones en el __left y __right campos del objeto enumerador.The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. El __state cuidadosamente se actualiza el campo del objeto enumerador para que el valor correcto Dispose() se llamará al método correctamente si se produce una excepción.The __state field of the enumerator object is carefully updated so that the correct Dispose() method will be called correctly if an exception is thrown. Tenga en cuenta que no es posible escribir el código traducido con simple foreach instrucciones.Note that it is not possible to write the translated code with simple foreach statements.

Funciones asincrónicasAsync functions

Un método (métodos) o una función anónima (expresiones de función anónima) con el async modificador se denomina un función asincrónica.A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an async function. En general, el término async se usa para describir cualquier tipo de función que tiene el async modificador.In general, the term async is used to describe any kind of function that has the async modifier.

Es un error en tiempo de compilación para la lista de parámetros formales de una función asincrónica para especificar cualquier ref o out parámetros.It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters.

El return_type Async método debe ser void o un tipo de tarea.The return_type of an async method must be either void or a task type. Los tipos de tarea son System.Threading.Tasks.Task y tipos construyan desde System.Threading.Tasks.Task<T>.The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. Por brevedad, en este capítulo estos tipos se denominan Task y Task<T>, respectivamente.For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. Un método asincrónico devuelve un tipo de tarea se dice que devuelven tareas.An async method returning a task type is said to be task-returning.

La definición exacta de los tipos de tarea es implementación definida, pero desde la perspectiva del lenguaje, un tipo de tarea está en uno de los Estados incompletos, se ha realizado correctamente o con errores.The exact definition of the task types is implementation defined, but from the language's point of view a task type is in one of the states incomplete, succeeded or faulted. Una tarea con error registra una excepción pertinente.A faulted task records a pertinent exception. Una correcta Task<T> registra un resultado de tipo T.A succeeded Task<T> records a result of type T. Tipos de tareas son esperables y puede ser, por tanto, los operandos de expresiones await (expresiones Await).Task types are awaitable, and can therefore be the operands of await expressions (Await expressions).

Una invocación de función async tiene la capacidad para suspender la evaluación por medio de expresiones await (expresiones Await) en su cuerpo.An async function invocation has the ability to suspend evaluation by means of await expressions (Await expressions) in its body. Más adelante se puede reanudar la evaluación en el punto de suspensión de la expresión por medio de await un delegado de reanudación.Evaluation may later be resumed at the point of the suspending await expression by means of a resumption delegate. El delegado de reanudación es de tipo System.Action, y cuando se invoca, evaluación de la invocación de función async se reanudará desde la expresión await, donde se quedó.The resumption delegate is of type System.Action, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. El llamador actual de una función asincrónica invocación es el llamador original si nunca se ha suspendido la invocación de función o el llamador del delegado de reanudación más reciente, en caso contrario.The current caller of an async function invocation is the original caller if the function invocation has never been suspended, or the most recent caller of the resumption delegate otherwise.

Evaluación de una función asincrónica de devolución de tareaEvaluation of a task-returning async function

Invocación de una función asincrónica de devolución de tarea hace que una instancia del tipo de tarea devuelta se genere.Invocation of a task-returning async function causes an instance of the returned task type to be generated. Esto se denomina la task devuelto de la función async.This is called the return task of the async function. La tarea está inicialmente en un estado incompleto.The task is initially in an incomplete state.

El cuerpo de la función async, a continuación, se evalúa hasta que se suspende (que se alcanza una expresión await) o se finaliza, en el que se devuelve punto de control al llamador, junto con la tarea devuelto.The async function body is then evaluated until it is either suspended (by reaching an await expression) or terminates, at which point control is returned to the caller, along with the return task.

Cuando el cuerpo de la función asincrónica termina, la tarea devuelta se mueve fuera del estado incompleto:When the body of the async function terminates, the return task is moved out of the incomplete state:

  • Si el cuerpo de la función termina como resultado de alcanzar una instrucción return o al final del cuerpo, cualquier valor de resultado se registra en la tarea devuelta, que se coloca en un estado correcto.If the function body terminates as the result of reaching a return statement or the end of the body, any result value is recorded in the return task, which is put into a succeeded state.
  • Si el cuerpo de la función termina como resultado de una excepción no detectada (la instrucción throw) se grabó la excepción en la tarea devuelta que se coloca en un estado de error.If the function body terminates as the result of an uncaught exception (The throw statement) the exception is recorded in the return task which is put into a faulted state.

Evaluación de una función asincrónica devuelve voidEvaluation of a void-returning async function

Si el tipo de valor devuelto de la función async es void, evaluación difiere de los pasos anteriores en la siguiente manera: Dado que no se devuelve ninguna tarea, la función comunica en su lugar excepciones en el subproceso actual y finalización contexto de sincronización.If the return type of the async function is void, evaluation differs from the above in the following way: Because no task is returned, the function instead communicates completion and exceptions to the current thread's synchronization context. La definición exacta del contexto de sincronización depende de la implementación, pero es una representación de "donde" se está ejecutando el subproceso actual.The exact definition of synchronization context is implementation-dependent, but is a representation of "where" the current thread is running. El contexto de sincronización se notifica cuando comienza la evaluación de una función asincrónica devuelve void, se completa correctamente o provoca que se produzca una excepción no detectada.The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.

Esto permite que el contexto para realizar un seguimiento de cuántas funciones async devuelven void se ejecutan en él y decidir cómo propagar las excepciones que salen de ellas.This allows the context to keep track of how many void-returning async functions are running under it, and to decide how to propagate exceptions coming out of them.