ClassesClasses

Uma classe é uma estrutura de dados que pode conter membros de dados (constantes e campos), membros de função (métodos, propriedades, eventos, indexadores, operadores, construtores de instância, destruidores e construtores estáticos) e tipos aninhados.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. Os tipos de classe dão suporte à herança, um mecanismo no qual uma classe derivada pode estender e especializar uma classe base.Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

Declarações de classeClass declarations

Um class_declaration é um type_declaration (declarações de tipo) que declara uma nova classe.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 ';'?
    ;

Um class_declaration consiste em um conjunto opcional de atributos (atributos), seguido por um conjunto opcional de class_modifiers (modificadores de classe), seguido por um modificador opcional partial, seguido pela palavra-chave class e um identificador que nomeia a classe, seguido por um type_parameter_list opcional (parâmetros de tipo), seguido por uma especificação de Class_base opcional (especificação debase de classe), seguida por um conjunto opcional de type_parameter_constraints_clauses (restrições de parâmetro de tipo), seguido por um class_body (corpo de classe), opcionalmente seguido por um ponto-e-vírgula.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.

Uma declaração de classe não pode fornecer type_parameter_constraints_clauses, a menos que ele também forneça um type_parameter_list.A class declaration cannot supply type_parameter_constraints_clauses unless it also supplies a type_parameter_list.

Uma declaração de classe que fornece um type_parameter_list é uma declaração de classe genérica.A class declaration that supplies a type_parameter_list is a generic class declaration. Além disso, qualquer classe aninhada dentro de uma declaração de classe genérica ou uma declaração struct genérica é, em si, uma declaração de classe genérica, já que parâmetros de tipo para o tipo recipiente devem ser fornecidos para criar um tipo construído.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 classeClass modifiers

Um class_declaration pode, opcionalmente, incluir uma sequência de modificadores de classe:A class_declaration may optionally include a sequence of class modifiers:

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

É um erro de tempo de compilação para o mesmo modificador aparecer várias vezes em uma declaração de classe.It is a compile-time error for the same modifier to appear multiple times in a class declaration.

O new modificador é permitido em classes aninhadas.The new modifier is permitted on nested classes. Ele especifica que a classe oculta um membro herdado com o mesmo nome, conforme descrito no novo modificador.It specifies that the class hides an inherited member by the same name, as described in The new modifier. É um erro de tempo de compilação para que new o modificador apareça em uma declaração de classe que não seja uma declaração de classe aninhada.It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration.

Os publicmodificadores internal, protected, eprivate controlam a acessibilidade da classe.The public, protected, internal, and private modifiers control the accessibility of the class. Dependendo do contexto no qual a declaração de classe ocorre, alguns desses modificadores podem não ser permitidos (acessibilidade declarada).Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

Os abstractmodificadores estaticsão discutidos nas seções a seguir. sealedThe abstract, sealed and static modifiers are discussed in the following sections.

Classes abstratasAbstract classes

O abstract modificador é usado para indicar que uma classe está incompleta e que se destina a ser usada somente como uma classe 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. Uma classe abstrata difere de uma classe não abstrata das seguintes maneiras:An abstract class differs from a non-abstract class in the following ways:

  • Uma classe abstrata não pode ser instanciada diretamente e é um erro de tempo de compilação para usar o new operador em uma classe abstrata.An abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class. Embora seja possível ter variáveis e valores cujos tipos de tempo de compilação sejam abstratos, tais variáveis e valores serão necessariamente null ou contêm referências a instâncias de classes não abstratas derivadas dos tipos abstratos.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.
  • Uma classe abstrata é permitida (mas não obrigatória) para conter membros abstratos.An abstract class is permitted (but not required) to contain abstract members.
  • Uma classe abstrata não pode ser selada.An abstract class cannot be sealed.

Quando uma classe não abstrata é derivada de uma classe abstrata, a classe não abstrata deve incluir implementações reais de todos os membros abstratos herdados, substituindo assim esses membros abstratos.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. No exemploIn 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
    }
}

a classe A abstract introduz um método Fabstract.the abstract class A introduces an abstract method F. A B classe introduz um método Gadicional, mas como não fornece uma implementação de F, B também deve ser declarada abstrata.Class B introduces an additional method G, but since it doesn't provide an implementation of F, B must also be declared abstract. As C substituições F de classe e fornecem uma implementação real.Class C overrides F and provides an actual implementation. Como não há membros abstratos no C, C o é permitido (mas não obrigatório) para ser não abstrato.Since there are no abstract members in C, C is permitted (but not required) to be non-abstract.

Classes lacradasSealed classes

O sealed modificador é usado para impedir a derivação de uma classe.The sealed modifier is used to prevent derivation from a class. Um erro em tempo de compilação ocorrerá se uma classe selada for especificada como a classe base de outra classe.A compile-time error occurs if a sealed class is specified as the base class of another class.

Uma classe selada também não pode ser uma classe abstrata.A sealed class cannot also be an abstract class.

O sealed modificador é usado principalmente para evitar derivação não intencional, mas também permite determinadas otimizações de tempo de execução.The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. Em particular, como uma classe selada é conhecida por nunca ter classes derivadas, é possível transformar invocações de membro de função virtual em instâncias de classe seladas em invocações não virtuais.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.

Classes estáticasStatic classes

O static modificador é usado para marcar a classe que está sendo declarada como uma classe estática.The static modifier is used to mark the class being declared as a static class. Uma classe estática não pode ser instanciada, não pode ser usada como um tipo e pode conter somente membros estáticos.A static class cannot be instantiated, cannot be used as a type and can contain only static members. Somente uma classe estática pode conter declarações de métodos de extensão (métodos de extensão).Only a static class can contain declarations of extension methods (Extension methods).

Uma declaração de classe estática está sujeita às seguintes restrições:A static class declaration is subject to the following restrictions:

  • Uma classe estática não pode incluir um sealed modificador ou abstract .A static class may not include a sealed or abstract modifier. No entanto, observe que, como uma classe estática não pode ser instanciada ou derivada de, ela se comporta como se fosse selada e abstrata.Note, however, that since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.
  • Uma classe estática pode não incluir uma especificação de class_base (especificação de base de classe) e não pode especificar explicitamente uma classe base ou uma 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. Uma classe estática herda implicitamente do tipo object.A static class implicitly inherits from type object.
  • Uma classe estática só pode conter membros estáticos (membros estáticos e de instância).A static class can only contain static members (Static and instance members). Observe que as constantes e os tipos aninhados são classificados como membros estáticos.Note that constants and nested types are classified as static members.
  • Uma classe estática não pode ter membros protected ou protected internal a acessibilidade declarada.A static class cannot have members with protected or protected internal declared accessibility.

É um erro de tempo de compilação para violar qualquer uma dessas restrições.It is a compile-time error to violate any of these restrictions.

Uma classe estática não tem construtores de instância.A static class has no instance constructors. Não é possível declarar um construtor de instância em uma classe estática e nenhum construtor de instância padrão (construtores padrão) é fornecido para uma classe 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.

Os membros de uma classe estática não são estáticos automaticamente e as declarações de membro devem incluir explicitamente static um modificador (exceto para constantes e tipos aninhados).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). Quando uma classe é aninhada em uma classe externa estática, a classe aninhada não é uma classe estática, a menos static que inclua explicitamente um 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.

Referenciando tipos de classe estáticaReferencing static class types

Um namespace_or_type_name (namespace e nomes de tipo) tem permissão para fazer referência a uma classe estática seA namespace_or_type_name (Namespace and type names) is permitted to reference a static class if

  • O namespace_or_type_name é o T em um namespace_or_type_name do formulário T.I ouThe namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • O namespace_or_type_name é o T em um typeof_expression (lista de argumentos1) do formato typeof(T).The namespace_or_type_name is the T in a typeof_expression (Argument lists1) of the form typeof(T).

Um primary_expression (membros da função) tem permissão para fazer referência a uma classe estática seA primary_expression (Function members) is permitted to reference a static class if

Em qualquer outro contexto, é um erro de tempo de compilação para fazer referência a uma classe estática.In any other context it is a compile-time error to reference a static class. Por exemplo, é um erro para uma classe estática ser usada como uma classe base, um tipo constituinte (tipos aninhados) de um membro, um argumento de tipo genérico ou uma restrição 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. Da mesma forma, uma classe estática não pode ser usada em um tipo de matriz, um new tipo de ponteiro, uma expressão, is uma expressão de as conversão, uma sizeof expressão, uma expressão, uma expressão ou uma expressão de valor padrão.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

O modificador partial é usado para indicar que esse class_declaration é uma declaração de tipo parcial.The partial modifier is used to indicate that this class_declaration is a partial type declaration. Várias declarações de tipo parcial com o mesmo nome dentro de um namespace delimitador ou declaração de tipo são combinadas para formar uma declaração de tipo, seguindo as regras especificadas em tipos parciais.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.

Ter a declaração de uma classe distribuída em segmentos separados de texto do programa pode ser útil se esses segmentos são produzidos ou mantidos em diferentes contextos.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 exemplo, uma parte de uma declaração de classe pode ser gerada pela máquina, enquanto a outra é criada manualmente.For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. A separação textual dos dois impede que as atualizações de um entrem em conflito com as atualizações do outro.Textual separation of the two prevents updates by one from conflicting with updates by the other.

Parâmetros de tipoType parameters

Um parâmetro de tipo é um identificador simples que denota um espaço reservado para um argumento de tipo fornecido para criar um tipo construído.A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. Um parâmetro de tipo é um espaço reservado formal para um tipo que será fornecido posteriormente.A type parameter is a formal placeholder for a type that will be supplied later. Por outro lado, um argumento de tipo (argumentos de tipo) é o tipo real que é substituído pelo parâmetro de tipo quando um tipo construído é criado.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 em uma declaração de classe define um nome no espaço de declaração (declarações) dessa classe.Each type parameter in a class declaration defines a name in the declaration space (Declarations) of that class. Portanto, ele não pode ter o mesmo nome que outro parâmetro de tipo ou um membro declarado nessa classe.Thus, it cannot have the same name as another type parameter or a member declared in that class. Um parâmetro de tipo não pode ter o mesmo nome que o próprio tipo.A type parameter cannot have the same name as the type itself.

Especificação de base de classeClass base specification

Uma declaração de classe pode incluir uma especificação class_base , que define a classe base direta da classe e as interfaces (interfaces) diretamente implementadas pela classe.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)*
    ;

A classe base especificada em uma declaração de classe pode ser um tipo de classe construído (tipos construídos).The base class specified in a class declaration can be a constructed class type (Constructed types). Uma classe base não pode ser um parâmetro de tipo por conta própria, embora possa envolver os parâmetros de tipo que estão no escopo.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

Classes baseBase classes

Quando um class_type é incluído no class_base, ele especifica a classe base direta da classe que está sendo declarada.When a class_type is included in the class_base, it specifies the direct base class of the class being declared. Se uma declaração de classe não tiver nenhum class_baseou se o class_base listar apenas os tipos de interface, a classe base direta será considerada 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. Uma classe herda membros de sua classe base direta, conforme descrito em herança.A class inherits members from its direct base class, as described in Inheritance.

No exemploIn the example

class A {}

class B: A {}

a A classe é considerada como a classe base direta de Be B é considerada derivada de A.class A is said to be the direct base class of B, and B is said to be derived from A. Como A o não especifica explicitamente uma classe base direta, sua classe base direta é objectimplicitamente.Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

Para um tipo de classe construída, se uma classe base for especificada na declaração de classe genérica, a classe base do tipo construído será obtida pela substituição, para cada type_parameter na declaração de classe base, o type_argument correspondentedo tipo construído.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 as declarações de classe genéricasGiven the generic class declarations

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

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

a classe base do tipo G<int> construído B<string,int[]>seria.the base class of the constructed type G<int> would be B<string,int[]>.

A classe base direta de um tipo de classe deve ser pelo menos acessível como o próprio tipo de classe (domínios de acessibilidade).The direct base class of a class type must be at least as accessible as the class type itself (Accessibility domains). Por exemplo, é um erro de tempo de compilação para uma public classe derivar de uma private classe internal ou.For example, it is a compile-time error for a public class to derive from a private or internal class.

A classe base direta de um tipo de classe não deve ser um dos seguintes tipos: System.Array System.MulticastDelegate, System.Delegate System.Enum,, ou 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. Além disso, uma declaração de classe genérica System.Attribute não pode usar como uma classe base direta ou indireta.Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

Ao determinar o significado da A especificação de classe base direta de uma classe B, a classe base direta de B é temporariamente considerada. objectWhile 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, isso garante que o significado de uma especificação de classe base não possa depender recursivamente por si só.Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. O Exemplo:The example:

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

class C : A<C.B> {}

está em erro porque, A<C.B> na especificação da classe base, a classe base direta de C é considerada objectcomo sendo e, portanto (pelas regras de namespace e nomesde C tipo) não é considerada como um membro 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.

As classes base de um tipo de classe são a classe base direta e suas classes base.The base classes of a class type are the direct base class and its base classes. Em outras palavras, o conjunto de classes base é o fechamento transitivo da relação de classe base direta.In other words, the set of base classes is the transitive closure of the direct base class relationship. Fazendo referência ao exemplo acima, as classes base do B são A e object.Referring to the example above, the base classes of B are A and object. No exemploIn the example

class A {...}

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

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

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

as classes base do D<int> são C<int[]>, B<IComparable<int[]>>, A, e object.the base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

Exceto para classe object, cada tipo de classe tem exatamente uma classe base direta.Except for class object, every class type has exactly one direct base class. A object classe não tem nenhuma classe base direta e é a classe base definitiva de todas as outras classes.The object class has no direct base class and is the ultimate base class of all other classes.

Quando uma classe B deriva de uma classe A, é um Berro de tempo de compilação para A o qual depender.When a class B derives from a class A, it is a compile-time error for A to depend on B. Uma classe depende diretamente de sua classe base direta (se houver) e depende diretamente da classe na qual ela é imediatamente aninhada (se houver).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 essa definição, o conjunto completo de classes sobre as quais uma classe depende é o fechamento reflexivo e transitivo da relação depende diretamente de.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.

O exemploThe example

class A: A {}

é errado porque a classe depende dele mesmo.is erroneous because the class depends on itself. Da mesma forma, o exemploLikewise, the example

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

é um erro porque as classes dependem circularmente.is in error because the classes circularly depend on themselves. Por fim, o exemploFinally, the example

class A: B.C {}

class B: A
{
    public class C {}
}

resulta em um erro de tempo de compilação A porque depende B.C de (sua classe base direta B ), que depende (sua Aclasse de circunscrição imediatamente), que depende circularmente.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.

Observe que uma classe não depende das classes que estão aninhadas dentro dela.Note that a class does not depend on the classes that are nested within it. No exemploIn the example

class A
{
    class B: A {}
}

Bdepende de A (porque A é sua classe base direta e sua classe imediatamente delimitadora), B mas A não depende (porque B não é uma classe base nem uma classe de circunscrição 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). Portanto, o exemplo é válido.Thus, the example is valid.

Não é possível derivar de uma sealed classe.It is not possible to derive from a sealed class. No exemploIn the example

sealed class A {}

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

Aa B classeestácomerroporquetentaderivardaclasse.sealedclass B is in error because it attempts to derive from the sealed class A.

Implementações de interfacesInterface implementations

Uma especificação class_base pode incluir uma lista de tipos de interface, caso em que a classe é mencionada para implementar diretamente os tipos de interface fornecidos.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. Implementações de interface são discutidas mais detalhadamente em implementações de interface.Interface implementations are discussed further in Interface implementations.

Restrições de parâmetro de tipoType parameter constraints

As declarações de tipo genérico e de método podem opcionalmente especificar restrições de parâmetro de tipo, incluindo 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 consiste no token where, seguido pelo nome de um parâmetro de tipo, seguido por dois-pontos e pela lista de restrições para esse 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. Pode haver no máximo uma where cláusula para cada parâmetro de tipo e as where cláusulas podem ser listadas em qualquer ordem.There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. Como os get tokens e set em um acessador where de propriedade, o token não é uma palavra-chave.Like the get and set tokens in a property accessor, the where token is not a keyword.

A lista de restrições fornecida em uma where cláusula pode incluir qualquer um dos seguintes componentes, nesta ordem: uma única restrição primária, uma ou mais restrições secundárias e a restrição de construtor, 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().

Uma restrição PRIMARY pode ser um tipo de classe ou a restrição class de tipo de referência ou a restrição structde tipo de valor.A primary constraint can be a class type or the reference type constraint class or the value type constraint struct. Uma restrição secundária pode ser um type_parameter ou um interface_type.A secondary constraint can be a type_parameter or interface_type.

A restrição de tipo de referência especifica que um argumento de tipo usado para o parâmetro de tipo deve ser um tipo de referência.The reference type constraint specifies that a type argument used for the type parameter must be a reference type. Todos os tipos de classe, tipos de interface, tipos delegados, tipos de matriz e parâmetros de tipo conhecidos como um tipo de referência (conforme definido abaixo) atendem a essa restrição.All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

A restrição de tipo de valor especifica que um argumento de tipo usado para o parâmetro de tipo deve ser um tipo de valor não anulável.The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. Todos os tipos de struct não anuláveis, tipos de enumeração e parâmetros de tipo que têm a restrição de tipo de valor atendem a essa restrição.All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Observe que, embora seja classificado como um tipo de valor, um tipo anulável (tipos anuláveis) não satisfaz a restrição de tipo de valor.Note that although classified as a value type, a nullable type (Nullable types) does not satisfy the value type constraint. Um parâmetro de tipo com a restrição de tipo de valor também não pode ter o constructor_constraint.A type parameter having the value type constraint cannot also have the constructor_constraint.

Os tipos de ponteiro nunca podem ser argumentos de tipo e não são considerados para satisfazer as restrições de tipo de referência ou tipo de valor.Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

Se uma restrição for um tipo de classe, um tipo de interface ou um parâmetro de tipo, esse tipo especificará um "tipo base" mínimo que cada argumento de tipo usado para esse parâmetro de tipo deve dar suporte a.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. Sempre que um tipo construído ou um método genérico é usado, o argumento de tipo é verificado em relação às restrições no parâmetro de tipo em tempo de compilação.Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. O argumento de tipo fornecido deve atender às condições descritas em satisfazer restrições.The type argument supplied must satisfy the conditions described in Satisfying constraints.

Uma restrição class_type deve atender às seguintes regras:A class_type constraint must satisfy the following rules:

  • O tipo deve ser um tipo de classe.The type must be a class type.
  • O tipo não deve ser sealed.The type must not be sealed.
  • O tipo não deve ser um dos seguintes System.Arraytipos: System.Enum, System.Delegate, ou System.ValueType.The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • O tipo não deve ser object.The type must not be object. Como todos os tipos derivam de object, essa restrição não teria nenhum efeito se fosse permitida.Because all types derive from object, such a constraint would have no effect if it were permitted.
  • No máximo uma restrição para um determinado parâmetro de tipo pode ser um tipo de classe.At most one constraint for a given type parameter can be a class type.

Um tipo especificado como uma restrição interface_type deve atender às seguintes regras:A type specified as an interface_type constraint must satisfy the following rules:

  • O tipo deve ser um tipo de interface.The type must be an interface type.
  • Um tipo não deve ser especificado mais de uma vez em uma where determinada cláusula.A type must not be specified more than once in a given where clause.

Em ambos os casos, a restrição pode envolver qualquer um dos parâmetros de tipo do tipo associado ou declaração de método como parte de um tipo construído e pode envolver o tipo que está sendo declarado.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.

Qualquer classe ou tipo de interface especificado como uma restrição de parâmetro de tipo deve ser pelo menos como acessível (restrições de acessibilidade) como o tipo genérico ou o método que está sendo declarado.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.

Um tipo especificado como uma restrição type_parameter deve atender às seguintes regras:A type specified as a type_parameter constraint must satisfy the following rules:

  • O tipo deve ser um parâmetro de tipo.The type must be a type parameter.
  • Um tipo não deve ser especificado mais de uma vez em uma where determinada cláusula.A type must not be specified more than once in a given where clause.

Além disso, não deve haver ciclos no grafo de dependência dos parâmetros de tipo, em que a dependência é uma relação 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:

  • Se um T parâmetro de tipo for usado como uma restrição para o S parâmetro S de tipo, dependerá de T.If a type parameter T is used as a constraint for type parameter S then S depends on T.
  • Se um parâmetro S de tipo depender de um parâmetro T de T tipo e depender de um U parâmetro S de tipo, dependerá de 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 essa relação, é um erro de tempo de compilação para um parâmetro de tipo depender de si mesmo (direta ou indiretamente).Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

Todas as restrições devem ser consistentes entre os parâmetros de tipo dependentes.Any constraints must be consistent among dependent type parameters. Se o parâmetro S de tipo depender do T parâmetro de tipo, então:If type parameter S depends on type parameter T then:

  • TNão deve ter a restrição de tipo de valor.T must not have the value type constraint. Caso contrário T , é efetivamente lacrado, portanto S T, seria forçado a ser o mesmo tipo de, eliminando a necessidade de dois 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.
  • Se S tiver a restrição de tipo de valor, T não deverá ter uma restrição class_type .If S has the value type constraint then T must not have a class_type constraint.
  • Se S tiver uma restrição class_type A e T tiver uma restrição class_type B, deverá haver uma conversão de identidade ou conversão de referência implícita de A para B ou uma conversão de referência implícita 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.
  • Se S também depender do parâmetro de tipo U e U tiver uma restrição class_type A e T tiver uma restrição class_type B, deverá haver uma conversão de identidade ou conversão implícita de referência de A para B ou uma conversão de referência implícita de 0 a 1.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.

É válido para S que o tenha a restrição de tipo de T valor e tenha a restrição de tipo de referência.It is valid for S to have the value type constraint and T to have the reference type constraint. Efetivamente, isso T limita os tipos System.Object, System.ValueType, System.Enume qualquer tipo de interface.Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

Se a where cláusula de um parâmetro de tipo incluir uma restrição de Construtor (que tem new()o formulário), será possível usar o new operador para criar instâncias do tipo (expressões de criação de objeto).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). Qualquer argumento de tipo usado para um parâmetro de tipo com uma restrição de construtor deve ter um construtor público sem parâmetros (esse construtor existe implicitamente para qualquer tipo de valor) ou ser um parâmetro de tipo que tenha a restrição de tipo de valor ou restrição de Construtor (consulte Restrições de parâmetro de tipo para obter detalhes).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).

Veja a seguir exemplos de restrições: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()
{
    ...
}

O exemplo a seguir está em erro porque causa uma circularidade no grafo de dependência dos 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
{
    ...
}

Os exemplos a seguir ilustram situações inválidas adicionais: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
{
    ...
}

A classe base efetiva de um parâmetro T de tipo é definida da seguinte maneira:The effective base class of a type parameter T is defined as follows:

  • Se T o não tiver restrições primárias ou restrições de parâmetro de tipo, sua objectclasse base efetiva será.If T has no primary constraints or type parameter constraints, its effective base class is object.
  • Se T tiver a restrição de tipo de valor, sua classe base System.ValueTypeefetiva será.If T has the value type constraint, its effective base class is System.ValueType.
  • Se T tiver uma restrição class_type C, mas nenhuma restrição de type_parameter , sua classe base efetiva será C.If T has a class_type constraint C but no type_parameter constraints, its effective base class is C.
  • Se T não tiver nenhuma restrição class_type , mas tiver uma ou mais restrições type_parameter , sua classe base efetiva será o tipo mais abrangedo (operadores de conversão levantados) no conjunto de classes base efetivas de seu type_ restrições de parâmetro.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. As regras de consistência asseguram que exista um tipo mais abrangente.The consistency rules ensure that such a most encompassed type exists.
  • Se T tiver uma restrição class_type e uma ou mais restrições type_parameter , sua classe base efetiva será o tipo mais abrangedo (operadores deconversão levantados) no conjunto que consiste em class_type restrição de T e as classes base efetivas de suas restrições type_parameter .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. As regras de consistência asseguram que exista um tipo mais abrangente.The consistency rules ensure that such a most encompassed type exists.
  • Se T tiver a restrição de tipo de referência, mas não houver restrições class_type , sua classe base efetiva será object.If T has the reference type constraint but no class_type constraints, its effective base class is object.

Para fins dessas regras, se T tiver uma restrição V que seja um value_type, use em vez disso o tipo base mais específico de V que seja um 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. Isso nunca pode ocorrer em uma restrição explicitamente determinada, mas pode ocorrer quando as restrições de um método genérico são implicitamente herdadas por uma declaração de método de substituição ou uma implementação explícita de um método de interface.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.

Essas regras garantem que a classe base efetiva seja sempre uma class_type.These rules ensure that the effective base class is always a class_type.

O conjunto de interfaces efetivas de um T parâmetro de tipo é definido da seguinte maneira:The effective interface set of a type parameter T is defined as follows:

  • Se T não tiver nenhum secondary_constraints, seu conjunto de interface eficaz estará vazio.If T has no secondary_constraints, its effective interface set is empty.
  • Se T tiver restrições interface_type , mas nenhuma restrição de type_parameter , seu conjunto de interface eficaz será seu conjunto de restrições interface_type .If T has interface_type constraints but no type_parameter constraints, its effective interface set is its set of interface_type constraints.
  • Se T não tiver restrições interface_type , mas tiver restrições type_parameter , seu conjunto de interface eficaz será a União dos conjuntos de interface efetivos de suas restrições type_parameter .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.
  • Se T tiver restrições interface_type e restrições type_parameter , seu conjunto de interface eficaz será a União de seu conjunto de restrições interface_type e os conjuntos de interface efetivos de seu type_parameter reflexiva.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.

Um parâmetro de tipo é conhecido como um tipo de referência se ele tiver a restrição de tipo de referência ou se sua classe object base System.ValueTypeefetiva não for ou.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.

Os valores de um tipo de parâmetro de tipo restrito podem ser usados para acessar os membros de instância implícitos pelas restrições.Values of a constrained type parameter type can be used to access the instance members implied by the constraints. No exemploIn the example

interface IPrintable
{
    void Print();
}

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

os métodos de IPrintable podem ser invocados diretamente x no T porque o é restrito a sempre implementar IPrintable.the methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

Corpo da classeClass body

O class_body de uma classe define os membros dessa classe.The class_body of a class defines the members of that class.

class_body
    : '{' class_member_declaration* '}'
    ;

Tipos parciaisPartial types

Uma declaração de tipo pode ser dividida em várias declarações de tipo parciais.A type declaration can be split across multiple partial type declarations. A declaração de tipo é construída de suas partes seguindo as regras nesta seção, momento ela é tratada como uma única declaração durante o restante do processamento de tempo de compilação e tempo de execução do 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.

Um class_declaration, struct_declaration ou interface_declaration representa uma declaração de tipo parcial se incluir um modificador partial.A class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partialNão é uma palavra-chave e atua apenas como um modificador se aparecer imediatamente antes de uma das palavras- classchave struct , interface ou em uma declaração de tipo ou antes do void tipo em uma declaração 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. Em outros contextos, ele pode ser usado como um identificador normal.In other contexts it can be used as a normal identifier.

Cada parte de uma declaração de tipo parcial deve incluir partial um modificador.Each part of a partial type declaration must include a partial modifier. Ele deve ter o mesmo nome e ser declarado no mesmo namespace ou em uma declaração de tipo que as outras partes.It must have the same name and be declared in the same namespace or type declaration as the other parts. O partial modificador indica que partes adicionais da declaração de tipo podem existir em outro lugar, mas a existência dessas partes adicionais não é um requisito; ela é válida para um tipo com uma única declaração para partial incluir o 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 as partes de um tipo parcial devem ser compiladas em conjunto, de modo que as partes possam ser mescladas em tempo de compilação em uma única declaração de tipo.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 parciais especificamente não permitem que tipos já compilados sejam estendidos.Partial types specifically do not allow already compiled types to be extended.

Tipos aninhados podem ser declarados em várias partes partial usando o modificador.Nested types may be declared in multiple parts by using the partial modifier. Normalmente, o tipo recipiente é declarado usando partial também, e cada parte do tipo aninhado é declarada em uma parte diferente do tipo recipiente.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.

O partial modificador não é permitido em declarações delegate ou enum.The partial modifier is not permitted on delegate or enum declarations.

AtributosAttributes

Os atributos de um tipo parcial são determinados pela combinação, em uma ordem não especificada, os atributos de cada uma das partes.The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. Se um atributo for colocado em várias partes, será equivalente a especificar o atributo várias vezes no tipo.If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. Por exemplo, as duas partes:For example, the two parts:

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

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

são equivalentes a uma declaração como:are equivalent to a declaration such as:

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

Os atributos nos parâmetros de tipo são combinados de maneira semelhante.Attributes on type parameters combine in a similar fashion.

ModificadoresModifiers

Quando uma declaração de tipo parcial inclui uma especificação de acessibilidade public( protectedos internalmodificadores private ,, e), ela deve concordar com todas as outras partes que incluem uma especificação de acessibilidade.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. Se nenhuma parte de um tipo parcial incluir uma especificação de acessibilidade, o tipo receberá a acessibilidade padrão apropriada (aacessibilidade declarada).If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (Declared accessibility).

Se uma ou mais declarações parciais de um tipo aninhado new incluírem um modificador, nenhum aviso será relatado se o tipo aninhado ocultar um membro herdado (ocultando a herança).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).

Se uma ou mais declarações parciais de uma classe incluírem um abstract modificador, a classe será considerada abstrata (classes abstratas).If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (Abstract classes). Caso contrário, a classe será considerada não abstrata.Otherwise, the class is considered non-abstract.

Se uma ou mais declarações parciais de uma classe incluírem um sealed modificador, a classe será considerada selada (classes seladas).If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (Sealed classes). Caso contrário, a classe será considerada sem lacre.Otherwise, the class is considered unsealed.

Observe que uma classe não pode ser abstrata e selada.Note that a class cannot be both abstract and sealed.

Quando o unsafe modificador é usado em uma declaração de tipo parcial, somente essa parte específica é considerada um contexto não seguro (contextos não 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 e restriçõesType parameters and constraints

Se um tipo genérico for declarado em várias partes, cada parte deverá declarar os parâmetros de tipo.If a generic type is declared in multiple parts, each part must state the type parameters. Cada parte deve ter o mesmo número de parâmetros de tipo e o mesmo nome para cada parâmetro de tipo, em ordem.Each part must have the same number of type parameters, and the same name for each type parameter, in order.

Quando uma declaração de tipo genérico parcial inclui restriçõeswhere (cláusulas), as restrições devem concordar com todas as outras partes que incluem restrições.When a partial generic type declaration includes constraints (where clauses), the constraints must agree with all other parts that include constraints. Especificamente, cada parte que inclui restrições deve ter restrições para o mesmo conjunto de parâmetros de tipo e, para cada parâmetro de tipo, os conjuntos de restrições primária, secundária e de construtor devem 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. Dois conjuntos de restrições são equivalentes se contiverem os mesmos membros.Two sets of constraints are equivalent if they contain the same members. Se nenhuma parte de um tipo genérico parcial especificar restrições de parâmetro de tipo, os parâmetros de tipo serão considerados irrestrito.If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.

O exemploThe 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>
{
    ...
}

está correto porque essas partes que incluem restrições (as duas primeiras) efetivamente especificam o mesmo conjunto de restrições primárias, secundárias e de construtor para o mesmo 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.

Classe baseBase class

Quando uma declaração de classe parcial inclui uma especificação de classe base, ela deve concordar com todas as outras partes que incluem uma especificação de classe base.When a partial class declaration includes a base class specification it must agree with all other parts that include a base class specification. Se nenhuma parte de uma classe parcial incluir uma especificação de classe base, a classe base System.Object se tornará (classes base).If no part of a partial class includes a base class specification, the base class becomes System.Object (Base classes).

Interfaces baseBase interfaces

O conjunto de interfaces base para um tipo declarado em várias partes é a União das interfaces base especificadas em 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. Uma interface base específica só pode ser nomeada uma vez em cada parte, mas é permitida para várias partes nomear as mesmas interfaces base.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). Deve haver apenas uma implementação dos membros de qualquer interface base fornecida.There must only be one implementation of the members of any given base interface.

No exemploIn the example

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

partial class C: IC {...}

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

o conjunto de interfaces base para a C classe IAé IB, e IC.the set of base interfaces for class C is IA, IB, and IC.

Normalmente, cada parte fornece uma implementação das interfaces declaradas nessa parte; no entanto, isso não é um requisito.Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. Uma parte pode fornecer a implementação para uma interface declarada em uma parte diferente: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
{
    ...
}

MembrosMembers

Com exceção dos métodos parciais (métodos parciais), o conjunto de membros de um tipo declarado em várias partes é simplesmente a União do conjunto de membros declarado em 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. Os corpos de todas as partes da declaração de tipo compartilham o mesmo espaço de declaração (declarações), e o escopo de cada membro (escopos) se estende aos corpos de todas as 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. O domínio de acessibilidade de qualquer membro sempre inclui todas as partes do tipo delimitador; um private membro declarado em uma parte pode ser acessado livremente de outra 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. É um erro de tempo de compilação para declarar o mesmo membro em mais de uma parte do tipo, a menos que esse membro seja um tipo com partial o 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;
    }
}

A ordenação de membros dentro de um tipo é raramente C# significativa para o código, mas pode ser significativa ao fazer a interface com outras linguagens e ambientes.The ordering of members within a type is rarely significant to C# code, but may be significant when interfacing with other languages and environments. Nesses casos, a ordenação de membros dentro de um tipo declarado em várias partes é indefinida.In these cases, the ordering of members within a type declared in multiple parts is undefined.

Métodos parciaisPartial methods

Os métodos parciais podem ser definidos em uma parte de uma declaração de tipo e implementados em outro.Partial methods can be defined in one part of a type declaration and implemented in another. A implementação é opcional; Se nenhuma parte implementar o método parcial, a declaração de método parcial e todas as chamadas para ele serão removidas da declaração de tipo resultante da combinação das 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.

Os métodos parciais não podem definir modificadores de acesso, privatemas são implicitamente.Partial methods cannot define access modifiers, but are implicitly private. O tipo de retorno deve voidser, e seus parâmetros não podem out ter o modificador.Their return type must be void, and their parameters cannot have the out modifier. O identificador partial será reconhecido como uma palavra-chave especial em uma declaração de método somente se aparecer imediatamente void antes do tipo; caso contrário, ele poderá ser usado como um 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. Um método parcial não pode implementar explicitamente métodos de interface.A partial method cannot explicitly implement interface methods.

Há dois tipos de declarações de método parciais: Se o corpo da declaração do método for um ponto e vírgula, a declaração será considerada uma declaração de método parcial de definição.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. Se o corpo for fornecido como um bloco, a declaração será considerada uma declaração de método parcial de implementação.If the body is given as a block, the declaration is said to be an implementing partial method declaration. Em todas as partes de uma declaração de tipo, pode haver apenas uma definição de declaração de método parcial com uma determinada assinatura, e pode haver apenas uma implementação de declaração de método parcial com uma determinada assinatura.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. Se uma declaração de método parcial de implementação for fornecida, uma declaração de método parcial de definição correspondente deverá existir e as declarações deverão corresponder conforme especificado no seguinte: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:

  • As declarações devem ter os mesmos modificadores (embora não necessariamente na mesma ordem), nome do método, número de parâmetros de tipo e 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.
  • Os parâmetros correspondentes nas declarações devem ter os mesmos modificadores (embora não necessariamente na mesma ordem) e os mesmos tipos (diferenças de módulo em nomes 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).
  • Os parâmetros de tipo correspondentes nas declarações devem ter as mesmas restrições (diferenças de módulo em nomes de parâmetro de tipo).Corresponding type parameters in the declarations must have the same constraints (modulo differences in type parameter names).

Uma declaração de método parcial de implementação pode aparecer na mesma parte da declaração de método parcial de definição correspondente.An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.

Apenas um método parcial de definição participa da resolução de sobrecarga.Only a defining partial method participates in overload resolution. Portanto, se uma declaração de implementação for ou não determinada, as expressões de invocação podem ser resolvidas para invocações do método parcial.Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Como um método parcial sempre retorna void, essas expressões de invocação sempre serão instruções de expressão.Because a partial method always returns void, such invocation expressions will always be expression statements. Além disso, como um método parcial é implicitamente private, essas instruções sempre ocorrerão dentro de uma das partes da declaração de tipo dentro da qual o método parcial é declarado.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.

Se nenhuma parte de uma declaração de tipo parcial contiver uma declaração de implementação para um determinado método parcial, qualquer instrução de expressão invocando-a será simplesmente removida da declaração de tipo combinado.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. Portanto, a expressão de invocação, incluindo quaisquer expressões constituintes, não tem nenhum efeito no tempo de execução.Thus the invocation expression, including any constituent expressions, has no effect at run-time. O próprio método parcial também é removido e não será um membro da declaração de tipo combinado.The partial method itself is also removed and will not be a member of the combined type declaration.

Se existir uma declaração de implementação para um determinado método parcial, as invocações dos métodos parciais serão mantidas.If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. O método parcial fornece a elevação para uma declaração de método semelhante à declaração de método parcial de implementação, exceto para o seguinte:The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

  • O partial modificador não está incluídoThe partial modifier is not included
  • Os atributos na declaração de método resultante são os atributos combinados da definição e a declaração de método parcial de implementação em ordem não especificada.The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. Duplicatas não são removidas.Duplicates are not removed.
  • Os atributos nos parâmetros da declaração de método resultante são os atributos combinados dos parâmetros correspondentes da definição e a declaração de método parcial de implementação em ordem não especificada.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. Duplicatas não são removidas.Duplicates are not removed.

Se uma declaração de definição, mas não uma declaração de implementação, for fornecida para um método parcial M, as seguintes restrições serão aplicáveis:If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:

Os métodos parciais são úteis para permitir que uma parte de uma declaração de tipo Personalize o comportamento de outra parte, por exemplo, uma que seja gerada por uma ferramenta.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 a seguinte declaração de classe 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();
}

Se essa classe for compilada sem nenhuma outra parte, a definição de declarações de método parcial e suas invocações será removida e a declaração de classe combinada resultante será equivalente à seguinte: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; }
    }
}

Suponha que outra parte seja fornecida, no entanto, que fornece declarações de implementação dos métodos parciais: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);
    }
}

Em seguida, a declaração de classe combinada resultante será equivalente à seguinte: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);
    }
}

Associação de nomeName binding

Embora cada parte de um tipo extensível deva ser declarada dentro do mesmo namespace, as partes normalmente são escritas em diferentes declarações de namespace.Although each part of an extensible type must be declared within the same namespace, the parts are typically written within different namespace declarations. Portanto, diretivas using diferentes (usando diretivas) podem estar presentes para cada parte.Thus, different using directives (Using directives) may be present for each part. Ao interpretar nomes simples (inferência de tipos) dentro de uma parte, using somente as diretivas das declarações de namespace que envolvem essa parte são consideradas.When interpreting simple names (Type inference) within one part, only the using directives of the namespace declaration(s) enclosing that part are considered. Isso pode resultar no mesmo identificador com significados diferentes em 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
    }
}

Membros de classeClass members

Os membros de uma classe consistem nos membros introduzidos por seus class_member_declarations e nos membros herdados da classe base direta.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
    ;

Os membros de um tipo de classe são divididos nas seguintes categorias:The members of a class type are divided into the following categories:

  • Constantes, que representam valores constantes associados à classe (constantes).Constants, which represent constant values associated with the class (Constants).
  • Campos, que são as variáveis da classe (campos).Fields, which are the variables of the class (Fields).
  • Métodos, que implementam os cálculos e as ações que podem ser executadas pela classe (métodos).Methods, which implement the computations and actions that can be performed by the class (Methods).
  • Propriedades, que definem características nomeadas e as ações associadas à leitura e à gravação dessas características (Propriedades).Properties, which define named characteristics and the actions associated with reading and writing those characteristics (Properties).
  • Eventos, que definem as notificações que podem ser geradas pela classe (eventos).Events, which define notifications that can be generated by the class (Events).
  • Indexadores, que permitem que as instâncias da classe sejam indexadas da mesma forma (sintaticamente) como matrizes (indexadores).Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays (Indexers).
  • Operadores, que definem os operadores de expressão que podem ser aplicados a instâncias da classe (operadores).Operators, which define the expression operators that can be applied to instances of the class (Operators).
  • Construtores de instância, que implementam as ações necessárias para inicializar instâncias da classe (construtores de instância)Instance constructors, which implement the actions required to initialize instances of the class (Instance constructors)
  • Destruidores, que implementam as ações a serem executadas antes que as instâncias da classe sejam descartadas permanentemente (destruidores).Destructors, which implement the actions to be performed before instances of the class are permanently discarded (Destructors).
  • Construtores estáticos, que implementam as ações necessárias para inicializar a própria classe (construtores estáticos).Static constructors, which implement the actions required to initialize the class itself (Static constructors).
  • Tipos, que representam os tipos que são locais para a classe (tipos aninhados).Types, which represent the types that are local to the class (Nested types).

Membros que podem conter código executável são coletivamente conhecidos como membros da função do tipo de classe.Members that can contain executable code are collectively known as the function members of the class type. Os membros da função de um tipo de classe são os métodos, as propriedades, os eventos, os indexadores, os operadores, os construtores de instância, os destruidores e os construtores estáticos desse tipo de classe.The function members of a class type are the methods, properties, events, indexers, operators, instance constructors, destructors, and static constructors of that class type.

Um class_declaration cria um novo espaço de declaração (declarações) e os class_member_declarations imediatamente contidos pelo class_declaration introduzem novos membros nesse espaço de declaração.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. As regras a seguir se aplicam a class_member_declarations:The following rules apply to class_member_declarations:

  • Construtores de instância, destruidores e construtores estáticos devem ter o mesmo nome que a classe de circunscrição imediata.Instance constructors, destructors and static constructors must have the same name as the immediately enclosing class. Todos os outros membros devem ter nomes que diferem do nome da classe imediatamente delimitadora.All other members must have names that differ from the name of the immediately enclosing class.
  • O nome de uma constante, campo, propriedade, evento ou tipo deve ser diferente dos nomes de todos os outros membros declarados na mesma classe.The name of a constant, field, property, event, or type must differ from the names of all other members declared in the same class.
  • O nome de um método deve ser diferente dos nomes de todos os outros não-métodos declarados na mesma classe.The name of a method must differ from the names of all other non-methods declared in the same class. Além disso, a assinatura (assinaturas e sobrecarga) de um método deve ser diferente das assinaturas de todos os outros métodos declarados na mesma classe, e dois métodos declarados na mesma classe podem não ter assinaturas que diferem exclusivamente por ref e 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.
  • A assinatura de um construtor de instância deve ser diferente das assinaturas de todos os outros construtores de instância declarados na mesma classe, e dois construtores declarados na mesma classe podem não ter assinaturas que ref diferem exclusivamente pelo and. outThe 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.
  • A assinatura de um indexador deve ser diferente das assinaturas de todos os outros indexadores declarados na mesma classe.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • A assinatura de um operador deve ser diferente das assinaturas de todos os outros operadores declarados na mesma classe.The signature of an operator must differ from the signatures of all other operators declared in the same class.

Os membros herdados de um tipo de classe (herança) não fazem parte do espaço de declaração de uma classe.The inherited members of a class type (Inheritance) are not part of the declaration space of a class. Assim, uma classe derivada tem permissão para declarar um membro com o mesmo nome ou assinatura que um membro herdado (que em vigor oculta o membro herdado).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).

O tipo de instânciaThe instance type

Cada declaração de classe tem um tipo de associado associado (tipos vinculados e desvinculados), o tipo de instância.Each class declaration has an associated bound type (Bound and unbound types), the instance type. Para uma declaração de classe genérica, o tipo de instância é formado criando um tipo construído (tipos construídos) da declaração de tipo, com cada um dos argumentos de tipo fornecidos sendo o parâmetro de tipo correspondente.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. Como o tipo de instância usa os parâmetros de tipo, ele só pode ser usado quando os parâmetros de tipo estão no escopo; ou seja, dentro da declaração de classe.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. O tipo de instância é o tipo this de para código escrito dentro da declaração de classe.The instance type is the type of this for code written inside the class declaration. Para classes não genéricas, o tipo de instância é simplesmente a classe declarada.For non-generic classes, the instance type is simply the declared class. O exemplo a seguir mostra várias declarações de classe junto com seus tipos de instância: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

Membros de tipos construídosMembers of constructed types

Os membros não herdados de um tipo construído são obtidos pela substituição, para cada type_parameter na declaração de membro, o type_argument correspondente do tipo construído.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. O processo de substituição é baseado no significado semântico das declarações de tipo e não é simplesmente a substituição textual.The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

Por exemplo, considerando a declaração de classe 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) {...}
}

o tipo Gen<int[],IComparable<string>> construído tem os seguintes membros: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) {...}

O a tipo do membro na Declaração Gen de classe genérica é "matriz bidimensional de T", portanto, o tipo do membro a no tipo construído acima é "matriz bidimensional de uma matriz unidimensional de int", ou 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[,][].

Em membros da função de instância, o this tipo de é o tipo de instância (o tipo de instância) da declaração que a contém.Within instance function members, the type of this is the instance type (The instance type) of the containing declaration.

Todos os membros de uma classe genérica podem usar parâmetros de tipo de qualquer classe delimitadora, seja diretamente ou como parte de um tipo construído.All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. Quando um tipo construído específico fechado (tipos abertos e fechados) é usado em tempo de execução, cada uso de um parâmetro de tipo é substituído pelo argumento de tipo real fornecido para o tipo construído.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 exemplo: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
    }
}

HerançaInheritance

Uma classe herda os membros de seu tipo de classe base direta.A class inherits the members of its direct base class type. Herança significa que uma classe implicitamente contém todos os membros de seu tipo de classe base direta, exceto para construtores de instância, destruidores e construtores estáticos da classe 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. Alguns aspectos importantes da herança são:Some important aspects of inheritance are:

  • A herança é transitiva.Inheritance is transitive. Se C é derivado de B, e B é derivado de A, C herda os membros declarados B em, bem como os membros declarados em. AIf 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.
  • Uma classe derivada estende sua classe base direta.A derived class extends its direct base class. Uma classe derivada pode adicionar novos membros aos que ela herda, mas ela não pode remover a definição de um membro herdado.A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • Construtores de instância, destruidores e construtores estáticos não são herdados, mas todos os outros membros são, independentemente de sua acessibilidade declarada (acesso de membro).Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (Member access). No entanto, dependendo da acessibilidade declarada, os membros herdados podem não estar acessíveis em uma classe derivada.However, depending on their declared accessibility, inherited members might not be accessible in a derived class.
  • Uma classe derivada pode ocultar membros herdados (ocultando a herança) declarando novos membros com o mesmo nome ou assinatura.A derived class can hide (Hiding through inheritance) inherited members by declaring new members with the same name or signature. No entanto, observe que ocultar um membro herdado não remove esse membro — ele simplesmente torna esse membro inacessível diretamente por meio da classe derivada.Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.
  • Uma instância de uma classe contém um conjunto de todos os campos de instância declarados na classe e suas classes base, e uma conversão implícita (conversões de referência implícita) existe de um tipo de classe derivada para qualquer um de seus tipos de classe 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. Assim, uma referência a uma instância de uma classe derivada pode ser tratada como uma referência a uma instância de qualquer uma de suas classes 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.
  • Uma classe pode declarar métodos, propriedades e indexadores virtuais, e classes derivadas podem substituir a implementação desses membros da função.A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. Isso permite que as classes exibam o comportamento polimórfico em que as ações executadas por uma invocação de membro de função variam dependendo do tipo de tempo de execução da instância por meio da qual esse membro de função é invocado.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.

O membro herdado de um tipo de classe construído são os membros do tipo de classe base imediato (classes base), que é encontrado substituindo os argumentos de tipo do tipo construído para cada ocorrência dos parâmetros de tipo correspondentes noespecificação de class_base.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. Esses membros, por sua vez, são transformados substituindo, para cada type_parameter na declaração de membro, o type_argument correspondente da especificação class_base .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) {...}
}

No exemplo acima, o D<int> tipo construído tem um membro public int G(string s) não herdado obtido pela substituição do argumento int de tipo para o parâmetro Tde tipo.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>também tem um membro herdado da Declaração Bde classe.D<int> also has an inherited member from the class declaration B. Esse membro herdado é determinado pela primeira vez que determina o B<int[]> tipo D<int> de classe base int de T substituindo para na especificação B<T[]>de classe base.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[]>. Em seguida, como um argumento de Btipo int[] para, é substituído U por public U F(long index)no, produzindo o membro public int[] F(long index)herdado.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).

O novo modificadorThe new modifier

Um class_member_declaration tem permissão para declarar um membro com o mesmo nome ou assinatura de um membro herdado.A class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. Quando isso ocorre, o membro da classe derivada é dito para ocultar o membro da classe base.When this occurs, the derived class member is said to hide the base class member. Ocultar um membro herdado não é considerado um erro, mas faz com que o compilador emita um aviso.Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. Para suprimir o aviso, a declaração do membro da classe derivada pode incluir new um modificador para indicar que o membro derivado deve ocultar o membro 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. Este tópico é abordado mais detalhadamente em ocultar por meio de herança.This topic is discussed further in Hiding through inheritance.

Se um new modificador for incluído em uma declaração que não oculta um membro herdado, um aviso para esse efeito será emitido.If a new modifier is included in a declaration that doesn't hide an inherited member, a warning to that effect is issued. Esse aviso é suprimido com a remoção new do modificador.This warning is suppressed by removing the new modifier.

Modificadores de acessoAccess modifiers

Um class_member_declaration pode ter qualquer um dos cinco tipos possíveis de acessibilidade declarada (acessibilidade declarada): public, protected internal, protected, internal ou 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. Exceto para a protected internal combinação, é um erro de tempo de compilação para especificar mais de um modificador de acesso.Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. Quando um class_member_declaration não inclui nenhum modificador de acesso, private é assumido.When a class_member_declaration does not include any access modifiers, private is assumed.

Tipos constituintesConstituent types

Os tipos que são usados na declaração de um membro são chamados de tipos constituintes desse membro.Types that are used in the declaration of a member are called the constituent types of that member. Os tipos constituintes possíveis são o tipo de uma constante, campo, propriedade, evento ou indexador, o tipo de retorno de um método ou operador e os tipos de parâmetro de um construtor de método, indexador, operador ou instância.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. Os tipos constituintes de um membro devem ser pelo menos tão acessíveis quanto o próprio membro (restrições de acessibilidade).The constituent types of a member must be at least as accessible as that member itself (Accessibility constraints).

Membros estáticos e de instânciaStatic and instance members

Os membros de uma classe são membros estáticos ou membros de instância.Members of a class are either static members or instance members. Em geral, é útil considerar os membros estáticos como pertencentes a tipos de classe e membros de instância como pertencentes a objetos (instâncias de tipos de classe).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).

Quando um campo, método, propriedade, evento, operador ou declaração de Construtor inclui um static modificador, ele declara um membro estático.When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. Além disso, uma constante ou declaração de tipo declara implicitamente um membro estático.In addition, a constant or type declaration implicitly declares a static member. Os membros estáticos têm as seguintes características:Static members have the following characteristics:

  • Quando um membro estático M é referenciado em um member_access (acesso de membro) do formato E.M, E deve indicar um tipo contendo 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. É um erro de tempo de compilação para E indicar uma instância.It is a compile-time error for E to denote an instance.
  • Um campo estático identifica exatamente um local de armazenamento a ser compartilhado por todas as instâncias de um determinado tipo de classe fechado.A static field identifies exactly one storage location to be shared by all instances of a given closed class type. Independentemente de quantas instâncias de um determinado tipo de classe fechada forem criadas, há apenas uma cópia de um 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.
  • Um membro de função estática (método, propriedade, evento, operador ou Construtor) não opera em uma instância específica e é um erro de tempo de compilação para fazer referência a this esse membro de função.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.

Quando um campo, método, propriedade, evento, indexador, Construtor ou declaração de destruidor não inclui um static modificador, ele declara um membro de instância.When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. (Um membro de instância é, às vezes, chamado de membro não estático.) Os membros da instância têm as seguintes características:(An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

  • Quando um membro de instância M é referenciado em um member_access (acesso de membro) do formato E.M, E deve indicar uma instância de um tipo que contém 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. É um erro de tempo de ligação para E indicar um tipo.It is a binding-time error for E to denote a type.
  • Cada instância de uma classe contém um conjunto separado de todos os campos de instância da classe.Every instance of a class contains a separate set of all instance fields of the class.
  • Um membro de função de instância (método, propriedade, indexador, Construtor de instância ou destruidor) opera em uma determinada instância da classe, e essa instância pode ser acessada como this (esse acesso).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).

O exemplo a seguir ilustra as regras para acessar membros estáticos e de instância: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
    }
}

O método F mostra que, em um membro da função de instância, um Simple_name (nomes simples) pode ser usado para acessar membros de instância e membros 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. O método G mostra que em um membro de função estática, é um erro de tempo de compilação para acessar um membro de instância por meio de um 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. O método Main mostra que em um member_access (acesso de membro), os membros da instância devem ser acessados por meio de instâncias e os membros estáticos devem ser acessados por meio 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 aninhadosNested types

Um tipo declarado em uma declaração de classe ou struct é chamado de tipo aninhado.A type declared within a class or struct declaration is called a nested type. Um tipo declarado em uma unidade de compilação ou namespace é chamado de tipo não aninhado.A type that is declared within a compilation unit or namespace is called a non-nested type.

No exemploIn the example

using System;

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

Class B é um tipo aninhado porque é declarado dentro de AClass e Class A é um tipo não aninhado porque é declarado em uma unidade de compilação.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.

Nome totalmente qualificadoFully qualified name

O nome totalmente qualificado (nomes totalmente qualificados) para um tipo aninhado S.N é S onde é o nome totalmente qualificado do tipo no qual o N tipo é declarado.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.

Acessibilidade declaradaDeclared accessibility

Tipos não aninhados podem ter public ou internal declarar acessibilidade e ter internal declarado acessibilidade por padrão.Non-nested types can have public or internal declared accessibility and have internal declared accessibility by default. Os tipos aninhados também podem ter esses formulários de acessibilidade declarados, além de uma ou mais formas adicionais de acessibilidade declarada, dependendo se o tipo recipiente é uma classe ou estrutura: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:

  • Um tipo aninhado declarado em uma classe pode ter qualquer uma das cinco formas de acessibilidade declaradas protected internal( protected``public, internal,, privateou) e, como outros membros da classe, o private padrão é declarado acessibilidade.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.
  • Um tipo aninhado declarado em um struct pode ter qualquer uma das três formas de acessibilidade declarada internal(public, privateou) e, como outros membros de struct, usa private acessibilidade declarada por padrão.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.

O exemploThe 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 uma classe Nodeaninhada privada.declares a private nested class Node.

OcultarHiding

Um tipo aninhado pode ocultar (ocultar o nome) um membro base.A nested type may hide (Name hiding) a base member. O new modificador é permitido em declarações de tipo aninhadas para que a ocultação possa ser expressa explicitamente.The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. O exemploThe 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();
    }
}

mostra uma classe M aninhada que oculta o método M definido em Base.shows a nested class M that hides the method M defined in Base.

Este acessothis access

Um tipo aninhado e seu tipo recipiente não têm uma relação especial com relação a this_access (esse acesso).A nested type and its containing type do not have a special relationship with regard to this_access (This access). Especificamente, this dentro de um tipo aninhado não pode ser usado para fazer referência a membros de instância do tipo recipiente.Specifically, this within a nested type cannot be used to refer to instance members of the containing type. Nos casos em que um tipo aninhado precisa de acesso aos membros da instância de seu tipo recipiente, o acesso pode ser this fornecido fornecendo o para a instância do tipo recipiente como um argumento de construtor para o tipo aninhado.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. O exemplo a seguirThe 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();
    }
}

mostra essa técnica.shows this technique. Uma instância do C cria uma instância do Nested e passa seu próprio this Construtor Nestedpara o para fornecer acesso subsequente aos Cmembros da instância do.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.

Acesso a membros privados e protegidos do tipo recipienteAccess to private and protected members of the containing type

Um tipo aninhado tem acesso a todos os membros que são acessíveis para seu tipo recipiente, incluindo membros do tipo recipiente que têm e private protected têm acessibilidade 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. O exemploThe 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();
    }
}

mostra uma classe C que contém uma classe Nestedaninhada.shows a class C that contains a nested class Nested. Em Nested, o método G chama o método F estático definido em Ce F tem acessibilidade definida como particular.Within Nested, the method G calls the static method F defined in C, and F has private declared accessibility.

Um tipo aninhado também pode acessar membros protegidos definidos em um tipo base de seu tipo recipiente.A nested type also may access protected members defined in a base type of its containing type. No exemploIn 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();
    }
}

a Derived.Nested classe aninhada acessa o método F protegido definido na Derivedclasse base do, Base, chamando por meio de uma instância Deriveddo.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 aninhados em classes genéricasNested types in generic classes

Uma declaração de classe genérica pode conter declarações de tipo aninhadas.A generic class declaration can contain nested type declarations. Os parâmetros de tipo da classe delimitadora podem ser usados dentro dos tipos aninhados.The type parameters of the enclosing class can be used within the nested types. Uma declaração de tipo aninhado pode conter parâmetros de tipo adicionais que se aplicam somente ao tipo aninhado.A nested type declaration can contain additional type parameters that apply only to the nested type.

Cada declaração de tipo contida em uma declaração de classe genérica é implicitamente uma declaração de tipo genérico.Every type declaration contained within a generic class declaration is implicitly a generic type declaration. Ao gravar uma referência a um tipo aninhado em um tipo genérico, o tipo construído que o contém, incluindo seus argumentos de tipo, deve ser nomeado.When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, must be named. No entanto, de dentro da classe externa, o tipo aninhado pode ser usado sem qualificação; o tipo de instância da classe externa pode ser usado implicitamente ao construir o tipo aninhado.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. O exemplo a seguir mostra três maneiras corretas diferentes de se referir a um Innertipo construído criado a partir de; as duas primeiras são 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
    }
}

Embora seja um estilo de programação inadequado, um parâmetro de tipo em um tipo aninhado pode ocultar um membro ou parâmetro de tipo declarado no 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
    }
}

Nomes de membros reservadosReserved member names

Para facilitar a implementação C# de tempo de execução subjacente, para cada declaração de membro de origem que seja uma propriedade, um evento ou um indexador, a implementação deve reservar duas assinaturas de método com base no tipo de declaração de membro, seu nome e seu 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. É um erro de tempo de compilação para um programa declarar um membro cuja assinatura corresponde a uma dessas assinaturas reservadas, mesmo que a implementação de tempo de execução subjacente não faça uso dessas 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.

Os nomes reservados não introduzem declarações, portanto, eles não participam da pesquisa de membros.The reserved names do not introduce declarations, thus they do not participate in member lookup. No entanto, as assinaturas de método reservado associadas de uma declaração participam da herança (herança) e podem ser new ocultadas com o modificador (o novo 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).

A reserva desses nomes serve para três finalidades:The reservation of these names serves three purposes:

  • Para permitir que a implementação subjacente use um identificador comum como um nome de método para obter ou definir o acesso C# ao recurso de idioma.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 outras linguagens interoperem usando um identificador comum como um nome de método para obter ou definir C# o acesso ao recurso de idioma.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 ajudar a garantir que a origem aceita por um compilador em conformidade é aceita por outro, tornando-se as especificidades dos nomes de membro reservados consistentes em todas as C# implementações.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.

A declaração de um destruidor (destruidores) também faz com que uma assinatura seja reservada (nomes de membros reservados para destruidores).The declaration of a destructor (Destructors) also causes a signature to be reserved (Member names reserved for destructors).

Nomes de membro reservados para propriedadesMember names reserved for properties

Para uma propriedade P (Propriedades) do tipo T, as seguintes assinaturas são reservadas:For a property P (Properties) of type T, the following signatures are reserved:

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

Ambas as assinaturas são reservadas, mesmo que a propriedade seja somente leitura ou somente gravação.Both signatures are reserved, even if the property is read-only or write-only.

No exemploIn 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());
    }
}

uma classe A define uma propriedade Psomente leitura, reservando, assim, assinaturas get_P para set_P os métodos e.a class A defines a read-only property P, thus reserving signatures for get_P and set_P methods. Uma classe B deriva de A e oculta essas duas assinaturas reservadas.A class B derives from A and hides both of these reserved signatures. O exemplo produz a saída:The example produces the output:

123
123
456

Nomes de membro reservados para eventosMember names reserved for events

Para um evento E (eventos) do tipo Tdelegate, as seguintes assinaturas são 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);

Nomes de membro reservados para indexadoresMember names reserved for indexers

Para um indexador (indexadores) do tipo T com a lista Lde parâmetros, as seguintes assinaturas são 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);

Ambas as assinaturas são reservadas, mesmo que o indexador seja somente leitura ou somente gravação.Both signatures are reserved, even if the indexer is read-only or write-only.

Além disso, o Item nome do membro é reservado.Furthermore the member name Item is reserved.

Nomes de membro reservados para destruidoresMember names reserved for destructors

Para uma classe que contém um destruidor (destruidores), a assinatura a seguir é reservada:For a class containing a destructor (Destructors), the following signature is reserved:

void Finalize();

ConstantesConstants

Uma constante é um membro de classe que representa um valor constante: um valor que pode ser calculado em tempo de compilação.A constant is a class member that represents a constant value: a value that can be computed at compile-time. Um constant_declaration introduz uma ou mais constantes de um determinado tipo.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
    ;

Um constant_declaration pode incluir um conjunto de atributos (atributos), um modificador new (o novo modificador) e uma combinação válida dos quatro modificadores de acesso (modificadores de acesso).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). Os atributos e os modificadores se aplicam a todos os membros declarados pelo constant_declaration.The attributes and modifiers apply to all of the members declared by the constant_declaration. Embora constantes sejam consideradas membros estáticos, um constant_declaration não requer nem permite um modificador static.Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. É um erro para que o mesmo modificador apareça várias vezes em uma declaração de constante.It is an error for the same modifier to appear multiple times in a constant declaration.

O tipo de um constant_declaration especifica o tipo dos membros introduzidos pela declaração.The type of a constant_declaration specifies the type of the members introduced by the declaration. O tipo é seguido por uma lista de constant_declarators, cada um dos quais introduz um novo membro.The type is followed by a list of constant_declarators, each of which introduces a new member. Um constant_declarator consiste em um identificador que nomeia o membro, seguido por um token "=", seguido por um constant_expression (expressões constantes) que fornece o valor do membro.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.

O tipo especificado em uma declaração de constante deve ser sbyte, byte, short, ushort, int, uint, long, ulong, char, 0, 1, 2, 3, 4, um enum_typeou um reference_ Digite.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 deve produzir um valor do tipo de destino ou de um tipo que possa ser convertido para o tipo de destino por uma conversão implícita (conversões 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).

O tipo de uma constante deve ser pelo menos tão acessível quanto a própria constante (restrições de acessibilidade).The type of a constant must be at least as accessible as the constant itself (Accessibility constraints).

O valor de uma constante é obtido em uma expressão usando um Simple_name (nomes simples) ou um member_access (acesso de membro).The value of a constant is obtained in an expression using a simple_name (Simple names) or a member_access (Member access).

Uma constante pode participar de um constant_expression.A constant can itself participate in a constant_expression. Portanto, uma constante pode ser usada em qualquer construção que exija um constant_expression.Thus, a constant may be used in any construct that requires a constant_expression. Exemplos dessas construções incluem case rótulos, goto case instruções, enum declarações de membro, atributos e outras declarações de constantes.Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

Conforme descrito em expressões constantes, um constant_expression é uma expressão que pode ser totalmente avaliada em tempo de compilação.As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time. Como a única maneira de criar um valor não nulo de um reference_type diferente de string é aplicar o operador new e, como o operador new não é permitido em um constant_expression, o único valor possível para constantes de reference_types diferentes de string é 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.

Quando é desejado um nome simbólico para um valor constante, mas quando o tipo desse valor não é permitido em uma declaração de constante, ou quando o valor não pode ser computado em tempo de compilação por um constant_expression, um campo readonly (campos ReadOnly) pode ser usado em vez disso.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.

Uma declaração constante que declara várias constantes é equivalente a várias declarações de constantes únicas com os mesmos atributos, modificadores e tipo.A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. Por exemploFor example

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

equivale ais equivalent to

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

As constantes podem depender de outras constantes no mesmo programa, contanto que as dependências não sejam de natureza circular.Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. O compilador organiza automaticamente para avaliar as declarações constantes na ordem apropriada.The compiler automatically arranges to evaluate the constant declarations in the appropriate order. No exemploIn 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;
}

A.Yo compilador primeiro avalia, depois B.Zavalia e A.X, finalmente, avalia, produzindo os valores 10, 11e 12.the compiler first evaluates A.Y, then evaluates B.Z, and finally evaluates A.X, producing the values 10, 11, and 12. Declarações constantes podem depender de constantes de outros programas, mas essas dependências só são possíveis em uma direção.Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. Fazendo referência ao exemplo acima, se A e B foram declarados em programas separados, seria B.Zpossível A.X depender, mas B.Z , em seguida, não depender A.Ysimultaneamente.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

Um campo é um membro que representa uma variável associada a um objeto ou classe.A field is a member that represents a variable associated with an object or class. Um field_declaration introduz um ou mais campos de um determinado tipo.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
    ;

Um field_declaration pode incluir um conjunto de atributos (atributos), um modificador new (o novo modificador), uma combinação válida dos quatro modificadores de acesso (modificadores de acesso) e um modificador de static ( Campos estáticos e de instância).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). Além disso, um field_declaration pode incluir um modificador readonly (campos somente leitura) ou um modificador de volatile (campos voláteis), mas não ambos.In addition, a field_declaration may include a readonly modifier (Readonly fields) or a volatile modifier (Volatile fields) but not both. Os atributos e os modificadores se aplicam a todos os membros declarados pelo field_declaration.The attributes and modifiers apply to all of the members declared by the field_declaration. É um erro para que o mesmo modificador apareça várias vezes em uma declaração de campo.It is an error for the same modifier to appear multiple times in a field declaration.

O tipo de um field_declaration especifica o tipo dos membros introduzidos pela declaração.The type of a field_declaration specifies the type of the members introduced by the declaration. O tipo é seguido por uma lista de variable_declarators, cada um dos quais introduz um novo membro.The type is followed by a list of variable_declarators, each of which introduces a new member. Um variable_declarator consiste em um identificador que nomeia esse membro, opcionalmente seguido por um token "=" e um variable_initializer (inicializadores de variável) que fornece o valor inicial desse membro.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.

O tipo de um campo deve ser pelo menos tão acessível quanto o próprio campo (restrições de acessibilidade).The type of a field must be at least as accessible as the field itself (Accessibility constraints).

O valor de um campo é obtido em uma expressão usando um Simple_name (nomes simples) ou um member_access (acesso de membro).The value of a field is obtained in an expression using a simple_name (Simple names) or a member_access (Member access). O valor de um campo não ReadOnly é modificado usando uma atribuição (operadores de atribuição).The value of a non-readonly field is modified using an assignment (Assignment operators). O valor de um campo não ReadOnly pode ser obtido e modificado usando o incremento de sufixo e os operadores de diminuição (operadores de incremento e diminuição de sufixo) e os operadores de incremento de prefixo e decréscimo (incremento de prefixo e decréscimo operadores).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).

Uma declaração de campo que declara vários campos é equivalente a várias declarações de campos únicos com os mesmos atributos, modificadores e tipo.A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. Por exemploFor example

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

equivale ais equivalent to

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

Campos estáticos e de instânciaStatic and instance fields

Quando uma declaração de campo inclui static um modificador, os campos introduzidos pela declaração são campos estáticos.When a field declaration includes a static modifier, the fields introduced by the declaration are static fields. Quando nenhum static modificador está presente, os campos introduzidos pela declaração são campos de instância.When no static modifier is present, the fields introduced by the declaration are instance fields. Campos estáticos e campos de instância são dois dos vários tipos de variáveis (variáveis) com C#suporte do e às vezes eles são chamados de variáveis estáticas e variáveis de instância, 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.

Um campo estático não faz parte de uma instância específica; em vez disso, ele é compartilhado entre todas as instâncias de um tipo fechado (tipos abertos e fechados).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). Não importa quantas instâncias de um tipo de classe fechada são criadas, há apenas uma cópia de um campo estático para o domínio de aplicativo associado.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 exemplo: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
    }
}

Um campo de instância pertence a uma instância.An instance field belongs to an instance. Especificamente, cada instância de uma classe contém um conjunto separado de todos os campos de instância dessa classe.Specifically, every instance of a class contains a separate set of all the instance fields of that class.

Quando um campo é referenciado em um member_access (acesso de membro) do formulário E.M, se M for um campo estático, E deverá indicar um tipo contendo M e se M for um campo de instância, e deverá indicar uma instância de um tipo que contém 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.

As diferenças entre os membros estático e de instância são discutidas mais detalhadamente em membros estáticos e de instância.The differences between static and instance members are discussed further in Static and instance members.

Campos somente leituraReadonly fields

Quando um field_declaration inclui um modificador readonly, os campos introduzidos pela declaração são campos somente leitura.When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Atribuições diretas a campos ReadOnly só podem ocorrer como parte dessa declaração ou em um construtor de instância ou construtor estático na mesma classe.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. (Um campo ReadOnly pode ser atribuído a várias vezes nesses contextos.) Especificamente, as atribuições diretas a um readonly campo são permitidas apenas nos seguintes contextos:(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:

  • No variable_declarator que introduz o campo (incluindo um variable_initializer na declaração).In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
  • Para um campo de instância, nos construtores de instância da classe que contém a declaração de campo; para um campo estático, no construtor estático da classe que contém a declaração 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. Esses são também os únicos contextos nos quais é válido passar um readonly campo como um out parâmetro ou ref .These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

A tentativa de atribuir a um readonly campo ou passá-lo como out um ref parâmetro ou em qualquer outro contexto é um erro em tempo de compilação.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.

Usando campos somente leitura estáticos para constantesUsing static readonly fields for constants

Um static readonly campo é útil quando um nome simbólico para um valor constante é desejado, mas quando o tipo do valor não é permitido em uma const declaração ou quando o valor não pode ser computado em tempo de compilação.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. No exemploIn 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;
    }
}

os BlackMembros White const ,, ,Greene nãoBlue podem ser declarados como membros porque seus valores não podem ser computados em tempo de compilação. Redthe Black, White, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. No entanto, static readonly declará-las, em vez disso, tem muito o mesmo efeito.However, declaring them static readonly instead has much the same effect.

Controle de versão de constantes e campos somente leitura estáticosVersioning of constants and static readonly fields

Os campos Constants e ReadOnly têm semântica de versão binária diferente.Constants and readonly fields have different binary versioning semantics. Quando uma expressão faz referência a uma constante, o valor da constante é obtido em tempo de compilação, mas quando uma expressão faz referência a um campo ReadOnly, o valor do campo não é obtido até o tempo de execução.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 um aplicativo que consiste em dois programas separados: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);
        }
    }
}

Os Program1 namespaces e Program2 denotam dois programas que são compilados separadamente.The Program1 and Program2 namespaces denote two programs that are compiled separately. Como Program1.Utils.X é declarado como um campo somente leitura estático, a saída do valor Console.WriteLine pela instrução não é conhecida em tempo de compilação, mas é obtida em tempo de execução.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. Portanto, se o valor de X for alterado e Program1 for recompilado, a Console.WriteLine instrução produzirá o novo valor, mesmo se Program2 não for recompilado.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. No entanto X , era uma constante, o valor X de teria sido obtido no momento Program2 em que foi compilado e permaneceria inalterado por alterações no Program1 até que Program2 seja recompilado.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áteisVolatile fields

Quando um field_declaration inclui um modificador volatile, os campos introduzidos por essa declaração são campos voláteis.When a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields.

Para campos não voláteis, as técnicas de otimização que reordenam instruções podem levar a resultados inesperados e imprevisíveis em programas multi-threaded que acessam campos sem sincronização, como o fornecido pelo lock_statement (o instrução 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). Essas otimizações podem ser executadas pelo compilador, pelo sistema de tempo de execução ou por hardware.These optimizations can be performed by the compiler, by the run-time system, or by hardware. Para campos voláteis, essas otimizações de reordenação são restritas:For volatile fields, such reordering optimizations are restricted:

  • Uma leitura de um campo volátil é chamada de leitura volátil.A read of a volatile field is called a volatile read. Uma leitura volátil tem "adquirir semântica"; ou seja, é garantido que ocorra antes de qualquer referência à memória que ocorra depois dela na sequência de instruções.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.
  • Uma gravação de um campo volátil é chamada de gravação volátil.A write of a volatile field is called a volatile write. Uma gravação volátil tem "semântica de liberação"; ou seja, é garantido que ocorra após qualquer referência de memória antes da instrução de gravação na sequência de instruções.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.

Essas restrições garantem que todos os threads observem as gravações voláteis executadas por qualquer outro thread na ordem em que elas foram realizadas.These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. Uma implementação de conformidade não é necessária para fornecer uma única ordenação total de gravações voláteis, como visto de todos os threads de execução.A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. O tipo de um campo volátil deve ser um dos seguintes:The type of a volatile field must be one of the following:

  • Um reference_type.A reference_type.
  • O tipo byte sbyte ,uint ,,System.UIntPtr,,,, ,bool, ouSystem.IntPtr. short ushort int char floatThe type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, or System.UIntPtr.
  • Um enum_type tem um tipo base de enumeração de byte, sbyte, short, ushort, int ou uint.An enum_type having an enum base type of byte, sbyte, short, ushort, int, or uint.

O exemploThe 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;
            }
        }
    }
}

produz a saída:produces the output:

result = 143

Neste exemplo, o método Main inicia um novo thread que executa o método. Thread2In this example, the method Main starts a new thread that runs the method Thread2. Esse método armazena um valor em um campo não volátil chamado resulte, em seguida, armazena true no campo finishedvolátil.This method stores a value into a non-volatile field called result, then stores true in the volatile field finished. O thread principal aguarda que o campo finished seja definido como truee, em seguida, lê o resultcampo.The main thread waits for the field finished to be set to true, then reads the field result. Como finished foi declarado volatile, o thread principal deve ler o valor 143 do campo result.Since finished has been declared volatile, the main thread must read the value 143 from the field result. Se o campo finished não tivesse sido declarado volatile, seria result permitido que o repositório fosse visível finishedpara o thread principal após o repositório e, portanto, para o thread principal ler o valor 0 do 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 um volatile campo impede qualquer inconsistência.Declaring finished as a volatile field prevents any such inconsistency.

Inicialização de campoField initialization

O valor inicial de um campo, seja um campo estático ou um campo de instância, é o valor padrão (valores padrão) do tipo do 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. Não é possível observar o valor de um campo antes que essa inicialização padrão tenha ocorrido, e um campo nunca é "não 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". O exemploThe 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);
    }
}

produz a saídaproduces the output

b = False, i = 0

Porque b ei são inicializados automaticamente para valores padrão.because b and i are both automatically initialized to default values.

Inicializadores de variávelVariable initializers

Declarações de campo podem incluir variable_initializers.Field declarations may include variable_initializers. Para campos estáticos, inicializadores de variáveis correspondem a instruções de atribuição que são executadas durante a inicialização da classe.For static fields, variable initializers correspond to assignment statements that are executed during class initialization. Para campos de instância, inicializadores de variáveis correspondem a instruções de atribuição que são executadas quando uma instância da classe é criada.For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

O exemploThe 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);
    }
}

produz a saídaproduces the output

x = 1.4142135623731, i = 100, s = Hello

Porque uma atribuição x ocorre quando inicializadores de campo estáticos são executados e i atribuições s e ocorrem quando os inicializadores de campo de instância são executados.because an assignment to x occurs when static field initializers execute and assignments to i and s occur when the instance field initializers execute.

A inicialização de valor padrão descrita na inicialização do campo ocorre para todos os campos, incluindo campos que têm inicializadores variáveis.The default value initialization described in Field initialization occurs for all fields, including fields that have variable initializers. Assim, quando uma classe é inicializada, todos os campos estáticos nessa classe são inicializados primeiro com seus valores padrão e, em seguida, os inicializadores de campo estático são executados em ordem 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. Da mesma forma, quando uma instância de uma classe é criada, todos os campos de instância nessa instância são inicializados primeiro com seus valores padrão e, em seguida, os inicializadores de campo de instância são executados na ordem 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.

É possível que campos estáticos com inicializadores de variáveis sejam observados em seu estado de valor padrão.It is possible for static fields with variable initializers to be observed in their default value state. No entanto, isso é fortemente desencorajado como uma questão de estilo.However, this is strongly discouraged as a matter of style. O exemploThe 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);
    }
}

exibe esse comportamento.exhibits this behavior. Apesar das definições circulares de a e b, o programa é válido.Despite the circular definitions of a and b, the program is valid. Isso resulta na saídaIt results in the output

a = 1, b = 2

Porque os a campos estáticos b e são inicializados para 0 (o valor intpadrão de) antes de seus inicializadores serem executados.because the static fields a and b are initialized to 0 (the default value for int) before their initializers are executed. Quando o inicializador a for executado, o valor b de será zero e, a portanto, será 1inicializado para.When the initializer for a runs, the value of b is zero, and so a is initialized to 1. Quando o inicializador b for executado, o valor a de já 1será e, b portanto, será 2inicializado para.When the initializer for b runs, the value of a is already 1, and so b is initialized to 2.

Inicialização de campo estáticoStatic field initialization

Os inicializadores de variável de campo estático de uma classe correspondem a uma sequência de atribuições que são executadas na ordem textual na qual aparecem na declaração de classe.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. Se existir um construtor estático (construtores estáticos) na classe, a execução dos inicializadores de campo estáticos ocorrerá imediatamente antes da execução desse construtor 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. Caso contrário, os inicializadores de campo estáticos são executados em uma hora dependente da implementação antes do primeiro uso de um campo estático dessa classe.Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class. O exemploThe 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");
}

pode produzir a saída:might produce either the output:

Init A
Init B
1 1

ou a saída:or the output:

Init B
Init A
1 1

como a execução do Xinicializador e Ydo inicializador do pode ocorrer em qualquer ordem; eles só são restritos a ocorrer antes das referências a esses 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. No entanto, no exemplo: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");
}

a saída deve ser:the output must be:

Init B
Init A
1 1

como as regras para quando os construtores estáticos são executados (conforme definido em construtores estáticos) Bfornecem esse construtor estático (e B, portanto, inicializadores de campo estáticos A) devem ser executados antes do estático inicializadores de construtor e 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.

Inicialização do campo de instânciaInstance field initialization

Os inicializadores de variável de campo de instância de uma classe correspondem a uma sequência de atribuições que são executadas imediatamente após a entrada para qualquer um dos construtores de instância (inicializadores de Construtor) dessa classe.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. Os inicializadores de variável são executados na ordem textual em que aparecem na declaração de classe.The variable initializers are executed in the textual order in which they appear in the class declaration. O processo de criação e inicialização da instância de classe é descrito mais detalhadamente em construtores de instância.The class instance creation and initialization process is described further in Instance constructors.

Um inicializador de variável para um campo de instância não pode fazer referência à instância que está sendo criada.A variable initializer for an instance field cannot reference the instance being created. Portanto, é um erro de tempo de compilação para referenciar this em um inicializador de variável, pois é um erro de tempo de compilação para um inicializador de variável referenciar qualquer membro de instância por meio de um 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. No exemploIn the example

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

o inicializador de y variável para resulta em um erro de tempo de compilação porque faz referência a um membro da instância que está sendo criada.the variable initializer for y results in a compile-time error because it references a member of the instance being created.

MétodosMethods

Um método é um membro que implementa um cálculo ou uma ação que pode ser executada por um objeto ou classe.A method is a member that implements a computation or action that can be performed by an object or class. Os métodos são declarados usando 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 ';'
    | ';'
    ;

Um method_declaration pode incluir um conjunto de atributos (atributos) e uma combinação válida dos quatro modificadores de acesso (modificadores de acesso), o new (o novo modificador), static (estático e de instância métodos), o virtual (métodos virtuais), os 0 (métodos de substituição), os modificadores 2 (métodos lacrados), 4 (métodos abstratos) e 6 (métodos externos).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.

Uma declaração tem uma combinação válida de modificadores se todas as seguintes opções forem verdadeiras:A declaration has a valid combination of modifiers if all of the following are true:

  • A declaração inclui uma combinação válida de modificadores de acesso (modificadores de acesso).The declaration includes a valid combination of access modifiers (Access modifiers).
  • A declaração não inclui o mesmo modificador várias vezes.The declaration does not include the same modifier multiple times.
  • A declaração inclui, no máximo, um dos seguintes modificadores static: virtual, e override.The declaration includes at most one of the following modifiers: static, virtual, and override.
  • A declaração inclui, no máximo, um dos seguintes modificadores new : overridee.The declaration includes at most one of the following modifiers: new and override.
  • Se a declaração incluir o abstract modificador, a declaração não incluirá nenhum dos seguintes modificadores: static, virtual sealed ou extern.If the declaration includes the abstract modifier, then the declaration does not include any of the following modifiers: static, virtual, sealed or extern.
  • Se a declaração incluir o private modificador, a declaração não incluirá nenhum dos seguintes modificadores: virtual, overrideou abstract.If the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual, override, or abstract.
  • Se a declaração incluir o sealed modificador, a declaração também incluirá override o modificador.If the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • Se a declaração incluir o partial modificador, ele não incluirá nenhum dos seguintes modificadores: protected new virtual, sealed private public internal,,,,,, override , abstractou .externIf 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.

Um método que tem o async modificador é uma função assíncrona e segue as regras descritas em funções assíncronas.A method that has the async modifier is an async function and follows the rules described in Async functions.

O return_type de uma declaração de método especifica o tipo do valor calculado e retornado pelo método.The return_type of a method declaration specifies the type of the value computed and returned by the method. O return_type será void se o método não retornar um valor.The return_type is void if the method does not return a value. Se a declaração incluir o partial modificador, o tipo de retorno deverá voidser.If the declaration includes the partial modifier, then the return type must be void.

O member_name especifica o nome do método.The member_name specifies the name of the method. A menos que o método seja uma implementação de membro de interface explícita (implementações explícitas de membro de interface), o member_name é simplesmente um identificador.Unless the method is an explicit interface member implementation (Explicit interface member implementations), the member_name is simply an identifier. Para uma implementação de membro de interface explícita, o member_name consiste em um interface_type seguido por um "." e um identificador.For an explicit interface member implementation, the member_name consists of an interface_type followed by a "." and an identifier.

O type_parameter_list opcional especifica os parâmetros de tipo do método (parâmetros de tipo).The optional type_parameter_list specifies the type parameters of the method (Type parameters). Se um type_parameter_list for especificado, o método será um método genérico.If a type_parameter_list is specified the method is a generic method. Se o método tiver um modificador extern, um type_parameter_list não poderá ser especificado.If the method has an extern modifier, a type_parameter_list cannot be specified.

O formal_parameter_list opcional especifica os parâmetros do método (parâmetros do método).The optional formal_parameter_list specifies the parameters of the method (Method parameters).

Os type_parameter_constraints_clauseopcionais especificam restrições em parâmetros de tipo individuais (restrições de parâmetro de tipo) e só podem ser especificados se um type_parameter_list também for fornecido e o método não tiver um modificador override.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.

O return_type e cada um dos tipos referenciados no formal_parameter_list de um método devem ser pelo menos tão acessíveis quanto o próprio método (restrições de acessibilidade).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).

O method_body é um ponto e vírgula, um corpo de instrução ou um corpo de expressão.The method_body is either a semicolon, a statement body or an expression body. Um corpo de instrução consiste em um bloco, que especifica as instruções a serem executadas quando o método é invocado.A statement body consists of a block, which specifies the statements to execute when the method is invoked. Um corpo de => expressão consiste em seguido por uma expressão e um ponto e vírgula e denota uma única expressão a ser executada quando o método é invocado.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 os métodos abstract e extern, o method_body consiste em apenas um ponto e vírgula.For abstract and extern methods, the method_body consists simply of a semicolon. Para métodos partial, o method_body pode consistir em um ponto e vírgula, um corpo de bloco ou um corpo de expressão.For partial methods the method_body may consist of either a semicolon, a block body or an expression body. Para todos os outros métodos, o method_body é um corpo de bloco ou um corpo de expressão.For all other methods, the method_body is either a block body or an expression body.

Se o method_body consistir em um ponto e vírgula, a declaração pode não incluir o modificador async.If the method_body consists of a semicolon, then the declaration may not include the async modifier.

O nome, a lista de parâmetros de tipo e a lista de parâmetros formais de um método definem a assinatura (assinaturas e sobrecarga) do 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. Especificamente, a assinatura de um método consiste em seu nome, o número de parâmetros de tipo e o número, os modificadores e os tipos de seus parâmetros formais.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 essas finalidades, qualquer parâmetro de tipo do método que ocorre no tipo de um parâmetro formal é identificado não por seu nome, mas por sua posição ordinal na lista de argumentos de tipo do método. O tipo de retorno não faz parte da assinatura de um método, nem os nomes dos parâmetros de tipo ou dos parâmetros formais.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.

O nome de um método deve ser diferente dos nomes de todos os outros não-métodos declarados na mesma classe.The name of a method must differ from the names of all other non-methods declared in the same class. Além disso, a assinatura de um método deve diferir das assinaturas de todos os outros métodos declarados na mesma classe, e dois métodos declarados na mesma classe podem não ter assinaturas que ref diferem exclusivamente por e. outIn 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.

As type_parameters do método estão no escopo em todo o method_declaratione podem ser usadas para formar tipos em todo esse escopo em return_type, method_bodye type_parameter_constraints_clauses, mas não em 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 os parâmetros formais e parâmetros de tipo devem ter nomes diferentes.All formal parameters and type parameters must have different names.

Parâmetros do métodoMethod parameters

Os parâmetros de um método, se houver, são declarados pelo formal_parameter_listdo método.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
    ;

A lista de parâmetros formais consiste em um ou mais parâmetros separados por vírgulas, dos quais apenas o último pode ser um parameter_array.The formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array.

Um fixed_parameter consiste em um conjunto opcional de atributos (atributos), um modificador opcional ref, out ou this, um tipo, um identificador e um default_argumentopcional.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 um parâmetro do tipo fornecido com o nome fornecido.Each fixed_parameter declares a parameter of the given type with the given name. O this modificador designa o método como um método de extensão e só é permitido no primeiro parâmetro de um 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. Os métodos de extensão são descritos mais detalhadamente em métodos de extensão.Extension methods are further described in Extension methods.

Um fixed_parameter com um default_argument é conhecido como um parâmetro opcional, enquanto que um fixed_parameter sem um default_argument é um parâmetro necessário.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. Um parâmetro necessário pode não aparecer após um parâmetro opcional em um formal_parameter_list.A required parameter may not appear after an optional parameter in a formal_parameter_list.

Um parâmetro ref ou out não pode ter um default_argument.A ref or out parameter cannot have a default_argument. A expressão em um default_argument deve ser uma das seguintes:The expression in a default_argument must be one of the following:

  • a constant_expressiona constant_expression
  • uma expressão do formulário new S() em que S é um tipo de valoran expression of the form new S() where S is a value type
  • uma expressão do formulário default(S) em que S é um tipo de valoran expression of the form default(S) where S is a value type

A expressão deve ser conversível implicitamente por uma identidade ou conversão anulável para o tipo do parâmetro.The expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.

Se os parâmetros opcionais ocorrerem em uma declaração de método parcial de implementação (métodos parciais), uma implementação de membro de interface explícita (implementações explícitas de membro de interface) ou em uma declaração de indexador de parâmetro único ( Indexadores) que o compilador deve dar a um aviso, já que esses membros nunca podem ser invocados de forma a permitir que os argumentos sejam omitidos.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.

Um parameter_array consiste em um conjunto opcional de atributos (atributos), um modificador params, um array_typee um identificador.A parameter_array consists of an optional set of attributes (Attributes), a params modifier, an array_type, and an identifier. Uma matriz de parâmetros declara um único parâmetro do tipo de matriz fornecido com o nome fornecido.A parameter array declares a single parameter of the given array type with the given name. O array_type de uma matriz de parâmetros deve ser um tipo de matriz de dimensão única (tipos de matriz).The array_type of a parameter array must be a single-dimensional array type (Array types). Em uma invocação de método, uma matriz de parâmetros permite que um único argumento do tipo de matriz fornecido seja especificado ou que zero ou mais argumentos do tipo de elemento de matriz sejam especificados.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. Matrizes de parâmetros são descritas em mais detalhes em matrizes de parâmetros.Parameter arrays are described further in Parameter arrays.

Um parameter_array pode ocorrer após um parâmetro opcional, mas não pode ter um valor padrão – a omissão de argumentos para um parameter_array resultaria na criação de uma matriz vazia.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.

O exemplo a seguir ilustra os 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
) { }

No formal_parameter_list para M, i é um parâmetro de referência necessário, d é um parâmetro de valor obrigatório, b, s, o e t são parâmetros de valor opcionais e a é uma 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.

Uma declaração de método cria um espaço de declaração separado para parâmetros, parâmetros de tipo e variáveis locais.A method declaration creates a separate declaration space for parameters, type parameters and local variables. Os nomes são introduzidos nesse espaço de declaração pela lista de parâmetros de tipo e pela lista de parâmetros formais do método e por declarações de variáveis locais no bloco do 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. É um erro para dois membros de um espaço de declaração de método ter o mesmo nome.It is an error for two members of a method declaration space to have the same name. É um erro para o espaço de declaração de método e o espaço de declaração de variável local de um espaço de declaração aninhada para conter elementos com o mesmo nome.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.

Uma invocação de método (invocações de método) cria uma cópia, específica para essa invocação, dos parâmetros formais e das variáveis locais do método, e a lista de argumentos da invocação atribui valores ou referências de variáveis ao formal recém-criado parâmetro.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 do bloco de um método, parâmetros formais podem ser referenciados por seus identificadores em expressões Simple_name (nomes simples).Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (Simple names).

Há quatro tipos de parâmetros formais:There are four kinds of formal parameters:

  • Parâmetros de valor, que são declarados sem nenhum modificador.Value parameters, which are declared without any modifiers.
  • Parâmetros de referência, que são declarados com o ref modificador.Reference parameters, which are declared with the ref modifier.
  • Parâmetros de saída, que são declarados com o out modificador.Output parameters, which are declared with the out modifier.
  • Matrizes de parâmetros, que são declaradas com o params modificador.Parameter arrays, which are declared with the params modifier.

Conforme descrito em assinaturas e sobrecargas, os ref modificadores e out são parte da assinatura de um método, mas o params modificador não é.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

Um parâmetro declarado sem nenhum modificador é um parâmetro de valor.A parameter declared with no modifiers is a value parameter. Um parâmetro de valor corresponde a uma variável local que obtém seu valor inicial do argumento correspondente fornecido na invocação do método.A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

Quando um parâmetro formal é um parâmetro de valor, o argumento correspondente em uma invocação de método deve ser uma expressão que é implicitamente conversível (conversões implícitas) para o 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.

Um método tem permissão para atribuir novos valores a um parâmetro de valor.A method is permitted to assign new values to a value parameter. Essas atribuições afetam apenas o local de armazenamento local representado pelo parâmetro value — elas não têm nenhum efeito sobre o argumento real fornecido na invocação do 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 referênciaReference parameters

Um parâmetro declarado com um ref modificador é um parâmetro de referência.A parameter declared with a ref modifier is a reference parameter. Ao contrário de um parâmetro de valor, um parâmetro de referência não cria um novo local de armazenamento.Unlike a value parameter, a reference parameter does not create a new storage location. Em vez disso, um parâmetro de referência representa o mesmo local de armazenamento que a variável fornecida como o argumento na invocação do método.Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

Quando um parâmetro formal é um parâmetro de referência, o argumento correspondente em uma invocação de método deve consistir na palavra-chave ref seguido por um variable_reference (regras precisas para determinar a atribuição definitiva) do mesmo tipo que o 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. Uma variável deve ser definitivamente atribuída antes de poder ser passada como um parâmetro de referência.A variable must be definitely assigned before it can be passed as a reference parameter.

Dentro de um método, um parâmetro de referência é sempre considerado definitivamente atribuído.Within a method, a reference parameter is always considered definitely assigned.

Um método declarado como iterador (iteradores) não pode ter parâmetros de referência.A method declared as an iterator (Iterators) cannot have reference parameters.

O exemploThe 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);
    }
}

produz a saídaproduces the output

i = 2, j = 1

Para a invocação de Swap em Main, x representa i e y representa j.For the invocation of Swap in Main, x represents i and y represents j. Assim, a invocação tem o efeito de trocar os valores de i e. jThus, the invocation has the effect of swapping the values of i and j.

Em um método que usa parâmetros de referência, é possível que vários nomes representem o mesmo local de armazenamento.In a method that takes reference parameters it is possible for multiple names to represent the same storage location. No exemploIn 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);
    }
}

a invocação do F no G passa uma referência para s o a e bo.the invocation of F in G passes a reference to s for both a and b. Portanto, para essa invocação, os nomes s, ae b todos fazem referência ao mesmo local de armazenamento e as três atribuições modificam o campo sde instância.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 saídaOutput parameters

Um parâmetro declarado com um out modificador é um parâmetro de saída.A parameter declared with an out modifier is an output parameter. Semelhante a um parâmetro de referência, um parâmetro de saída não cria um novo local de armazenamento.Similar to a reference parameter, an output parameter does not create a new storage location. Em vez disso, um parâmetro de saída representa o mesmo local de armazenamento que a variável fornecida como o argumento na invocação do método.Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

Quando um parâmetro formal é um parâmetro de saída, o argumento correspondente em uma invocação de método deve consistir na palavra-chave out seguido de um variable_reference (regras precisas para determinar a atribuição definitiva) do mesmo tipo que o 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. Uma variável não precisa ser definitivamente atribuída antes de poder ser passada como um parâmetro de saída, mas após uma invocação em que uma variável foi passada como um parâmetro de saída, a variável é considerada definitivamente atribuída.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 um método, assim como uma variável local, um parâmetro de saída é inicialmente considerado não atribuído e deve ser definitivamente atribuído antes de seu valor ser usado.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 saída de um método deve ser definitivamente atribuído antes do retorno do método.Every output parameter of a method must be definitely assigned before the method returns.

Um método declarado como um método parcial (métodos parciais) ou um iterador (iteradores) não pode ter parâmetros de saída.A method declared as a partial method (Partial methods) or an iterator (Iterators) cannot have output parameters.

Os parâmetros de saída normalmente são usados em métodos que produzem vários valores de retorno.Output parameters are typically used in methods that produce multiple return values. Por exemplo: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);
    }
}

O exemplo produz a saída:The example produces the output:

c:\Windows\System\
hello.txt

Observe que as dir variáveis name e podem ser desatribuídas antes de serem passadas para SplitPathe que elas sejam consideradas definitivamente atribuídas após a chamada.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.

Matrizes de parâmetrosParameter arrays

Um parâmetro declarado com um params modificador é uma matriz de parâmetros.A parameter declared with a params modifier is a parameter array. Se uma lista de parâmetros formais incluir uma matriz de parâmetros, ela deverá ser o último parâmetro na lista e deverá ser de um 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 exemplo, os tipos string[] e string[][] podem ser usados como o tipo de uma matriz de parâmetros, mas o string[,] tipo não pode.For example, the types string[] and string[][] can be used as the type of a parameter array, but the type string[,] can not. Não é possível combinar o params modificador com os ref modificadores e out.It is not possible to combine the params modifier with the modifiers ref and out.

Uma matriz de parâmetros permite que argumentos sejam especificados de uma das duas maneiras em uma invocação de método:A parameter array permits arguments to be specified in one of two ways in a method invocation:

  • O argumento fornecido para uma matriz de parâmetros pode ser uma única expressão conversível implicitamente (conversões implícitas) para o 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. Nesse caso, a matriz de parâmetros funciona precisamente como um parâmetro de valor.In this case, the parameter array acts precisely like a value parameter.
  • Como alternativa, a invocação pode especificar zero ou mais argumentos para a matriz de parâmetros, em que cada argumento é uma expressão que é implicitamente conversível (conversões implícitas) para o tipo de elemento da 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. Nesse caso, a invocação cria uma instância do tipo de matriz de parâmetros com um comprimento correspondente ao número de argumentos, inicializa os elementos da instância de matriz com os valores de argumento fornecidos e usa a instância de matriz recém-criada como o 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.

Exceto para permitir um número variável de argumentos em uma invocação, uma matriz de parâmetros é precisamente equivalente a um parâmetro de valor (parâmetros de valor) do mesmo 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.

O exemploThe 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();
    }
}

produz a saídaproduces the output

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

A primeira invocação de F simplesmente passa a matriz a como um parâmetro de valor.The first invocation of F simply passes the array a as a value parameter. A segunda invocação de F cria automaticamente um quatro elementos int[] com os valores de elemento fornecidos e passa essa instância de matriz como um parâmetro de valor.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. Da mesma forma, a terceira invocação de F cria um elemento int[] zero e passa essa instância como um parâmetro de valor.Likewise, the third invocation of F creates a zero-element int[] and passes that instance as a value parameter. A segunda e terceira invocações são precisamente equivalentes a escrever:The second and third invocations are precisely equivalent to writing:

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

Ao executar a resolução de sobrecarga, um método com uma matriz de parâmetros pode ser aplicável em seu formato normal ou em sua forma expandida (membro de função aplicável).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). A forma expandida de um método só estará disponível se a forma normal do método não for aplicável e somente se um método aplicável com a mesma assinatura do formulário expandido ainda não estiver declarado no mesmo 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.

O exemploThe 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);
    }
}

produz a saídaproduces the output

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

No exemplo, duas das possíveis formas expandidas do método com uma matriz de parâmetros já estão incluídas na classe como métodos regulares.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. Portanto, esses formulários expandidos não são considerados ao executar a resolução de sobrecarga e a primeira e terceira invocações de método, portanto, selecionam os 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. Quando uma classe declara um método com uma matriz de parâmetros, não é incomum também incluir alguns dos formulários expandidos 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. Ao fazer isso, é possível evitar a alocação de uma instância de matriz que ocorre quando uma forma expandida de um método com uma matriz de parâmetros é invocada.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.

Quando o tipo de uma matriz de parâmetros object[]é, ocorre uma possível ambiguidade entre a forma normal do método e o formulário informado para um ú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. O motivo da ambiguidade é que um object[] é implicitamente conversível no tipo. objectThe reason for the ambiguity is that an object[] is itself implicitly convertible to type object. No entanto, a ambiguidade não apresenta nenhum problema, pois ela pode ser resolvida com a inserção de uma conversão, se necessário.The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.

O exemploThe 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);
    }
}

produz a saídaproduces the output

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

Na primeira e última invocações do F, a forma normal do F é aplicável porque existe uma conversão implícita do tipo de argumento para o tipo de parâmetro (ambos são do 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[]). Portanto, a resolução de sobrecarga seleciona a forma Fnormal de e o argumento é passado como um parâmetro de valor regular.Thus, overload resolution selects the normal form of F, and the argument is passed as a regular value parameter. Na segunda e terceira invocações, a forma normal de F não é aplicável porque não existe nenhuma conversão implícita do tipo de argumento para o tipo de parâmetro (o tipo object não pode ser convertido implicitamente object[]no tipo).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[]). No entanto, a forma F expandida do é aplicável, portanto, é selecionada pela resolução de sobrecarga.However, the expanded form of F is applicable, so it is selected by overload resolution. Como resultado, um elemento object[] One é criado pela invocação, e o único elemento da matriz é inicializado com o valor do argumento fornecido (que, por sua vez, é uma referência a um 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 e de instânciaStatic and instance methods

Quando uma declaração de método inclui static um modificador, esse método é considerado um método estático.When a method declaration includes a static modifier, that method is said to be a static method. Quando nenhum static modificador está presente, o método é considerado um método de instância.When no static modifier is present, the method is said to be an instance method.

Um método estático não funciona em uma instância específica e é um erro de tempo de compilação para fazer referência a this um 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.

Um método de instância opera em uma determinada instância de uma classe, e essa instância pode ser acessada como this (esse acesso).An instance method operates on a given instance of a class, and that instance can be accessed as this (This access).

Quando um método é referenciado em um member_access (acesso de membro) do formulário E.M, se M for um método estático, E deverá indicar um tipo contendo M e, se M for um método de instância, E deverá indicar uma instância de um tipo contendo 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.

As diferenças entre os membros estático e de instância são discutidas mais detalhadamente em membros estáticos e de instância.The differences between static and instance members are discussed further in Static and instance members.

Métodos virtuaisVirtual methods

Quando uma declaração de método de instância virtual inclui um modificador, esse método é considerado um método virtual.When an instance method declaration includes a virtual modifier, that method is said to be a virtual method. Quando nenhum virtual modificador está presente, o método é considerado um método não virtual.When no virtual modifier is present, the method is said to be a non-virtual method.

A implementação de um método não virtual é invariável: A implementação é a mesma se o método é invocado em uma instância da classe na qual ela é declarada ou uma instância de uma classe 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. Por outro lado, a implementação de um método virtual pode ser substituída por classes derivadas.In contrast, the implementation of a virtual method can be superseded by derived classes. O processo de substituição da implementação de um método virtual herdado é conhecido como substituição desse método (métodos de substituição).The process of superseding the implementation of an inherited virtual method is known as overriding that method (Override methods).

Em uma invocação de método virtual, o tipo de tempo de execução da instância para o qual a invocação ocorre determina a implementação do método real a ser invocado.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. Em uma invocação de método não virtual, o tipo de tempo de compilação da instância é o fator determinante.In a non-virtual method invocation, the compile-time type of the instance is the determining factor. Em termos precisos, quando um método N chamado é invocado com uma A lista de argumentos em uma instância com um tipo C de tempo de compilação e um R tipo de R tempo de C execução (onde é ou uma classe derivada de C), a invocação é processada da seguinte maneira: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:

  • Primeiro, a resolução de sobrecarga é Caplicada Na, Ae, para selecionar um método M específico do conjunto de métodos declarado em e herdado Cpor.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. Isso é descrito em invocações de método.This is described in Method invocations.
  • Em seguida, M se for um método não virtual, M será invocado.Then, if M is a non-virtual method, M is invoked.
  • Caso contrário M , é um método virtual e a implementação mais derivada de M com relação a R é invocada.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 em ou herdado por uma classe, existe uma implementação mais derivada do método em relação a essa classe.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. A implementação mais derivada de um método M virtual em relação a uma classe R é determinada da seguinte maneira:The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • Se R contiver a virtual declaração de Mintrodução de, essa será a implementação mais derivada Mde.If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • Caso contrário, R se contiver Mum override de, essa será a implementação mais derivada Mde.Otherwise, if R contains an override of M, then this is the most derived implementation of M.
  • Caso contrário, a implementação mais derivada M de em relação R ao é igual à implementação mais derivada de M em relação à classe base direta do 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.

O exemplo a seguir ilustra as diferenças entre métodos virtuais e não virtuais: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();
    }
}

No exemplo, A apresenta um método F não virtual e um método Gvirtual.In the example, A introduces a non-virtual method F and a virtual method G. A classe B introduz um novo método Fnão virtual, ocultando o herdado Fe também substitui o método Gherdado.The class B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. O exemplo produz a saída:The example produces the output:

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

Observe que a instrução a.G() B.Ginvoca, não A.G.Notice that the statement a.G() invokes B.G, not A.G. Isso ocorre porque o tipo de tempo de execução da instância (que é B), não o tipo de tempo de compilação da instância (que é A), determina a implementação do método real a ser invocado.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.

Como os métodos têm permissão para ocultar os métodos herdados, é possível que uma classe contenha vários métodos virtuais com a mesma assinatura.Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. Isso não apresenta um problema de ambiguidade, pois todos, exceto o método mais derivado, estão ocultos.This does not present an ambiguity problem, since all but the most derived method are hidden. No exemploIn 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();
    }
}

as C classes D e contêm dois métodos virtuais com a mesma assinatura: Aquele introduzido pelo A e aquele introduzido pelo 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. O método introduzido por C oculta o método herdado de A.The method introduced by C hides the method inherited from A. Assim, a declaração de substituição D em substitui o método introduzido Cpelo, e não é possível D substituir o método introduzido pelo 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. O exemplo produz a saída:The example produces the output:

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

Observe que é possível invocar o método virtual oculto acessando uma instância do D por meio de um tipo menos derivado no qual o método não 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.

Métodos de substituiçãoOverride methods

Quando uma declaração de método de instância override inclui um modificador, o método é considerado um método de substituição.When an instance method declaration includes an override modifier, the method is said to be an override method. Um método override substitui um método virtual herdado pela mesma assinatura.An override method overrides an inherited virtual method with the same signature. Enquanto uma declaração de método virtual apresenta um novo método, uma declaração de método de substituição restringe um método virtual herdado existente fornecendo uma nova implementação do 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.

O método substituído por uma override declaração é conhecido como método base substituído.The method overridden by an override declaration is known as the overridden base method. Para um método M override declarado em uma classe C, o método base substituído é determinado examinando cada tipo de classe base Cde, começando com o tipo de C classe base direta e continuando com cada sucesso o tipo de classe base direta, até em um determinado tipo de classe base, pelo menos um método acessível está localizado, que M tem a mesma assinatura que após a substituição dos 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 fins de localização do método base substituído, um método é considerado acessível publicse for protected protected internal, se for, se for, ou se internal for e declarado no mesmo programa que Co.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.

Um erro de tempo de compilação ocorre a menos que todos os itens a seguir sejam verdadeiros para uma declaração de substituição:A compile-time error occurs unless all of the following are true for an override declaration:

  • Um método base substituído pode ser localizado conforme descrito acima.An overridden base method can be located as described above.
  • Há exatamente um desses métodos de base substituído.There is exactly one such overridden base method. Essa restrição só terá efeito se o tipo de classe base for um tipo construído em que a substituição de argumentos de tipo torna a assinatura de dois métodos o mesmo.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.
  • O método base substituído é um método virtual, abstract ou override.The overridden base method is a virtual, abstract, or override method. Em outras palavras, o método base substituído não pode ser estático ou não virtual.In other words, the overridden base method cannot be static or non-virtual.
  • O método base substituído não é um método lacrado.The overridden base method is not a sealed method.
  • O método override e o método base substituído têm o mesmo tipo de retorno.The override method and the overridden base method have the same return type.
  • A declaração de substituição e o método base substituído têm a mesma acessibilidade declarada.The override declaration and the overridden base method have the same declared accessibility. Em outras palavras, uma declaração de substituição não pode alterar a acessibilidade do método virtual.In other words, an override declaration cannot change the accessibility of the virtual method. No entanto, se o método base substituído for protegido internamente e for declarado em um assembly diferente do assembly que contém o método override, a acessibilidade declarada do método de substituição deverá ser 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.
  • A declaração de substituição não especifica cláusulas de tipo-parâmetro-Constraints.The override declaration does not specify type-parameter-constraints-clauses. Em vez disso, as restrições são herdadas do método base substituído.Instead the constraints are inherited from the overridden base method. Observe que as restrições que são parâmetros de tipo no método substituído podem ser substituídas por argumentos de tipo na restrição herdada.Note that constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. Isso pode levar a restrições que não são legais quando explicitamente especificados, como tipos de valor ou tipos lacrados.This can lead to constraints that are not legal when explicitly specified, such as value types or sealed types.

O exemplo a seguir demonstra como as regras de substituição funcionam para classes 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>
}

Uma declaração de substituição pode acessar o método base substituído usando um base_access (acesso de base).An override declaration can access the overridden base method using a base_access (Base access). No exemploIn 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);
    }
}

a base.PrintFields() invocação no B invoca o PrintFields método declarado em A.the base.PrintFields() invocation in B invokes the PrintFields method declared in A. Um base_access desabilita o mecanismo de invocação virtual e simplesmente trata o método base como um método não virtual.A base_access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. A invocação B foi escrita A PrintFields ((A)this).PrintFields(), ela invocaria recursivamente o método declarado em B, e não o declarado em, pois PrintFields é virtual e o tipo de tempo de execução de ((A)this) é .BHad 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.

Somente ao incluir um override modificador, um método pode substituir outro método.Only by including an override modifier can a method override another method. Em todos os outros casos, um método com a mesma assinatura que um método herdado simplesmente oculta o método herdado.In all other cases, a method with the same signature as an inherited method simply hides the inherited method. No exemploIn the example

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

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

o F método no B não inclui um override modificador e, portanto, não substitui F o método Aem.the F method in B does not include an override modifier and therefore does not override the F method in A. Em vez disso F , o B método em oculta o método Aem e um aviso é relatado porque a declaração não inclui um 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.

No exemploIn 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
}

o F método em B oculta o método virtual F herdado de A.the F method in B hides the virtual F method inherited from A. Como o novo F no B tem acesso privado, seu escopo inclui apenas o corpo da classe B de e não se estende Cpara.Since the new F in B has private access, its scope only includes the class body of B and does not extend to C. Portanto, a declaração de F no C tem permissão para substituir o F herdado Ade.Therefore, the declaration of F in C is permitted to override the F inherited from A.

Métodos lacradosSealed methods

Quando uma declaração de método de instância sealed inclui um modificador, esse método é considerado um método lacrado.When an instance method declaration includes a sealed modifier, that method is said to be a sealed method. Se uma declaração de método de instância sealed incluir o modificador, ele também override deverá incluir o modificador.If an instance method declaration includes the sealed modifier, it must also include the override modifier. O uso do sealed modificador impede que uma classe derivada substitua ainda mais o método.Use of the sealed modifier prevents a derived class from further overriding the method.

No exemploIn 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");
    } 
}

a classe B fornece dois métodos de substituição: F um método que tem sealed o modificador G e um método que não tem.the class B provides two override methods: an F method that has the sealed modifier and a G method that does not. Bo uso do modifier desealed C impede a substituição Fadicional.B's use of the sealed modifier prevents C from further overriding F.

Métodos abstratosAbstract methods

Quando uma declaração de método de instância abstract inclui um modificador, esse método é considerado um método abstrato.When an instance method declaration includes an abstract modifier, that method is said to be an abstract method. Embora um método abstract também seja implicitamente um método virtual, ele não pode ter o virtualmodificador.Although an abstract method is implicitly also a virtual method, it cannot have the modifier virtual.

Uma declaração de método abstract apresenta um novo método virtual, mas não fornece uma implementação desse método.An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Em vez disso, as classes derivadas não abstratas são necessárias para fornecer sua própria implementação substituindo esse método.Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Como um método abstract não fornece implementação real, o method_body de um método abstract simplesmente consiste em um ponto-e-vírgula.Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon.

Declarações de método abstract só são permitidas em classes abstratas (classes abstratas).Abstract method declarations are only permitted in abstract classes (Abstract classes).

No exemploIn 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);
    }
}

a Shape classe define a noção abstrata de um objeto de forma geométrica que pode ser pintada.the Shape class defines the abstract notion of a geometrical shape object that can paint itself. O Paint método é abstrato porque não há nenhuma implementação padrão significativa.The Paint method is abstract because there is no meaningful default implementation. As Ellipse classes Box e são implementações concretas Shape .The Ellipse and Box classes are concrete Shape implementations. Como essas classes não são abstratas, elas são necessárias para substituir o Paint método e fornecer uma implementação real.Because these classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

É um erro de tempo de compilação para um base_access (acesso de base) para referenciar um método abstrato.It is a compile-time error for a base_access (Base access) to reference an abstract method. No exemploIn the example

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

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

um erro de tempo de compilação é relatado para base.F() a invocação porque faz referência a um método abstrato.a compile-time error is reported for the base.F() invocation because it references an abstract method.

Uma declaração de método abstract tem permissão para substituir um método virtual.An abstract method declaration is permitted to override a virtual method. Isso permite que uma classe abstrata force a reimplementação do método em classes derivadas e torna a implementação original do método indisponível.This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. No exemploIn 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");
    }
}

classe A declara um método virtual, a classe B substitui esse método por um método abstrato e a classe C substitui o método abstract para fornecer sua própria implementação.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

Quando uma declaração de método inclui extern um modificador, esse método é considerado um método externo.When a method declaration includes an extern modifier, that method is said to be an external method. Os métodos externos são implementados externamente, normalmente usando uma linguagem C#diferente de.External methods are implemented externally, typically using a language other than C#. Como uma declaração de método externo não fornece implementação real, o method_body de um método externo simplesmente consiste em um ponto-e-vírgula.Because an external method declaration provides no actual implementation, the method_body of an external method simply consists of a semicolon. Um método externo não pode ser genérico.An external method may not be generic.

O extern modificador é normalmente usado em conjunto com DllImport um atributo (interoperação com componentes com e Win32), permitindo que métodos externos sejam implementados por DLLs (bibliotecas de vínculo dinâmico).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). O ambiente de execução pode dar suporte a outros mecanismos nos quais implementações de métodos externos podem ser fornecidas.The execution environment may support other mechanisms whereby implementations of external methods can be provided.

Quando um método externo inclui um DllImport atributo, a declaração de método também deve incluir static um modificador.When an external method includes a DllImport attribute, the method declaration must also include a static modifier. Este exemplo demonstra o uso do extern modificador e do 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 parciais (recapitulação)Partial methods (recap)

Quando uma declaração de método inclui partial um modificador, esse método é considerado um método parcial.When a method declaration includes a partial modifier, that method is said to be a partial method. Os métodos parciais só podem ser declarados como membros de tipos parciais (tipos parciais) e estão sujeitos a várias restrições.Partial methods can only be declared as members of partial types (Partial types), and are subject to a number of restrictions. Os métodos parciais são descritos mais detalhadamente em métodos parciais.Partial methods are further described in Partial methods.

Métodos de extensãoExtension methods

Quando o primeiro parâmetro de um método inclui o this modificador, esse método é considerado um método de extensão.When the first parameter of a method includes the this modifier, that method is said to be an extension method. Os métodos de extensão só podem ser declarados em classes estáticas não genéricas e não aninhadas.Extension methods can only be declared in non-generic, non-nested static classes. O primeiro parâmetro de um método de extensão não pode ter nenhum modificador thisdiferente de e o tipo de parâmetro não pode ser um tipo de ponteiro.The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type.

Veja a seguir um exemplo de uma classe estática que declara dois métodos de extensão: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;
    }
}

Um método de extensão é um método estático regular.An extension method is a regular static method. Além disso, onde sua classe estática delimitadora está no escopo, um método de extensão pode ser invocado usando a sintaxe de invocação do método de instância (invocações de método de extensão), usando a expressão Receiver como o primeiro 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.

O programa a seguir usa os métodos de extensão declarados acima: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());
        }
    }
}

O Slice método está disponível string[]no, e o ToInt32 método está disponível em string, porque eles foram declarados como métodos de extensão.The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as extension methods. O significado do programa é o mesmo que o seguinte, usando chamadas de método estáticos comuns: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));
        }
    }
}

Corpo do métodoMethod body

O method_body de uma declaração de método consiste em um corpo de bloco, um corpo de expressão ou um ponto-e-vírgula.The method_body of a method declaration consists of either a block body, an expression body or a semicolon.

O tipo de resultado de um método void é se o tipo de voidretorno for ou se o método for Async e o tipo de System.Threading.Tasks.Taskretorno for.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. Caso contrário, o tipo de resultado de um método não assíncrono é seu tipo de retorno, e o tipo de resultado de um método assíncrono System.Threading.Tasks.Task<T> com Ttipo de retorno é.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.

Quando um método tem um void tipo de resultado e um corpo de return bloco,as instruções (a instrução return) no bloco não têm permissão para especificar uma expressão.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. Se a execução do bloco de um método void for concluída normalmente (ou seja, o controle flui para fora do final do corpo do método), esse método simplesmente retorna ao seu chamador atual.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.

Quando um método tem um resultado void e um corpo de expressão, a expressão E deve ser um statement_expression, e o corpo é exatamente equivalente a um corpo de bloco do formulário { 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; }.

Quando um método tem um tipo de resultado não void e um corpo de bloco, return cada instrução no bloco deve especificar uma expressão que é implicitamente conversível para o 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. O ponto de extremidade de um corpo de bloco de um método de retorno de valor não deve ser acessível.The endpoint of a block body of a value-returning method must not be reachable. Em outras palavras, em um método de retorno de valor com um corpo de bloco, o controle não tem permissão para fluir para fora do final do corpo do 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.

Quando um método tem um tipo de resultado não void e um corpo de expressão, a expressão deve ser conversível implicitamente no tipo de resultado e o corpo é exatamente equivalente a um corpo de bloco do { return E; }formulário.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; }.

No exemploIn 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;
}

o método de retorno F de valor resulta em um erro de tempo de compilação porque o controle pode fluir para fora do final do corpo do método.the value-returning F method results in a compile-time error because control can flow off the end of the method body. Os G métodos H e estão corretos porque todos os caminhos de execução possíveis terminam em uma instrução Return que especifica um valor de retorno.The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value. O I método está correto, pois seu corpo é equivalente a um bloco de instrução com apenas uma única instrução de retorno.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étodoMethod overloading

As regras de resolução de sobrecarga de método são descritas em inferência de tipos.The method overload resolution rules are described in Type inference.

PropertiesProperties

Uma Propriedade é um membro que fornece acesso a uma característica de um objeto ou de uma classe.A property is a member that provides access to a characteristic of an object or a class. Exemplos de propriedades incluem o comprimento de uma cadeia de caracteres, o tamanho de uma fonte, a legenda de uma janela, o nome de um cliente e assim por diante.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. As propriedades são uma extensão natural dos campos – ambos são membros nomeados com tipos associados e a sintaxe para acessar campos e propriedades é a mesma.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. No entanto, diferentemente dos campos, as propriedades não denotam locais de armazenamento.However, unlike fields, properties do not denote storage locations. Em vez disso, as propriedades têm acessadores que especificam as instruções a serem executadas quando os valores forem lidos ou gravados.Instead, properties have accessors that specify the statements to be executed when their values are read or written. Portanto, as propriedades fornecem um mecanismo para associar ações com a leitura e gravação de atributos de um objeto; Além disso, eles permitem que esses atributos sejam computados.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.

As propriedades são declaradas usando 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 ';'
    ;

Um property_declaration pode incluir um conjunto de atributos (atributos) e uma combinação válida dos quatro modificadores de acesso (modificadores de acesso), o new (o novo modificador), static (estático e de instância métodos), o virtual (métodos virtuais), os 0 (métodos de substituição), os modificadores 2 (métodos lacrados), 4 (métodos abstratos) e 6 (métodos externos).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.

As declarações de propriedade estão sujeitas às mesmas regras que as declarações de método (métodos) em relação a combinações válidas de modificadores.Property declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

O tipo de uma declaração de propriedade especifica o tipo da propriedade introduzida pela declaração e member_name especifica o nome da propriedade.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 a propriedade seja uma implementação de membro de interface explícita, o member_name é simplesmente um identificador.Unless the property is an explicit interface member implementation, the member_name is simply an identifier. Para uma implementação explícita de membro de interface (implementações explícitas de membro de interface), o member_name consiste em um interface_type seguido por um "." e um 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.

O tipo de uma propriedade deve ser pelo menos tão acessível quanto a própria propriedade (restrições de acessibilidade).The type of a property must be at least as accessible as the property itself (Accessibility constraints).

Um property_body pode consistir em um corpo de acessador ou um corpo de expressão.A property_body may either consist of an accessor body or an expression body. Em um corpo de acessador, accessor_declarations, que deve ser incluído nos tokens "{" e "}", declare os acessadores (acessadores) da propriedade.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Os acessadores especificam as instruções Executáveis associadas à leitura e gravação da propriedade.The accessors specify the executable statements associated with reading and writing the property.

Um corpo de => expressão que consiste em seguido por uma expressão E e um ponto-e-vírgula é exatamente equivalente ao corpo { get { return E; } }da instrução e, portanto, só pode ser usado para especificar propriedades somente getter em que o resultado de o getter é fornecido por uma única expressão.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.

Um property_initializer só pode ser fornecido para uma propriedade implementada automaticamente (Propriedades implementadas automaticamente) e causa a inicialização do campo subjacente de tais propriedades com o valor fornecido pela expressão .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.

Embora a sintaxe para acessar uma propriedade seja a mesma de um campo, uma propriedade não é classificada como uma variável.Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Portanto, não é possível passar uma propriedade como um ref argumento ou. outThus, it is not possible to pass a property as a ref or out argument.

Quando uma declaração de propriedade inclui extern um modificador, a propriedade é considerada uma Propriedade externa.When a property declaration includes an extern modifier, the property is said to be an external property. Como uma declaração de propriedade externa não fornece implementação real, cada uma de suas accessor_declarations consiste em um ponto-e-vírgula.Because an external property declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

Propriedades de instância e estáticaStatic and instance properties

Quando uma declaração de propriedade inclui static um modificador, a propriedade é considerada uma propriedade estática.When a property declaration includes a static modifier, the property is said to be a static property. Quando nenhum static modificador estiver presente, a propriedade será considerada uma propriedade de instância.When no static modifier is present, the property is said to be an instance property.

Uma propriedade estática não está associada a uma instância específica e é um erro de tempo de compilação para se referir this aos acessadores de uma propriedade 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.

Uma propriedade de instância é associada a uma determinada instância de uma classe, e essa instância pode ser acessada como this (esse acesso) nos acessadores dessa propriedade.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.

Quando uma propriedade é referenciada em um member_access (acesso de membro) do formulário E.M, se M for uma propriedade estática, E deverá indicar um tipo contendo M e se M for uma propriedade de instância, e deverá denotar uma instância de um tipo contendo 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.

As diferenças entre os membros estático e de instância são discutidas mais detalhadamente em membros estáticos e de instância.The differences between static and instance members are discussed further in Static and instance members.

AcessadoresAccessors

O accessor_declarations de uma propriedade especifica as instruções Executáveis associadas à leitura e gravação dessa propriedade.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
    | ';'
    ;

As declarações de acessador consistem em um get_accessor_declaration, um set_accessor_declarationou ambos.The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Cada declaração de acessador consiste no token get ou set seguido por um accessor_modifier opcional e um accessor_body.Each accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

O uso de accessor_modifiers é regido pelas seguintes restrições:The use of accessor_modifiers is governed by the following restrictions:

  • Um accessor_modifier não pode ser usado em uma interface ou em uma implementação de membro de interface explícita.An accessor_modifier may not be used in an interface or in an explicit interface member implementation.
  • Para uma propriedade ou um indexador que não tem um modificador override, um accessor_modifier será permitido somente se a propriedade ou o indexador tiver um acessador get e set e, em seguida, for permitido somente em um desses acessadores.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 uma propriedade ou um indexador que inclui um modificador override, um acessador deve corresponder ao accessor_modifier, se houver, do acessador que está sendo substituído.For a property or indexer that includes an override modifier, an accessor must match the accessor_modifier, if any, of the accessor being overridden.
  • O accessor_modifier deve declarar uma acessibilidade que seja estritamente mais restritiva do que a acessibilidade declarada da propriedade ou do indexador em si.The accessor_modifier must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. Para ser preciso:To be precise:
    • Se a propriedade ou o indexador tiver uma acessibilidade declarada de public, o accessor_modifier poderá ser protected internal, internal, protected ou private.If the property or indexer has a declared accessibility of public, the accessor_modifier may be either protected internal, internal, protected, or private.
    • Se a propriedade ou o indexador tiver uma acessibilidade declarada de protected internal, o accessor_modifier poderá ser internal, protected ou private.If the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • Se a propriedade ou o indexador tiver uma acessibilidade declarada de internal ou protected, o accessor_modifier deverá ser private.If the property or indexer has a declared accessibility of internal or protected, the accessor_modifier must be private.
    • Se a propriedade ou o indexador tiver uma acessibilidade declarada de private, nenhum accessor_modifier poderá ser usado.If the property or indexer has a declared accessibility of private, no accessor_modifier may be used.

Para as propriedades abstract e extern, o accessor_body para cada acessador especificado é simplesmente um ponto-e-vírgula.For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. Uma propriedade não abstrata que não seja externa pode ter cada accessor_body um ponto-e-vírgula; nesse caso, é uma propriedade implementada automaticamente (Propriedades implementadas automaticamente).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). Uma propriedade implementada automaticamente deve ter pelo menos um acessador get.An automatically implemented property must have at least a get accessor. Para os acessadores de qualquer outra propriedade não abstrata e não externa, o accessor_body é um bloco que especifica as instruções a serem executadas quando o acessador correspondente é invocado.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.

Um get acessador corresponde a um método sem parâmetros com um valor de retorno do tipo de propriedade.A get accessor corresponds to a parameterless method with a return value of the property type. Exceto como o destino de uma atribuição, quando uma propriedade é referenciada em uma expressão, get o acessador da propriedade é invocado para calcular o valor da propriedade (valores de expressões).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). O corpo de um get acessador deve estar em conformidade com as regras para métodos de retorno de valor descritos no corpo do método.The body of a get accessor must conform to the rules for value-returning methods described in Method body. Em particular, todas return as instruções no corpo de um get acessador devem especificar uma expressão que é implicitamente conversível para o tipo de propriedade.In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. Além disso, o ponto de get extremidade de um acessador não deve ser acessível.Furthermore, the endpoint of a get accessor must not be reachable.

Um set acessador corresponde a um método com um parâmetro de valor único do tipo de void Propriedade e um tipo de retorno.A set accessor corresponds to a method with a single value parameter of the property type and a void return type. O parâmetro implícito de um set acessador é valuesempre denominado.The implicit parameter of a set accessor is always named value. Quando uma propriedade é referenciada como o destino de uma atribuição(operadores de atribuição) ou como o operando ++ de -- ou (operadores deincremento de sufixo e decréscimo, incremento de prefixo e diminuir os operadores), o acessador é invocado com um argumento (cujo valor é o lado direito da atribuição ou o operando ++ do operador or -- ) que fornece o novo valor (atribuição simples). setWhen 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). O corpo de um set acessador deve estar em conformidade void com as regras para métodos descritos no corpo do método.The body of a set accessor must conform to the rules for void methods described in Method body. Em particular, return as instruções no set corpo do acessador não têm permissão para especificar uma expressão.In particular, return statements in the set accessor body are not permitted to specify an expression. Como um set acessador implicitamente tem um valueparâmetro chamado, ele é um erro de tempo de compilação para uma variável local ou uma set declaração de constante em um acessador para ter esse nome.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.

Com base na presença ou ausência de get set acessadores, uma propriedade é classificada da seguinte maneira:Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • Uma propriedade que inclui um get acessador e um set acessador é considerada uma propriedade de leitura/gravação .A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • Uma propriedade que tem apenas um get acessador é considerada uma propriedade somente leitura .A property that has only a get accessor is said to be a read-only property. É um erro de tempo de compilação para uma propriedade somente leitura ser o destino de uma atribuição.It is a compile-time error for a read-only property to be the target of an assignment.
  • Uma propriedade que tem apenas um set acessador é considerada uma propriedade somente gravação .A property that has only a set accessor is said to be a write-only property. Exceto como o destino de uma atribuição, é um erro de tempo de compilação para referenciar uma propriedade somente gravação em uma expressão.Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression.

No exemploIn 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
    }
}

o Button controle declara uma propriedade pública Caption .the Button control declares a public Caption property. O get acessador Caption da propriedade retorna a cadeia de caracteres armazenada no campo particular caption .The get accessor of the Caption property returns the string stored in the private caption field. O set acessador verifica se o novo valor é diferente do valor atual e, nesse caso, armazena o novo valor e redesenha o controle.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. As propriedades geralmente seguem o padrão mostrado acima: O get acessador simplesmente retorna um valor armazenado em um campo particular, set e o acessador modifica esse campo particular e, em seguida, executa quaisquer ações adicionais necessárias para atualizar totalmente o estado do 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.

Considerando a Button classe acima, veja a seguir um exemplo de uso Caption da propriedade: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

Aqui, o set acessador é invocado atribuindo um valor à propriedade, e o get acessador é invocado referenciando a propriedade em uma expressão.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.

O get eset os acessadores de uma propriedade não são membros distintos e não é possível declarar os acessadores de uma propriedade separadamente.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. Assim, não é possível que os dois acessadores de uma propriedade de leitura/gravação tenham acessibilidade diferente.As such, it is not possible for the two accessors of a read-write property to have different accessibility. O exemploThe 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; }
    }
}

não declara uma única propriedade de leitura/gravação.does not declare a single read-write property. Em vez disso, ele declara duas propriedades com o mesmo nome, uma somente leitura e uma somente gravação.Rather, it declares two properties with the same name, one read-only and one write-only. Como dois membros declarados na mesma classe não podem ter o mesmo nome, o exemplo causa a ocorrência de um erro de tempo de compilação.Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

Quando uma classe derivada declara uma propriedade com o mesmo nome de uma propriedade herdada, a propriedade derivada oculta a propriedade herdada em relação à leitura e à gravação.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. No exemploIn the example

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

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

a P Propriedade em B oculta a P Propriedade em A com relação à leitura e gravação.the P property in B hides the P property in A with respect to both reading and writing. Portanto, nas instruçõesThus, 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

a atribuição para b.P causa a reportação de um erro em tempo de compilação, pois a P propriedade somente B leitura no oculta a propriedade somente P gravação no 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. Observe, no entanto, que uma conversão pode ser usada para acessar P a Propriedade Hidden.Note, however, that a cast can be used to access the hidden P property.

Ao contrário dos campos públicos, as propriedades fornecem uma separação entre o estado interno de um objeto e sua interface pública.Unlike public fields, properties provide a separation between an object's internal state and its public interface. Considere o exemplo: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; }
    }
}

Aqui, a Label classe usa dois int campos x e y, para armazenar seu local.Here, the Label class uses two int fields, x and y, to store its location. O local é exposto X publicamente como um e uma Y Propriedade e como uma Location Propriedade do tipo Point.The location is publicly exposed both as an X and a Y property and as a Location property of type Point. Se, em uma versão futura do Label, for mais conveniente armazenar o local como um Point internamente, a alteração poderá ser feita sem afetar a interface pública da classe: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; }
    }
}

Tinha x public readonly Label e y , em vez de campos, seria impossível fazer essa alteração na classe.Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

Expor o estado por meio de propriedades não é necessariamente qualquer menos eficiente do que expor os campos diretamente.Exposing state through properties is not necessarily any less efficient than exposing fields directly. Em particular, quando uma propriedade é não virtual e contém apenas uma pequena quantidade de código, o ambiente de execução pode substituir chamadas para acessadores pelo código real dos acessadores.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. Esse processo é conhecido como inlininge torna o acesso à propriedade tão eficiente quanto o acesso ao campo, mas preserva a maior flexibilidade das propriedades.This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

Como invocar um get acessador é conceitualmente equivalente a ler o valor de um campo, ele é considerado um estilo get de programação insatisfatório para que os acessadores tenham efeitos colaterais observáveis.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. No exemploIn the example

class Counter
{
    private int next;

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

o valor da Next Propriedade depende do número de vezes que a propriedade foi acessada anteriormente.the value of the Next property depends on the number of times the property has previously been accessed. Portanto, o acesso à propriedade produz um efeito colateral observável, e a propriedade deve ser implementada como um método em vez disso.Thus, accessing the property produces an observable side-effect, and the property should be implemented as a method instead.

A Convenção "sem efeitos colaterais" para get acessadores não significa get que os acessadores sempre devem ser escritos para simplesmente retornar valores armazenados em 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. Na verdade get , os acessadores geralmente calculam o valor de uma propriedade acessando vários campos ou invocando métodos.Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. No entanto, um get acessador projetado corretamente não executa nenhuma ação que cause alterações observáveis no estado do objeto.However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

As propriedades podem ser usadas para atrasar a inicialização de um recurso até o momento em que ele é referenciado pela primeira vez.Properties can be used to delay initialization of a resource until the moment it is first referenced. Por exemplo: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;
        }
    }
}

A Console classe contém três propriedades Out, In, e Error, que representam os dispositivos de entrada, saída e erro padrão, respectivamente.The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. Ao expor esses membros como propriedades, a Console classe pode atrasar a inicialização até que elas sejam realmente usadas.By exposing these members as properties, the Console class can delay their initialization until they are actually used. Por exemplo, após a primeira referência Out à propriedade, como emFor example, upon first referencing the Out property, as in

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

o subjacente TextWriter para o dispositivo de saída é criado.the underlying TextWriter for the output device is created. Mas se o aplicativo não fizer nenhuma referência às In propriedades Error e, nenhum objeto será criado para esses dispositivos.But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

Propriedades implementadas automaticamenteAutomatically implemented properties

Uma propriedade implementada automaticamente (ou Propriedade automática para curto), é uma propriedade não abstrata não abstraida com corpos de acessadores somente de ponto e vírgula.An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. As propriedades automáticas devem ter um acessador get e, opcionalmente, podem ter um acessador set.Auto-properties must have a get accessor and can optionally have a set accessor.

Quando uma propriedade é especificada como uma propriedade implementada automaticamente, um campo de apoio oculto fica automaticamente disponível para a propriedade e os acessadores são implementados para ler e gravar nesse campo de backup.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. Se a propriedade automática não tiver nenhum acessador set, o campo de backup readonly será considerado (campos somente leitura).If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Assim como um readonly campo, uma propriedade automática somente getter também pode ser atribuída ao corpo de um construtor da classe delimitadora.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. Tal atribuição atribui diretamente ao campo de apoio somente leitura da propriedade.Such an assignment assigns directly to the readonly backing field of the property.

Uma propriedade automática pode, opcionalmente, ter um property_initializer, que é aplicado diretamente ao campo de backup como um variable_initializer (inicializadores de variável).An auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (Variable initializers).

O exemplo a seguir:The following example:

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

é equivalente à declaração a seguir: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; } }
}

O exemplo a seguir:The following example:

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

é equivalente à declaração a seguir: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; }
}

Observe que as atribuições para o campo ReadOnly são legais, pois elas ocorrem dentro do construtor.Notice that the assignments to the readonly field are legal, because they occur within the constructor.

AcessibilidadeAccessibility

Se um acessador tiver um accessor_modifier, o domínio de acessibilidade (domínios de acessibilidade) do acessador será determinado usando a acessibilidade declarada do 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. Se um acessador não tiver um accessor_modifier, o domínio de acessibilidade do acessador será determinado da acessibilidade declarada da propriedade ou do indexador.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.

A presença de um accessor_modifier nunca afeta a pesquisa de Membros (operadores) ou a resolução de sobrecarga (resolução de sobrecarga).The presence of an accessor_modifier never affects member lookup (Operators) or overload resolution (Overload resolution). Os modificadores na propriedade ou no indexador sempre determinam a qual propriedade ou indexador está associado, independentemente do contexto do acesso.The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access.

Depois que uma determinada propriedade ou um indexador tiver sido selecionado, os domínios de acessibilidade dos acessadores específicos envolvidos serão usados para determinar se esse uso é 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:

  • Se o uso for como um valor (valores de expressões), o get acessador deverá existir e estar acessível.If the usage is as a value (Values of expressions), the get accessor must exist and be accessible.
  • Se o uso for como o destino de uma atribuição simples (atribuição simples), o set acessador deverá existir e estar acessível.If the usage is as the target of a simple assignment (Simple assignment), the set accessor must exist and be accessible.
  • Se o uso for como o destino da atribuição composta (atribuição composta) ou como o destino dos ++ operadores ou -- (membros defunção. 9, expressões de get invocação), os acessadores e o set acessador deve existir e estar acessível.If the usage is as the target of compound assignment (Compound assignment), or as the target of the ++ or -- operators (Function members.9, Invocation expressions), both the get accessors and the set accessor must exist and be accessible.

No exemplo a seguir, a propriedade A.Text é ocultada pela propriedade B.Text, mesmo em contextos onde apenas o set acessador é chamado.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. Por outro lado, a B.Count propriedade não é acessível para Mclasse, portanto, a A.Count Propriedade acessível é usada em seu 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
    }
}

Um acessador que é usado para implementar uma interface pode não ter um accessor_modifier.An accessor that is used to implement an interface may not have an accessor_modifier. Se apenas um acessador for usado para implementar uma interface, o outro acessador poderá ser declarado com um 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 string Prop {
        get { return "April"; }       // Must not have a modifier here
        internal set {...}            // Ok, because I.Prop has no set accessor
    }
}

Acessadores de propriedade virtual, sealed, override e abstractVirtual, sealed, override, and abstract property accessors

Uma virtual declaração de propriedade especifica que os acessadores da propriedade são virtuais.A virtual property declaration specifies that the accessors of the property are virtual. O virtual modificador se aplica a ambos os acessadores de uma propriedade de leitura/gravação — não é possível que apenas um acessador de uma propriedade de leitura/gravação seja 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.

Uma abstract declaração de propriedade especifica que os acessadores da propriedade são virtuais, mas não fornecem uma implementação real dos acessadores.An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. Em vez disso, as classes derivadas não abstratas são necessárias para fornecer sua própria implementação para os acessadores, substituindo a propriedade.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. Como um acessador de uma declaração de propriedade abstrata não fornece implementação real, seu accessor_body simplesmente consiste em um ponto-e-vírgula.Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon.

Uma declaração de propriedade que inclui os abstract modificadores e override especifica que a propriedade é abstrata e substitui uma propriedade base.A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. Os acessadores de tal propriedade também são abstratos.The accessors of such a property are also abstract.

As declarações de propriedade abstratas só são permitidas em classes abstratas (classes abstratas). Os acessadores de uma propriedade virtual herdada podem ser substituídos em uma classe derivada, incluindo uma declaração de override propriedade que especifica uma diretiva.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. Isso é conhecido como uma declaração de propriedade de substituição.This is known as an overriding property declaration. Uma declaração de propriedade de substituição não declara uma nova propriedade.An overriding property declaration does not declare a new property. Em vez disso, ele simplesmente especializa as implementações dos acessadores de uma propriedade virtual existente.Instead, it simply specializes the implementations of the accessors of an existing virtual property.

Uma declaração de propriedade de substituição deve especificar exatamente os mesmos modificadores de acessibilidade, tipo e nome que a propriedade herdada.An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. Se a propriedade Inherited tiver apenas um único acessador (ou seja, se a propriedade herdada for somente leitura ou somente gravação), a propriedade de substituição deverá incluir somente esse acessador.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. Se a propriedade Inherited incluir os acessadores (ou seja, se a propriedade herdada for Read-Write), a propriedade de substituição poderá incluir um único acessador ou ambos os acessadores.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.

Uma declaração de propriedade de substituição pode sealed incluir o modificador.An overriding property declaration may include the sealed modifier. O uso desse modificador impede que uma classe derivada substitua ainda mais a propriedade.Use of this modifier prevents a derived class from further overriding the property. Os acessadores de uma propriedade selada também são lacrados.The accessors of a sealed property are also sealed.

Exceto pelas diferenças na sintaxe de declaração e invocação, os acessadores virtual, sealed, override e abstract se comportam exatamente como métodos virtuais, lacrados, de substituição e abstratos.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Especificamente, as regras descritas em métodos virtuais, métodos de substituição, métodos lacradose métodos abstratos se aplicam como se os acessadores fossem métodos de um formulário correspondente:Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form:

  • Um get acessador corresponde a um método sem parâmetros com um valor de retorno do tipo de propriedade e os mesmos modificadores que a propriedade recipiente.A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property.
  • Um set acessador corresponde a um método com um parâmetro de valor único do tipo de void Propriedade, um tipo de retorno e os mesmos modificadores que a propriedade recipiente.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.

No exemploIn 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é uma propriedade somente leitura virtual, Y é uma propriedade de leitura/gravação virtual e Z é uma propriedade de leitura/gravação abstrata.X is a virtual read-only property, Y is a virtual read-write property, and Z is an abstract read-write property. Como Z é abstrato, a classe A recipiente também deve ser declarada abstrata.Because Z is abstract, the containing class A must also be declared abstract.

Uma classe derivada de A é mostrada abaixo: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; }
    }
}

Aqui, as declarações de X, Ye Z estão substituindo declarações de propriedade.Here, the declarations of X, Y, and Z are overriding property declarations. Cada declaração de propriedade corresponde exatamente aos modificadores de acessibilidade, ao tipo e ao nome da propriedade herdada correspondente.Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. O get acessador do set X e o Y acessador do usam a base palavra-chave para acessar os acessadores herdados.The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. A declaração de Z substitui ambos os acessadores abstratos – portanto, não há nenhum membro de Bfunção abstrata B pendente no e é permitido ser uma classe não abstrata.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.

Quando uma propriedade é declarada overridecomo um, todos os acessadores substituídos devem ser acessíveis para o código de substituição.When a property is declared as an override, any overridden accessors must be accessible to the overriding code. Além disso, a acessibilidade declarada da propriedade ou do indexador em si, e dos acessadores, deve corresponder à do membro e aos acessadores substituídos.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 exemplo: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
    }
}

EventsEvents

Um evento é um membro que permite que um objeto ou classe forneça notificações.An event is a member that enables an object or class to provide notifications. Os clientes podem anexar código executável para eventos fornecendo manipuladores de eventos.Clients can attach executable code for events by supplying event handlers.

Os eventos são declarados usando 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
    ;

Um event_declaration pode incluir um conjunto de atributos (atributos) e uma combinação válida dos quatro modificadores de acesso (modificadores de acesso), o new (o novo modificador), static (estático e de instância métodos), o virtual (métodos virtuais), os 0 (métodos de substituição), os modificadores 2 (métodos lacrados), 4 (métodos abstratos) e 6 (métodos externos).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.

As declarações de evento estão sujeitas às mesmas regras que as declarações de método (métodos) em relação a combinações válidas de modificadores.Event declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

O tipo de uma declaração de evento deve ser um delegate_type (tipos de referência) e esse delegate_type deve ser pelo menos acessível como o próprio evento (restrições de acessibilidade).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).

Uma declaração de evento pode incluir event_accessor_declarations.An event declaration may include event_accessor_declarations. No entanto, se não for, para eventos não-extern e não abstratos, o compilador fornecerá esses itens automaticamente (eventos semelhantes a campo); para eventos extern, os acessadores são fornecidos 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.

Uma declaração de evento que omite event_accessor_declarations define um ou mais eventos, um para cada um dos variable_declarators.An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarators. Os atributos e os modificadores se aplicam a todos os membros declarados por tal event_declaration.The attributes and modifiers apply to all of the members declared by such an event_declaration.

É um erro de tempo de compilação para um event_declaration incluir o modificador abstract e event_accessor_declarationsdelimitado por chave.It is a compile-time error for an event_declaration to include both the abstract modifier and brace-delimited event_accessor_declarations.

Quando uma declaração de evento inclui extern um modificador, o evento é considerado um evento externo.When an event declaration includes an extern modifier, the event is said to be an external event. Como uma declaração de evento externo não fornece implementação real, é um erro para que ela inclua o modificador extern e 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.

É um erro de tempo de compilação para um variable_declarator de uma declaração de evento com um modificador abstract ou external para incluir um 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.

Um evento pode ser usado como o operando esquerdo dos operadores += e -= (atribuição deevento).An event can be used as the left-hand operand of the += and -= operators (Event assignment). Esses operadores são usados, respectivamente, para anexar manipuladores de eventos ou para remover manipuladores de eventos de um evento, e os modificadores de acesso do evento controlam os contextos nos quais essas operações são permitidas.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.

Como += e-= são as únicas operações que são permitidas em um evento fora do tipo que declara o evento, o código externo pode adicionar e remover manipuladores para um evento, mas não pode obter ou modificar a lista subjacente de eventos manipuladores.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.

Em uma operação do formulário x += y ou x -= y, quando x é um evento e a referência ocorre fora do tipo que contém a declaração de x, o resultado da operação tem o tipo void (em vez de ter o tipo de x, com o valor de x após a atribuição).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). Essa regra proíbe que o código externo examine indiretamente o delegado subjacente de um evento.This rule prohibits external code from indirectly examining the underlying delegate of an event.

O exemplo a seguir mostra como os manipuladores de eventos são anexados Button a instâncias da classe: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
    }
}

Aqui, o LoginDialog Construtor de instância cria Button duas instâncias e anexa manipuladores de Click eventos aos eventos.Here, the LoginDialog instance constructor creates two Button instances and attaches event handlers to the Click events.

Eventos do tipo campoField-like events

Dentro do texto do programa da classe ou struct que contém a declaração de um evento, determinados eventos podem ser usados 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 ser usado dessa forma, um evento não deve ser abstract ou extern e não deve incluir explicitamente 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. Tal evento pode ser usado em qualquer contexto que permita um campo.Such an event can be used in any context that permits a field. O campo contém um delegado (delegados) que se refere à lista de manipuladores de eventos que foram adicionados ao evento.The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event. Se nenhum manipulador de eventos tiver sido adicionado, o campo conterá null.If no event handlers have been added, the field contains null.

No exemploIn 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é usado como um campo dentro da Button classe.Click is used as a field within the Button class. Como demonstra o exemplo, o campo pode ser examinado, modificado e usado em expressões de invocação de delegado.As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. O OnClick método Click na classe "gera" o evento. ButtonThe OnClick method in the Button class "raises" the Click event. A noção de gerar um evento é precisamente equivalente a invocar o delegado representado pelo evento — assim, não há constructos de linguagem especial para gerar 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. Observe que a invocação de delegado é precedida por uma verificação que garante que o delegado não seja nulo.Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

Button Fora da declaração da classe, o Click membro só pode ser usado no lado esquerdo dos += operadores e -= , como emOutside 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 acrescenta um delegado à lista de invocação do Click evento ewhich appends a delegate to the invocation list of the Click event, and

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

que remove um delegado da lista de invocação do Click evento.which removes a delegate from the invocation list of the Click event.

Ao compilar um evento do tipo campo, o compilador cria automaticamente o armazenamento para manter o delegado e cria acessadores para o evento que adiciona ou remove manipuladores de eventos ao campo 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. As operações de adição e remoção são thread-safe e podem (mas não são necessárias) serem realizadas enquanto mantém o bloqueio (a instrução Lock) no objeto recipiente para um evento de instância ou o tipo Object (expressões de criação de objeto anônimo) para um 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.

Assim, uma declaração de evento de instância do formulário:Thus, an instance event declaration of the form:

class X
{
    public event D Ev;
}

será compilado para algo equivalente a: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 da classe X, referências à Ev no lado esquerdo dos += operadores e -= fazem com que os acessadores adicionar e remover sejam invocados.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 as outras referências Ev a são compiladas para referenciar __Ev o campo oculto em vez disso (acesso de membro).All other references to Ev are compiled to reference the hidden field __Ev instead (Member access). O nome "__Ev" é arbitrário; o campo oculto pode ter qualquer nome ou nenhum nome.The name "__Ev" is arbitrary; the hidden field could have any name or no name at all.

Acessadores de eventosEvent accessors

As declarações de evento normalmente omitem event_accessor_declarations, como no exemplo Button acima.Event declarations typically omit event_accessor_declarations, as in the Button example above. Uma situação para isso envolve o caso em que o custo de armazenamento de um campo por evento não é aceitável.One situation for doing so involves the case in which the storage cost of one field per event is not acceptable. Nesses casos, uma classe pode incluir event_accessor_declarations e usar um mecanismo privado para armazenar a lista de manipuladores de eventos.In such cases, a class can include event_accessor_declarations and use a private mechanism for storing the list of event handlers.

O event_accessor_declarations de um evento especifica as instruções Executáveis associadas à adição e remoção de manipuladores de eventos.The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers.

As declarações de acessador consistem em um add_accessor_declaration e um remove_accessor_declaration.The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Cada declaração de acessador consiste add no remove token ou seguido por um bloco.Each accessor declaration consists of the token add or remove followed by a block. O bloco associado a um add_accessor_declaration especifica as instruções a serem executadas quando um manipulador de eventos é adicionado e o bloco associado a um remove_accessor_declaration especifica as instruções a serem executadas Quando um manipulador de eventos é removido.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 e remove_accessor_declaration corresponde a um método com um parâmetro de valor único do tipo de evento e um tipo de retorno void.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. O parâmetro implícito de um acessador de valueeventos é nomeado.The implicit parameter of an event accessor is named value. Quando um evento é usado em uma atribuição de evento, o acessador de evento apropriado é usado.When an event is used in an event assignment, the appropriate event accessor is used. Especificamente, se o operador de atribuição += for, o acessador Add será usado e, se o -= operador de atribuição for, o acessador de remoção será usado.Specifically, if the assignment operator is += then the add accessor is used, and if the assignment operator is -= then the remove accessor is used. Em ambos os casos, o operando do lado direito do operador de atribuição é usado como o argumento para o acessador de eventos.In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. O bloco de um add_accessor_declaration ou remove_accessor_declaration deve estar em conformidade com as regras para os métodos void descritos no corpo do 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. Em particular, return instruções nesse bloco não são permitidas para especificar uma expressão.In particular, return statements in such a block are not permitted to specify an expression.

Como um acessador de evento implicitamente tem valueum parâmetro chamado, ele é um erro de tempo de compilação para uma variável local ou constante declarada em um acessador de evento para ter esse nome.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.

No exemploIn 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);
    }
}

a Control classe implementa um mecanismo de armazenamento interno para eventos.the Control class implements an internal storage mechanism for events. O AddEventHandler método associa um valor delegado a uma chave, o GetEventHandler método retorna o delegado atualmente associado a uma chave e o RemoveEventHandler método Remove um delegado como um manipulador de eventos para o 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. Supostamente, o mecanismo de armazenamento subjacente foi projetado de forma que não há nenhum custo para associar um null valor delegado a uma chave e, portanto, eventos sem tratamento não consomem nenhum armazenamento.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 e de instânciaStatic and instance events

Quando uma declaração de evento inclui static um modificador, o evento é considerado um evento estático.When an event declaration includes a static modifier, the event is said to be a static event. Quando nenhum static modificador está presente, o evento é considerado um evento de instância.When no static modifier is present, the event is said to be an instance event.

Um evento estático não está associado a uma instância específica e é um erro de tempo de compilação para se referir this aos acessadores de um 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.

Um evento de instância é associado a uma determinada instância de uma classe, e essa instância pode ser acessada como this (esse acesso) nos acessadores desse 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.

Quando um evento é referenciado em um member_access (acesso de membro) do formulário E.M, se M for um evento estático, E deverá indicar um tipo contendo M e se M for um evento de instância, e deverá indicar uma instância de um tipo que contém 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.

As diferenças entre os membros estático e de instância são discutidas mais detalhadamente em membros estáticos e de instância.The differences between static and instance members are discussed further in Static and instance members.

Acessadores de evento virtual, sealed, override e abstractVirtual, sealed, override, and abstract event accessors

Uma virtual declaração de evento especifica que os acessadores desse evento são virtuais.A virtual event declaration specifies that the accessors of that event are virtual. O virtual modificador se aplica a ambos os acessadores de um evento.The virtual modifier applies to both accessors of an event.

Uma abstract declaração de evento especifica que os acessadores do evento são virtuais, mas não fornecem uma implementação real dos acessadores.An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. Em vez disso, as classes derivadas não abstratas são necessárias para fornecer sua própria implementação para os acessadores, substituindo o evento.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. Como uma declaração de evento abstract não fornece implementação real, ela não pode fornecer event_accessor_declarationsdelimitado por chaves.Because an abstract event declaration provides no actual implementation, it cannot provide brace-delimited event_accessor_declarations.

Uma declaração de evento que inclui os abstract modificadores e override especifica que o evento é abstrato e substitui um evento base.An event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. Os acessadores de tal evento também são abstratos.The accessors of such an event are also abstract.

As declarações de evento abstract só são permitidas em classes abstratas (classes abstratas).Abstract event declarations are only permitted in abstract classes (Abstract classes).

Os acessadores de um evento virtual herdado podem ser substituídos em uma classe derivada, incluindo uma declaração de override evento que especifica um 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. Isso é conhecido como uma declaração de evento de substituição.This is known as an overriding event declaration. Uma declaração de evento de substituição não declara um novo evento.An overriding event declaration does not declare a new event. Em vez disso, ele simplesmente especializa as implementações dos acessadores de um evento virtual existente.Instead, it simply specializes the implementations of the accessors of an existing virtual event.

Uma declaração de evento de substituição deve especificar exatamente os mesmos modificadores de acessibilidade, tipo e nome que o evento substituído.An overriding event declaration must specify the exact same accessibility modifiers, type, and name as the overridden event.

Uma declaração de evento de substituição pode sealed incluir o modificador.An overriding event declaration may include the sealed modifier. O uso desse modificador impede que uma classe derivada substitua o evento.Use of this modifier prevents a derived class from further overriding the event. Os acessadores de um evento lacrado também são lacrados.The accessors of a sealed event are also sealed.

É um erro de tempo de compilação para uma declaração de evento de substituição para new incluir um modificador.It is a compile-time error for an overriding event declaration to include a new modifier.

Exceto pelas diferenças na sintaxe de declaração e invocação, os acessadores virtual, sealed, override e abstract se comportam exatamente como métodos virtuais, lacrados, de substituição e abstratos.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Especificamente, as regras descritas em métodos virtuais, métodos de substituição, métodos lacradose métodos abstratos se aplicam como se os acessadores fossem métodos de um formulário correspondente.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 acessador corresponde a um método com um parâmetro de valor único do tipo de void evento, um tipo de retorno e os mesmos modificadores que o evento recipiente.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.

IndexadoresIndexers

Um indexador é um membro que permite que um objeto seja indexado da mesma maneira que uma matriz.An indexer is a member that enables an object to be indexed in the same way as an array. Indexadores são declarados usando 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 ';'
    ;

Um indexer_declaration pode incluir um conjunto de atributos (atributos) e uma combinação válida dos quatro modificadores de acesso (modificadores de acesso), o new (o novo modificador), virtual (métodos virtuais ), override (métodos de substituição), 0(métodos lacrados), os modificadores 2 (métodos abstratos) e 4 (métodos externos).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.

As declarações do indexador estão sujeitas às mesmas regras que as declarações de método (métodos) em relação a combinações válidas de modificadores, com a única exceção de que o modificador estático não é permitido em uma declaração de indexador.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.

Os modificadores virtual, overridee abstract são mutuamente exclusivos, exceto em um caso.The modifiers virtual, override, and abstract are mutually exclusive except in one case. Os abstract modificadores e override podem ser usados juntos para que um indexador abstrato possa substituir um virtual.The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one.

O tipo de uma declaração de indexador especifica o tipo de elemento do indexador introduzido pela declaração.The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. A menos que o indexador seja uma implementação de membro de interface explícita, o tipo é thisseguido pela palavra-chave.Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. Para uma implementação de membro de interface explícita, o tipo é seguido por um interface_type, um "." e a palavra-chave this.For an explicit interface member implementation, the type is followed by an interface_type, a ".", and the keyword this. Ao contrário de outros membros, os indexadores não têm nomes definidos pelo usuário.Unlike other members, indexers do not have user-defined names.

O formal_parameter_list especifica os parâmetros do indexador.The formal_parameter_list specifies the parameters of the indexer. A lista de parâmetros formais de um indexador corresponde ao de um método (parâmetros de método), exceto pelo menos um parâmetro que deve ser especificado e que ref os out modificadores e de parâmetro não são permitidos.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.

O tipo de um indexador e cada um dos tipos referenciados no formal_parameter_list devem ser pelo menos tão acessíveis quanto o próprio indexador (restrições de acessibilidade).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).

Um indexer_body pode consistir em um corpo de acessador ou um corpo de expressão.An indexer_body may either consist of an accessor body or an expression body. Em um corpo de acessador, accessor_declarations, que deve ser incluído nos tokens "{" e "}", declare os acessadores (acessadores) da propriedade.In an accessor body, accessor_declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Os acessadores especificam as instruções Executáveis associadas à leitura e gravação da propriedade.The accessors specify the executable statements associated with reading and writing the property.

Um corpo de expressão que consiste em=>"" seguido por uma E expressão e um ponto-e-vírgula é exatamente { get { return E; } }equivalente ao corpo da instrução e, portanto, só pode ser usado para especificar indexadores somente getter em que o resultado do getter é fornecido por uma única expressão.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.

Embora a sintaxe para acessar um elemento do indexador seja a mesma que para um elemento de matriz, um elemento de indexador não é classificado como uma variável.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. Portanto, não é possível passar um elemento do indexador como um ref argumento ou. outThus, it is not possible to pass an indexer element as a ref or out argument.

A lista de parâmetros formais de um indexador define a assinatura (assinaturas e sobrecarga) do indexador.The formal parameter list of an indexer defines the signature (Signatures and overloading) of the indexer. Especificamente, a assinatura de um indexador consiste no número e tipos de seus parâmetros formais.Specifically, the signature of an indexer consists of the number and types of its formal parameters. O tipo de elemento e os nomes dos parâmetros formais não fazem parte da assinatura de um indexador.The element type and names of the formal parameters are not part of an indexer's signature.

A assinatura de um indexador deve ser diferente das assinaturas de todos os outros indexadores declarados na mesma classe.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

Indexadores e propriedades são muito semelhantes em conceito, mas diferem das seguintes maneiras:Indexers and properties are very similar in concept, but differ in the following ways:

  • Uma propriedade é identificada por seu nome, enquanto um indexador é identificado por sua assinatura.A property is identified by its name, whereas an indexer is identified by its signature.
  • Uma propriedade é acessada por meio de um Simple_name (nomes simples) ou um member_access (acesso de membro), enquanto um elemento de indexador é acessado por meio de um element_access (acesso do indexador).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).
  • Uma propriedade pode ser um static membro, enquanto um indexador é sempre um membro de instância.A property can be a static member, whereas an indexer is always an instance member.
  • Um get acessador de uma propriedade corresponde a um método sem parâmetros, enquanto get um acessador de um indexador corresponde a um método com a mesma lista de parâmetros formais que o indexador.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.
  • Um set acessador de uma propriedade corresponde a um método com um único valueparâmetro chamado, set enquanto que um acessador de um indexador corresponde a um método com a mesma lista de parâmetros formais que o indexador, além de um parâmetro adicional nomeado 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.
  • É um erro de tempo de compilação para um acessador de indexador declarar uma variável local com o mesmo nome que um parâmetro de indexador.It is a compile-time error for an indexer accessor to declare a local variable with the same name as an indexer parameter.
  • Em uma declaração de propriedade de substituição, a propriedade herdada é acessada P usando a sintaxe base.P, onde é o nome da propriedade.In an overriding property declaration, the inherited property is accessed using the syntax base.P, where P is the property name. Em uma declaração de substituição do indexador, o indexador herdado é acessado E usando a sintaxe base[E], em que é uma lista separada por vírgulas de expressões.In an overriding indexer declaration, the inherited indexer is accessed using the syntax base[E], where E is a comma separated list of expressions.
  • Não há nenhum conceito de "indexador implementado automaticamente".There is no concept of an "automatically implemented indexer". É um erro ter um indexador não-abstrato e não externo com acessadores de ponto e vírgula.It is an error to have a non-abstract, non-external indexer with semicolon accessors.

Além dessas diferenças, todas as regras definidas em acessadores e Propriedades implementadas automaticamente se aplicam a acessadores indexadores, bem como a acessadores de propriedade.Aside from these differences, all rules defined in Accessors and Automatically implemented properties apply to indexer accessors as well as to property accessors.

Quando uma declaração de indexador inclui extern um modificador, o indexador é considerado um indexador externo.When an indexer declaration includes an extern modifier, the indexer is said to be an external indexer. Como uma declaração externa do indexador não fornece nenhuma implementação real, cada uma de suas accessor_declarations consiste em um ponto-e-vírgula.Because an external indexer declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.

O exemplo a seguir declara uma BitArray classe que implementa um indexador para acessar os bits individuais na 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);
            }
        }
    }
}

Uma instância da BitArray classe consome substancialmente menos memória do que uma correspondente bool[] (já que cada valor do primeiro ocupa apenas um bit em vez do último byte), mas permite as mesmas operações que um. 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[].

A classe CountPrimes a seguir usa BitArray um e o algoritmo clássico "sieve" para calcular o número de primos entre 1 e um determinado máximo: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);
    }
}

Observe que a sintaxe para acessar elementos do BitArray é exatamente a mesma do para um. bool[]Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].

O exemplo a seguir mostra uma classe de grade 26 * 10 que tem um indexador com dois parâmetros.The following example shows a 26 * 10 grid class that has an indexer with two parameters. O primeiro parâmetro deve ser uma letra maiúscula ou minúscula no intervalo de A-Z e o segundo deve ser um número inteiro no intervalo de 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 do indexadorIndexer overloading

As regras de resolução de sobrecarga do indexador são descritas em inferência de tipos.The indexer overload resolution rules are described in Type inference.

OperadoresOperators

Um operador é um membro que define o significado de um operador de expressão que pode ser aplicado a instâncias da classe.An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. Os operadores são declarados usando 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 ';'
    | ';'
    ;

Há três categorias de operadores que podem ser sobrecarregados: Operadores unários (operadores unários), operadores binários (operadores binários) e operadores de conversão (operadores de conversão).There are three categories of overloadable operators: Unary operators (Unary operators), binary operators (Binary operators), and conversion operators (Conversion operators).

O operator_body é um ponto e vírgula, um corpo de instrução ou um corpo de expressão.The operator_body is either a semicolon, a statement body or an expression body. Um corpo de instrução consiste em um bloco, que especifica as instruções a serem executadas quando o operador é invocado.A statement body consists of a block, which specifies the statements to execute when the operator is invoked. O bloco deve estar em conformidade com as regras para métodos de retorno de valor descritos no corpo do método.The block must conform to the rules for value-returning methods described in Method body. Um corpo de => expressão consiste em seguido por uma expressão e um ponto e vírgula e denota uma única expressão a ser executada quando o operador é invocado.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 operadores extern, o operator_body consiste apenas de um ponto e vírgula.For extern operators, the operator_body consists simply of a semicolon. Para todos os outros operadores, o operator_body é um corpo de bloco ou um corpo de expressão.For all other operators, the operator_body is either a block body or an expression body.

As regras a seguir se aplicam a todas as declarações de operador:The following rules apply to all operator declarations:

  • Uma declaração de operador deve incluir um public e um static modificador.An operator declaration must include both a public and a static modifier.
  • Os parâmetros de um operador devem ser parâmetros de valor (parâmetros devalor).The parameter(s) of an operator must be value parameters (Value parameters). É um erro de tempo de compilação para uma declaração de operador especificar ref ou out parâmetros.It is a compile-time error for an operator declaration to specify ref or out parameters.
  • A assinatura de um operador (operadores unários, operadores binários, operadores de conversão) deve ser diferente das assinaturas de todos os outros operadores declarados na mesma classe.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 os tipos referenciados em uma declaração de operador devem ser pelo menos tão acessíveis quanto o próprio operador (restrições de acessibilidade).All types referenced in an operator declaration must be at least as accessible as the operator itself (Accessibility constraints).
  • É um erro para que o mesmo modificador apareça várias vezes em uma declaração de operador.It is an error for the same modifier to appear multiple times in an operator declaration.

Cada categoria de operador impõe restrições adicionais, conforme descrito nas seções a seguir.Each operator category imposes additional restrictions, as described in the following sections.

Como outros membros, os operadores declarados em uma classe base são herdados por classes derivadas.Like other members, operators declared in a base class are inherited by derived classes. Como as declarações de operador sempre exigem a classe ou struct em que o operador é declarado para participar da assinatura do operador, não é possível que um operador declarado em uma classe derivada oculte um operador declarado em uma classe 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. Assim, o new modificador nunca é necessário e, portanto, nunca é permitido em uma declaração de operador.Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

Informações adicionais sobre operadores unários e binários podem ser encontradas em operadores.Additional information on unary and binary operators can be found in Operators.

Informações adicionais sobre operadores de conversão podem ser encontradas em conversões definidas pelo usuário.Additional information on conversion operators can be found in User-defined conversions.

Operadores unáriosUnary operators

As regras a seguir se aplicam a declarações de T operador unários, onde denota o tipo de instância da classe ou struct que contém a declaração do 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:

  • Um operador + -unário ~ T ,, ou deve usar um único parâmetro do tipo ou T? pode retornar qualquer tipo. !A unary +, -, !, or ~ operator must take a single parameter of type T or T? and can return any type.
  • Um operador ++ or -- unário deve usar um único parâmetro do T tipo T? ou deve retornar o mesmo tipo ou um tipo derivado dele.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.
  • Um operador true or false unário deve usar um único parâmetro do T tipo T? ou deve retornar o booltipo.A unary true or false operator must take a single parameter of type T or T? and must return type bool.

A assinatura de um operador unário consiste no token do operador+( - !, -- ~ ++,,,, true, ou false) e no tipo do único 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. O tipo de retorno não faz parte da assinatura de um operador unário, nem é o nome do parâmetro formal.The return type is not part of a unary operator's signature, nor is the name of the formal parameter.

Os true operadores false e unários exigem declarações emparelhadas.The true and false unary operators require pair-wise declaration. Ocorrerá um erro em tempo de compilação se uma classe declarar um desses operadores sem também declarar o outro.A compile-time error occurs if a class declares one of these operators without also declaring the other. Os true operadores false e são descritos mais detalhadamente em operadores lógicos condicionais definidos pelo usuário e expressões booleanas.The true and false operators are described further in User-defined conditional logical operators and Boolean expressions.

O exemplo a seguir mostra uma implementação e o uso operator ++ subsequente de para uma classe de vetor de inteiro: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
    }
}

Observe como o método Operator retorna o valor produzido pela adição de 1 ao operando, assim como os operadores de incremento de sufixo e decréscimo (incremento de sufixo e diminuição de operadores) e os operadores de incremento de prefixo e decréscimo (prefixo operadores de incremento e decréscimo).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). Diferentemente C++do no, esse método não precisa modificar o valor de seu operando diretamente.Unlike in C++, this method need not modify the value of its operand directly. Na verdade, modificar o valor do operando violaria a semântica padrão do operador de incremento de sufixo.In fact, modifying the operand value would violate the standard semantics of the postfix increment operator.

Operadores bináriosBinary operators

As regras a seguir se aplicam a declarações de T operador binários, onde denota o tipo de instância da classe ou struct que contém a declaração do 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:

  • Um operador binário non-Shift deve usar dois parâmetros, pelo menos um dos quais deve ter tipo T ou T?, e pode retornar qualquer 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.
  • Um operador << ou >> binário deve usar dois parâmetros, o primeiro deles deve ter o tipo T ou T? e o segundo deve ter o tipo int ou int?, e pode retornar qualquer 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.

A assinatura de um operador binário consiste no token do operador (+ *, - & | / % ,,^,,,,,,, >> << == ,,,<=, ou>=) e os tipos dos dois parâmetros formais. < != >The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. O tipo de retorno e os nomes dos parâmetros formais não fazem parte da assinatura de um operador binário.The return type and the names of the formal parameters are not part of a binary operator's signature.

Determinados operadores binários exigem declarações emparelhadas.Certain binary operators require pair-wise declaration. Para cada declaração de qualquer operador de um par, deve haver uma declaração correspondente do outro operador do par.For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. Duas declarações de operador correspondem quando têm o mesmo tipo de retorno e o mesmo tipo para cada parâmetro.Two operator declarations match when they have the same return type and the same type for each parameter. Os seguintes operadores exigem a declaração de pares:The following operators require pair-wise declaration:

  • operator == e operator !=operator == and operator !=
  • operator > e operator <operator > and operator <
  • operator >= e operator <=operator >= and operator <=

Operadores de conversãoConversion operators

Uma declaração de operador de conversão apresenta uma conversão definida pelo usuário (conversões definidas pelo usuário) que aumenta as conversões implícitas e explícitas predefinidas.A conversion operator declaration introduces a user-defined conversion (User-defined conversions) which augments the pre-defined implicit and explicit conversions.

Uma declaração de operador de conversão que implicit inclui a palavra-chave apresenta uma conversão implícita definida pelo usuário.A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Conversões implícitas podem ocorrer em várias situações, incluindo invocações de membro de função, expressões de conversão e atribuições.Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. Isso é descrito mais detalhadamente em conversões implícitas.This is described further in Implicit conversions.

Uma declaração de operador de conversão que explicit inclui a palavra-chave apresenta uma conversão explícita definida pelo usuário.A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Conversões explícitas podem ocorrer em expressões de conversão e são descritas em conversões explícitas.Explicit conversions can occur in cast expressions, and are described further in Explicit conversions.

Um operador de conversão converte de um tipo de origem, indicado pelo tipo de parâmetro do operador de conversão, em um tipo de destino, indicado pelo tipo de retorno do operador de conversão.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 um S determinado tipo de origem e tipo Tde destino, se S ou T forem tipos anuláveis T0 , avise S0 e faça referência aos seus S0 tipos T0 subjacentes, caso contrário, e serão igual a S e 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. Uma classe ou estrutura tem permissão para declarar uma conversão de um tipo S de origem para um tipo T de destino somente se todas as seguintes opções forem verdadeiras: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:

  • S0e T0 são tipos diferentes.S0 and T0 are different types.
  • S0 OuT0 é o tipo de classe ou struct no qual a declaração do operador ocorre.Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • Nem S0 nem T0 é um interface_type.Neither S0 nor T0 is an interface_type.
  • Excluindo conversões definidas S pelo usuário, uma conversão não existe de T ou T Spara.Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

Para os fins dessas regras, quaisquer parâmetros de tipo associados S a ou T são considerados como tipos exclusivos que não têm nenhuma relação de herança com outros tipos, e quaisquer restrições nesses parâmetros de tipo são ignoradas.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.

No exemploIn 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
}

as primeiras duas declarações de operador são permitidas porque, para fins de indexadores3 T e int , string respectivamente, são consideradas tipos exclusivos sem nenhuma relação.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. No entanto, o terceiro operador é um C<T> erro porque é a classe D<T>base de.However, the third operator is an error because C<T> is the base class of D<T>.

Da segunda regra, ela segue que um operador de conversão deve converter de ou para o tipo class ou struct no qual o operador é declarado.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 exemplo, é C possível que um tipo de classe ou estrutura defina uma conversão de C para int e int de Cpara, mas não de int para 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.

Não é possível redefinir diretamente uma conversão predefinida.It is not possible to directly redefine a pre-defined conversion. Assim, os operadores de conversão não têm permissão para converter de object ou para porque as conversões implícitas e explícitas já existem entre object o e todos os outros 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. Da mesma forma, nem os tipos de origem nem de destino de uma conversão podem ser um tipo base do outro, já que uma conversão já existirá.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.

No entanto, é possível declarar operadores em tipos genéricos que, para argumentos de tipo específicos, especificar conversões que já existem como conversões 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. No exemploIn the example

struct Convertible<T>
{
    public static implicit operator Convertible<T>(T value) {...}
    public static explicit operator T(Convertible<T> value) {...}
}

Quando Type object é especificado como um argumento de tipo Tpara, o segundo operador declara uma conversão que já existe (um implícito e, portanto, também uma conversão explícita existe de qualquer tipo para 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).

Nos casos em que existe uma conversão predefinida entre dois tipos, todas as conversões definidas pelo usuário entre esses tipos serão ignoradas.In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. Especificamente:Specifically:

  • Se uma conversão implícita predefinida (conversões implícitas) S existir de tipo para tipo T, todas as conversões definidas pelo usuário (implícitas ou explícitas) S de T para serão ignoradas.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.
  • Se uma conversão explícita predefinida (conversões explícitas) S existir de tipo para tipo T, todas as conversões explícitas definidas pelo usuário do S para T serão ignoradas.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. AlémFurthermore:

Se T for um tipo de interface, conversões implícitas definidas pelo usuário S de T para serão ignoradas.If T is an interface type, user-defined implicit conversions from S to T are ignored.

Caso contrário, conversões implícitas definidas pelo usuário S de T para o ainda serão consideradas.Otherwise, user-defined implicit conversions from S to T are still considered.

Para todos os tipos object, mas os operadores declarados Convertible<T> pelo tipo acima não entram em conflito com conversões predefinidas.For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions. Por exemplo: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
}

No entanto, objectpara conversões de tipo predefinidas, oculte as conversões definidas pelo usuário em todos os casos, exceto uma: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
}

As conversões definidas pelo usuário não têm permissão para converter de ou para interface_types.User-defined conversions are not allowed to convert from or to interface_types. Em particular, essa restrição garante que nenhuma transformação definida pelo usuário ocorra durante a conversão para um interface_typee que uma conversão em um interface_type só terá sucesso se o objeto que está sendo convertido realmente implementar o interface_typeespecificado.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.

A assinatura de um operador de conversão consiste no tipo de origem e no tipo de destino.The signature of a conversion operator consists of the source type and the target type. (Observe que essa é a única forma de membro para a qual o tipo de retorno participa na assinatura.) A implicit classificação explicit ou de um operador de conversão não faz parte da assinatura do 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. Assim, uma classe ou struct não pode declarar um implicit operador de explicit conversão e um com os mesmos tipos de origem e destino.Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

Em geral, as conversões implícitas definidas pelo usuário devem ser projetadas para nunca gerar exceções e nunca perderem informações.In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. Se uma conversão definida pelo usuário puder dar origem às exceções (por exemplo, porque o argumento de origem está fora do intervalo) ou a perda de informações (como descartar bits de ordem superior), essa conversão deve ser definida como uma conversão 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.

No exemploIn 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);
    }
}

a conversão de Digit para byte é implícita porque ela nunca gera exceções ou perde informações, mas a conversão de byte para Digit é explícita, Digit já que só pode representar um subconjunto do possível valores de a 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.

Construtores de instânciaInstance constructors

Um construtor de instância é um membro que implementa as ações necessárias para inicializar uma instância de uma classe.An instance constructor is a member that implements the actions required to initialize an instance of a class. Construtores de instância são declarados usando 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
    | ';'
    ;

Um constructor_declaration pode incluir um conjunto de atributos (atributos), uma combinação válida dos quatro modificadores de acesso (modificadores de acesso) e um modificador de extern (métodos externos).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. Uma declaração de construtor não tem permissão para incluir o mesmo modificador várias vezes.A constructor declaration is not permitted to include the same modifier multiple times.

O identificador de um constructor_declarator deve nomear a classe na qual o construtor de instância é declarado.The identifier of a constructor_declarator must name the class in which the instance constructor is declared. Se qualquer outro nome for especificado, ocorrerá um erro em tempo de compilação.If any other name is specified, a compile-time error occurs.

O formal_parameter_list opcional de um construtor de instância está sujeito às mesmas regras que o formal_parameter_list de um 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). A lista de parâmetros formais define a assinatura (assinaturas e sobrecarga) de um construtor de instância e governa o processo pelo qual a resolução de sobrecarga (inferência de tipos) seleciona um construtor de instância específico em uma invocação.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 um dos tipos referenciados no formal_parameter_list de um construtor de instância deve ser pelo menos acessível como o próprio Construtor (restrições de acessibilidade).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).

O constructor_initializer opcional especifica outro construtor de instância para invocar antes de executar as instruções fornecidas no constructor_body deste construtor de instância.The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. Isso é descrito mais detalhadamente em inicializadores de Construtor.This is described further in Constructor initializers.

Quando uma declaração de Construtor inclui extern um modificador, o construtor é considerado um Construtor externo.When a constructor declaration includes an extern modifier, the constructor is said to be an external constructor. Como uma declaração de Construtor externo não fornece nenhuma implementação real, seu constructor_body consiste em um ponto-e-vírgula.Because an external constructor declaration provides no actual implementation, its constructor_body consists of a semicolon. Para todos os outros construtores, o constructor_body consiste em um bloco que especifica as instruções para inicializar uma nova instância da classe.For all other constructors, the constructor_body consists of a block which specifies the statements to initialize a new instance of the class. Isso corresponde exatamente ao bloco de um método de instância com um void tipo de retorno (corpo do método).This corresponds exactly to the block of an instance method with a void return type (Method body).

Construtores de instância não são herdados.Instance constructors are not inherited. Portanto, uma classe não tem construtores de instância diferentes daqueles realmente declarados na classe.Thus, a class has no instance constructors other than those actually declared in the class. Se uma classe não contiver nenhuma declaração de construtor de instância, um construtor de instância padrão será fornecido automaticamente (construtores padrão).If a class contains no instance constructor declarations, a default instance constructor is automatically provided (Default constructors).

Os construtores de instância são invocados por object_creation_expressions (expressões de criação de objeto) e por meio de constructor_initializers.Instance constructors are invoked by object_creation_expressions (Object creation expressions) and through constructor_initializers.

Inicializadores de construtoresConstructor initializers

Todos os construtores de instância (exceto aqueles para a classe object) incluem implicitamente uma invocação de outro construtor de instância imediatamente antes do constructor_body.All instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. O construtor para invocar implicitamente é determinado pelo constructor_initializer:The constructor to implicitly invoke is determined by the constructor_initializer:

  • Um inicializador de construtor de instância base(argument_list) do base() formulário ou faz com que um construtor de instância da classe base direta seja invocado.An instance constructor initializer of the form base(argument_list) or base() causes an instance constructor from the direct base class to be invoked. Esse construtor é selecionado usando argument_list , se presente, e as regras de resolução de sobrecarga da resolução de sobrecarga.That constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. O conjunto de construtores de instância de candidato consiste em todos os construtores de instância acessíveis contidos na classe base direta ou no construtor padrão (construtores padrão), se nenhum construtor de instância for declarado na classe base direta.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. Se esse conjunto estiver vazio, ou se um único Construtor de instância recomendada não puder ser identificado, ocorrerá um erro em tempo de compilação.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.
  • Um inicializador de construtor de instância this(argument-list) do this() formulário ou faz com que um construtor de instância da própria classe seja invocado.An instance constructor initializer of the form this(argument-list) or this() causes an instance constructor from the class itself to be invoked. O construtor é selecionado usando argument_list , se presente, e as regras de resolução de sobrecarga da resolução de sobrecarga.The constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. O conjunto de construtores de instância de candidato consiste em todos os construtores de instância acessíveis declarados na própria classe.The set of candidate instance constructors consists of all accessible instance constructors declared in the class itself. Se esse conjunto estiver vazio, ou se um único Construtor de instância recomendada não puder ser identificado, ocorrerá um erro em tempo de compilação.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. Se uma declaração de construtor de instância incluir um inicializador de construtor que invoca o Construtor em si, ocorrerá um erro em tempo de compilação.If an instance constructor declaration includes a constructor initializer that invokes the constructor itself, a compile-time error occurs.

Se um construtor de instância não tiver nenhum inicializador de construtor, um inicializador de construtor do formulário base() será fornecido implicitamente.If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. Portanto, uma declaração de construtor de instância do formulárioThus, an instance constructor declaration of the form

C(...) {...}

é exatamente equivalente ais exactly equivalent to

C(...): base() {...}

O escopo dos parâmetros fornecidos pelo formal_parameter_list de uma declaração de construtor de instância inclui o inicializador de construtor dessa declaração.The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. Assim, um inicializador de construtor tem permissão para acessar os parâmetros do construtor.Thus, a constructor initializer is permitted to access the parameters of the constructor. Por exemplo:For example:

class A
{
    public A(int x, int y) {}
}

class B: A
{
    public B(int x, int y): base(x + y, x - y) {}
}

Um inicializador de construtor de instância não pode acessar a instância que está sendo criada.An instance constructor initializer cannot access the instance being created. Portanto, é um erro de tempo de compilação para referenciar this em uma expressão de argumento do inicializador de construtor, pois é um erro de tempo de compilação para uma expressão de argumento referenciar qualquer membro de instância por meio de um 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 variável de instânciaInstance variable initializers

Quando um construtor de instância não tem nenhum inicializador de construtor ou tem um inicializador de construtor do formulário base(...), esse construtor executa implicitamente as inicializações especificadas pelos variable_initializers dos campos de instância declarados em sua classe.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. Isso corresponde a uma sequência de atribuições que são executadas imediatamente após a entrada para o construtor e antes da invocação implícita do construtor da classe base direta.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. Os inicializadores de variável são executados na ordem textual em que aparecem na declaração de classe.The variable initializers are executed in the textual order in which they appear in the class declaration.

Execução do ConstrutorConstructor execution

Inicializadores de variáveis são transformados em instruções de atribuição, e essas instruções de atribuição são executadas antes da invocação do construtor da instância da classe base.Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. Essa ordenação garante que todos os campos de instância sejam inicializados por seus inicializadores variáveis antes que qualquer instrução que tenha acesso a essa instância seja executada.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 o exemploGiven 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);
    }
}

Quando new B() é usado para criar uma instância do B, a seguinte saída é produzida:when new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

O valor de x é 1 porque o inicializador de variável é executado antes que o construtor da instância da classe base seja invocado.The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. No entanto, o y valor de é 0 (o valor padrão intde um) porque a y atribuição a não é executada até que o construtor da classe base seja retornado.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.

É útil considerar inicializadores de variável de instância e inicializadores de construtor como instruções que são inseridas automaticamente antes de constructor_body.It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor_body. O exemploThe 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;
    }
}

contém vários inicializadores de variável; Ele também contém inicializadores de construtor de ambos osbase formulários this(e).contains several variable initializers; it also contains constructor initializers of both forms (base and this). O exemplo corresponde ao código mostrado abaixo, onde cada comentário indica uma instrução inserida automaticamente (a sintaxe usada para as invocações de Construtor inseridas automaticamente não é válida, mas meramente serve para ilustrar o 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;
    }
}

Construtores padrãoDefault constructors

Se uma classe não contiver nenhuma declaração de construtor de instância, um construtor de instância padrão será fornecido automaticamente.If a class contains no instance constructor declarations, a default instance constructor is automatically provided. Esse construtor padrão simplesmente invoca o construtor sem parâmetros da classe base direta.That default constructor simply invokes the parameterless constructor of the direct base class. Se a classe for abstrata, a acessibilidade declarada para o construtor padrão será protegida.If the class is abstract then the declared accessibility for the default constructor is protected. Caso contrário, a acessibilidade declarada para o construtor padrão é pública.Otherwise, the declared accessibility for the default constructor is public. Portanto, o construtor padrão sempre está no formatoThus, the default constructor is always of the form

protected C(): base() {}

ouor

public C(): base() {}

em C que é o nome da classe.where C is the name of the class. Se a resolução de sobrecarga não puder determinar um melhor candidato exclusivo para o inicializador de construtor da classe base, ocorrerá um erro em tempo de compilação.If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs.

No exemploIn the example

class Message
{
    object sender;
    string text;
}

um construtor padrão é fornecido porque a classe não contém nenhuma declaração de construtor de instância.a default constructor is provided because the class contains no instance constructor declarations. Portanto, o exemplo é precisamente equivalente aThus, the example is precisely equivalent to

class Message
{
    object sender;
    string text;

    public Message(): base() {}
}

Construtores particularesPrivate constructors

Quando uma classe T declara apenas construtores de instância privada, não é possível para classes fora do texto do programa de T para derivar de T ou para criar instâncias diretamente do 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. Portanto, se uma classe contiver apenas membros estáticos e não tiver a intenção de ser instanciada, a adição de um construtor de instância particular vazio impedirá a instanciação.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 exemplo: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) {...}
}

A Trig classe agrupa métodos e constantes relacionados, mas não deve ser instanciada.The Trig class groups related methods and constants, but is not intended to be instantiated. Portanto, ele declara um único Construtor de instância particular vazio.Therefore it declares a single empty private instance constructor. Pelo menos um construtor de instância deve ser declarado para suprimir a geração automática de um construtor padrão.At least one instance constructor must be declared to suppress the automatic generation of a default constructor.

Parâmetros opcionais do construtor de instânciaOptional instance constructor parameters

A this(...) forma de inicializador de construtor é comumente usada em conjunto com sobrecarga para implementar parâmetros de construtor de instância opcionais.The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. No exemploIn 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
    }
}

os primeiros dois construtores de instância fornecem apenas os valores padrão para os argumentos ausentes.the first two instance constructors merely provide the default values for the missing arguments. Ambos usam um this(...) inicializador de construtor para invocar o terceiro construtor de instância, que realmente faz o trabalho de inicializar a nova instância.Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. O efeito é o dos parâmetros de Construtor opcionais: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");

Construtores estáticosStatic constructors

Um construtor estático é um membro que implementa as ações necessárias para inicializar um tipo de classe fechado.A static constructor is a member that implements the actions required to initialize a closed class type. Construtores estáticos são declarados usando 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
    | ';'
    ;

Um static_constructor_declaration pode incluir um conjunto de atributos (atributos) e um modificador de extern (métodos externos).A static_constructor_declaration may include a set of attributes (Attributes) and an extern modifier (External methods).

O identificador de um static_constructor_declaration deve nomear a classe na qual o construtor estático é declarado.The identifier of a static_constructor_declaration must name the class in which the static constructor is declared. Se qualquer outro nome for especificado, ocorrerá um erro em tempo de compilação.If any other name is specified, a compile-time error occurs.

Quando uma declaração de construtor estático inclui extern um modificador, o construtor estático é considerado um construtor estático externo.When a static constructor declaration includes an extern modifier, the static constructor is said to be an external static constructor. Como uma declaração de construtor estático externa não fornece nenhuma implementação real, seu static_constructor_body consiste em um ponto-e-vírgula.Because an external static constructor declaration provides no actual implementation, its static_constructor_body consists of a semicolon. Para todas as outras declarações de construtor estático, o static_constructor_body consiste em um bloco que especifica as instruções a serem executadas para inicializar a classe.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. Isso corresponde exatamente ao method_body de um método estático com um tipo de retorno void (corpo do método).This corresponds exactly to the method_body of a static method with a void return type (Method body).

Construtores estáticos não são herdados e não podem ser chamados diretamente.Static constructors are not inherited, and cannot be called directly.

O construtor estático para um tipo de classe fechada é executado no máximo uma vez em um determinado domínio de aplicativo.The static constructor for a closed class type executes at most once in a given application domain. A execução de um construtor estático é disparada pelo primeiro dos seguintes eventos para ocorrer dentro de um domínio de aplicativo:The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • Uma instância do tipo de classe é criada.An instance of the class type is created.
  • Qualquer um dos membros estáticos do tipo de classe é referenciado.Any of the static members of the class type are referenced.

Se uma classe contiver Main o método (inicialização do aplicativo) no qual a execução começa, o construtor estático para aquela classe é Main executado antes de o método ser chamado.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 um novo tipo de classe fechada, primeiro um novo conjunto de campos estáticos (campos estáticos e de instância) para esse tipo fechado específico é criado.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 um dos campos estáticos é inicializado para seu valor padrão (valores padrão).Each of the static fields is initialized to its default value (Default values). Em seguida, os inicializadores de campo estáticos (inicialização de campo estático) são executados para esses campos estáticos.Next, the static field initializers (Static field initialization) are executed for those static fields. Por fim, o construtor estático é executado.Finally, the static constructor is executed.

O exemploThe 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");
    }
}

deve produzir a saída:must produce the output:

Init A
A.F
Init B
B.F

Porque a execução do Aconstrutor estático do é disparada pela chamada A.Fpara, e a execução Bdo construtor estático do é disparada pela B.Fchamada para.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.

É possível construir dependências circulares que permitem que campos estáticos com inicializadores de variáveis sejam observados em seu estado de valor padrão.It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

O exemploThe 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);
    }
}

produz a saídaproduces the output

X = 1, Y = 2

Para executar o Main método, o sistema primeiro executa o inicializador B.Ypara, antes do Bconstrutor estático da classe.To execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Yo inicializador Afaz com que o construtor estático seja executado porque o A.X valor de é referenciado.Y's initializer causes A's static constructor to be run because the value of A.X is referenced. O construtor estático de A , por sua vez, prossegue para calcular o Xvalor de e, ao fazer isso, busca o valor Ypadrão de, que é zero.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é, portanto, inicializado como 1.A.X is thus initialized to 1. O processo de execução Ade inicializadores de campo estático e construtor estático é concluído, retornando ao cálculo do valor inicial de Y, o resultado é 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.

Como o construtor estático é executado exatamente uma vez para cada tipo de classe construída fechada, é um local conveniente para impor verificações de tempo de execução no parâmetro de tipo que não pode ser verificado em tempo de compilação via restrições (restrições de parâmetro de tipo) .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 exemplo, o tipo a seguir usa um construtor estático para impor que o argumento de tipo seja uma enumeração: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");
        }
    }
}

DestruidoresDestructors

Um destruidor é um membro que implementa as ações necessárias para destruir uma instância de uma classe.A destructor is a member that implements the actions required to destruct an instance of a class. Um destruidor é declarado usando um destructor_declaration:A destructor is declared using a destructor_declaration:

destructor_declaration
    : attributes? 'extern'? '~' identifier '(' ')' destructor_body
    | destructor_declaration_unsafe
    ;

destructor_body
    : block
    | ';'
    ;

Um destructor_declaration pode incluir um conjunto de atributos (atributos).A destructor_declaration may include a set of attributes (Attributes).

O identificador de um destructor_declaration deve nomear a classe na qual o destruidor é declarado.The identifier of a destructor_declaration must name the class in which the destructor is declared. Se qualquer outro nome for especificado, ocorrerá um erro em tempo de compilação.If any other name is specified, a compile-time error occurs.

Quando uma declaração de destruidor inclui extern um modificador, diz-se que o destruidor é um destruidor externo.When a destructor declaration includes an extern modifier, the destructor is said to be an external destructor. Como uma declaração de destruidor externo não fornece implementação real, seu destructor_body consiste em um ponto-e-vírgula.Because an external destructor declaration provides no actual implementation, its destructor_body consists of a semicolon. Para todos os outros destruidores, o destructor_body consiste em um bloco que especifica as instruções a serem executadas a fim de destruir uma instância da classe.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. Um destructor_body corresponde exatamente ao method_body de um método de instância com um tipo de retorno void (corpo do método).A destructor_body corresponds exactly to the method_body of an instance method with a void return type (Method body).

Os destruidores não são herdados.Destructors are not inherited. Portanto, uma classe não tem destruidores além daquele que pode ser declarado nessa classe.Thus, a class has no destructors other than the one which may be declared in that class.

Como um destruidor é necessário para não ter parâmetros, ele não pode ser sobrecarregado, portanto, uma classe pode ter, no máximo, um destruidor.Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.

Os destruidores são invocados automaticamente e não podem ser invocados explicitamente.Destructors are invoked automatically, and cannot be invoked explicitly. Uma instância fica qualificada para destruição quando não é mais possível que qualquer código use essa instância.An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. A execução do destruidor para a instância pode ocorrer a qualquer momento depois que a instância for qualificada para destruição.Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. Quando uma instância é destruída, os destruidores na cadeia de herança dessa instância são chamados, em ordem, da mais derivada para a 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. Um destruidor pode ser executado em qualquer thread.A destructor may be executed on any thread. Para obter mais informações sobre as regras que regem quando e como um destruidor é executado, consulte gerenciamento automático de memória.For further discussion of the rules that govern when and how a destructor is executed, see Automatic memory management.

A saída do exemploThe 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

como os destruidores em uma cadeia de herança são chamados em ordem, da mais derivada para a menos derivada.since destructors in an inheritance chain are called in order, from most derived to least derived.

Os destruidores são implementados substituindo o Finalize método System.Objectvirtual em.Destructors are implemented by overriding the virtual method Finalize on System.Object. C#os programas não têm permissão para substituir esse método ou chamá-lo (ou substituí-lo) diretamente.C# programs are not permitted to override this method or call it (or overrides of it) directly. Por exemplo, o programaFor instance, the program

class A 
{
    override protected void Finalize() {}    // error

    public void F() {
        this.Finalize();                     // error
    }
}

contém dois erros.contains two errors.

O compilador se comporta como se esse método e as substituições, não existem.The compiler behaves as if this method, and overrides of it, do not exist at all. Portanto, este programa:Thus, this program:

class A 
{
    void Finalize() {}                            // permitted
}

é válido e o método mostrado oculta System.Object Finalize o método.is valid, and the method shown hides System.Object's Finalize method.

Para obter uma discussão sobre o comportamento quando uma exceção é gerada de um destruidor, consulte como as exceções são tratadas.For a discussion of the behavior when an exception is thrown from a destructor, see How exceptions are handled.

IteratorsIterators

Um membro de função (membros da função) implementado usando um bloco do iterador (blocos) é chamado de iterador.A function member (Function members) implemented using an iterator block (Blocks) is called an iterator.

Um bloco do iterador pode ser usado como o corpo de um membro da função, desde que o tipo de retorno do membro da função correspondente seja uma das interfaces do enumerador (interfaces do enumerador) ou uma das interfaces enumeráveis (interfaces enumeráveis) .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). Ele pode ocorrer como um method_body, operator_body ou accessor_body, enquanto eventos, construtores de instância, construtores estáticos e destruidores não podem ser implementados 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.

Quando um membro de função é implementado usando um bloco iterador, ele é um erro de tempo de compilação para a lista de parâmetros formais do membro ref da out função para especificar qualquer parâmetro ou.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 do enumeradorEnumerator interfaces

As interfaces do enumerador são a interface System.Collections.IEnumerator não genérica e todas as instanciações da interface System.Collections.Generic.IEnumerator<T>genérica.The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. Por questão de brevidade, neste capítulo, essas interfaces são referenciadas como IEnumerator e IEnumerator<T>, respectivamente.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively.

Interfaces enumeráveisEnumerable interfaces

As interfaces enumeráveis são a interface System.Collections.IEnumerable não genérica e todas as instanciações da interface System.Collections.Generic.IEnumerable<T>genérica.The enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. Por questão de brevidade, neste capítulo, essas interfaces são referenciadas como IEnumerable e IEnumerable<T>, respectivamente.For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively.

Tipo yieldYield type

Um iterador produz uma sequência de valores, todos do mesmo tipo.An iterator produces a sequence of values, all of the same type. Esse tipo é chamado de tipo yield do iterador.This type is called the yield type of the iterator.

  • O tipo yield de um iterador que IEnumerator retorna IEnumerable ou objecté.The yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • O tipo yield de um iterador que IEnumerator<T> retorna IEnumerable<T> ou Té.The yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T.

Objetos do enumeradorEnumerator objects

Quando um membro de função que retorna um tipo de interface de enumerador é implementado usando um bloco de iterador, invocar o membro da função não executa imediatamente o código no bloco do iterador.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. Em vez disso, um objeto enumerador é criado e retornado.Instead, an enumerator object is created and returned. Esse objeto encapsula o código especificado no bloco do iterador e a execução do código no bloco do iterador ocorre quando o método do MoveNext objeto enumerador é invocado.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. Um objeto enumerador tem as seguintes características:An enumerator object has the following characteristics:

  • Ele implementa IEnumerator e IEnumerator<T>, onde T é o tipo yield do iterador.It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator.
  • Ele implementa System.IDisposable.It implements System.IDisposable.
  • Ele é inicializado com uma cópia dos valores de argumento (se houver) e o valor de instância passado para o membro da função.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
  • Ele tem quatro Estados potenciais, antes, em execução, suspensoe depois, e está inicialmente no estado anterior .It has four potential states, before, running, suspended, and after, and is initially in the before state.

Um objeto enumerador normalmente é uma instância de uma classe enumeradora gerada pelo compilador que encapsula o código no bloco iterador e implementa as interfaces do enumerador, mas outros métodos de implementação são possíveis.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. Se uma classe de enumerador for gerada pelo compilador, essa classe será aninhada, direta ou indiretamente, na classe que contém o membro da função, ela terá uma acessibilidade privada e terá um nome reservado para uso do 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).

Um objeto enumerador pode implementar mais interfaces do que aquelas especificadas acima.An enumerator object may implement more interfaces than those specified above.

As seções a seguir descrevem o comportamento exato dos MoveNextMembros Current, e Dispose , das IEnumerable implementações IEnumerable<T> de interface e fornecidas por um objeto enumerador.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.

Observe que os objetos do enumerador não IEnumerator.Reset oferecem suporte ao método.Note that enumerator objects do not support the IEnumerator.Reset method. Invocar esse método faz com System.NotSupportedException que um seja gerado.Invoking this method causes a System.NotSupportedException to be thrown.

O método MoveNextThe MoveNext method

O MoveNext método de um objeto de enumerador encapsula o código de um bloco de iterador.The MoveNext method of an enumerator object encapsulates the code of an iterator block. Invocar o MoveNext método executa o código no bloco do iterador e define Current a propriedade do objeto enumerador conforme apropriado.Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. A ação precisa executada MoveNext depende do estado do objeto do enumerador quando MoveNext o é invocado:The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • Se o estado do objeto enumerador for antes, invocando MoveNext:If the state of the enumerator object is before, invoking MoveNext:
    • Altera o estado para em execução.Changes the state to running.
    • Inicializa os parâmetros (incluindo this) do bloco do iterador para os valores de argumento e o valor de instância salvos quando o objeto enumerador foi inicializado.Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized.
    • Executa o bloco de iteradores desde o início até que a execução seja interrompida (conforme descrito abaixo).Executes the iterator block from the beginning until execution is interrupted (as described below).
  • Se o estado do objeto enumerador estiver em execução, o resultado da invocação MoveNext será não especificado.If the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • Se o estado do objeto do enumerador for suspenso, invocando MoveNext:If the state of the enumerator object is suspended, invoking MoveNext:
    • Altera o estado para em execução.Changes the state to running.
    • Restaura os valores de todas as variáveis e parâmetros locais (incluindo isso) para os valores salvos quando a execução do bloco do iterador foi suspensa pela última vez.Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. Observe que o conteúdo de quaisquer objetos referenciados por essas variáveis pode ter sido alterado desde a chamada anterior para MoveNext.Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext.
    • Retoma a execução do bloco iterador imediatamente após a yield return instrução que causou a suspensão da execução e continua até que a execução seja interrompida (conforme descrito abaixo).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).
  • Se o estado do objeto enumerador for After, invocando MoveNext retorna false.If the state of the enumerator object is after, invoking MoveNext returns false.

Quando MoveNext o executa o bloco do iterador, a execução pode ser interrompida de quatro maneiras: Por uma yield return instrução, por uma yield break instrução, encontrando o final do bloco do iterador e, por uma exceção sendo gerada e propagada do bloco do iterador.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.

  • Quando uma yield return instrução é encontrada (a instrução yield):When a yield return statement is encountered (The yield statement):
    • A expressão fornecida na instrução é avaliada, implicitamente convertida para o tipo yield e atribuída à Current Propriedade do objeto Enumerator.The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
    • A execução do corpo do iterador é suspensa.Execution of the iterator body is suspended. Os valores de todas as variáveis e parâmetros locais ( thisincluindo) são salvos, como é o local yield return desta instrução.The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. Se a yield return instrução estiver dentro de um ou try mais blocos, os finally blocos associados não serão executados neste momento.If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
    • O estado do objeto do enumerador é alterado para suspenso.The state of the enumerator object is changed to suspended.
    • O MoveNext método retorna true ao chamador, indicando que a iteração foi avançada com êxito para o próximo valor.The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • Quando uma yield break instrução é encontrada (a instrução yield):When a yield break statement is encountered (The yield statement):
    • Se a yield break instrução estiver dentro de um ou try mais blocos, os finally blocos associados serão executados.If the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • O estado do objeto enumerador é alterado para After.The state of the enumerator object is changed to after.
    • O MoveNext método retorna false ao seu chamador, indicando que a iteração foi concluída.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Quando o final do corpo do iterador for encontrado:When the end of the iterator body is encountered:
    • O estado do objeto enumerador é alterado para After.The state of the enumerator object is changed to after.
    • O MoveNext método retorna false ao seu chamador, indicando que a iteração foi concluída.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Quando uma exceção é gerada e propagada para fora do bloco do iterador:When an exception is thrown and propagated out of the iterator block:
    • Os finally blocos apropriados no corpo do iterador serão executados pela propagação de exceção.Appropriate finally blocks in the iterator body will have been executed by the exception propagation.
    • O estado do objeto enumerador é alterado para After.The state of the enumerator object is changed to after.
    • A propagação de exceção continua para o chamador do MoveNext método.The exception propagation continues to the caller of the MoveNext method.

A propriedade atualThe Current property

A propriedade de Current um objeto enumerador é yield return afetada por instruções no bloco do iterador.An enumerator object's Current property is affected by yield return statements in the iterator block.

Quando um objeto de enumerador está no estado suspenso , o valor Current de é o valor definido pela chamada anterior para MoveNext.When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. Quando um objeto enumerador está nos Estados anterior, em execuçãoou depois , o resultado do acesso Current não é especificado.When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified.

Para um iterador com um tipo yield diferente objectde, o resultado do Current acesso por meio da implementação IEnumerable do objeto enumerador Current IEnumerator<T> corresponde ao acesso por meio do objeto enumerador implementação e conversão do resultado em 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.

O método DisposeThe Dispose method

O Dispose método é usado para limpar a iteração, trazendo o objeto enumerador para o estado After .The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • Se o estado do objeto enumerador for antes, invocar Dispose altera o estado para After.If the state of the enumerator object is before, invoking Dispose changes the state to after.
  • Se o estado do objeto enumerador estiver em execução, o resultado da invocação Dispose será não especificado.If the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • Se o estado do objeto do enumerador for suspenso, invocando Dispose:If the state of the enumerator object is suspended, invoking Dispose:
    • Altera o estado para em execução.Changes the state to running.
    • Executa qualquer bloco finally como se a última instrução executada yield return fosse uma yield break instrução.Executes any finally blocks as if the last executed yield return statement were a yield break statement. Se isso fizer com que uma exceção seja lançada e propagada do corpo do iterador, o estado do objeto enumerador será definido como After e a exceção será propagada para o Dispose chamador do 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.
    • Altera o estado para depois.Changes the state to after.
  • Se o estado do objeto enumerador for After, a invocação Dispose não terá nenhum efeito.If the state of the enumerator object is after, invoking Dispose has no affect.

Objetos enumeráveisEnumerable objects

Quando um membro de função que retorna um tipo de interface enumerável é implementado usando um bloco de iterador, invocar o membro da função não executa imediatamente o código no bloco do iterador.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. Em vez disso, um objeto enumerável é criado e retornado.Instead, an enumerable object is created and returned. O método do GetEnumerator objeto Enumerable retorna um objeto enumerador que encapsula o código especificado no bloco do iterador e a execução do código no bloco do iterador ocorre quando o método do MoveNext objeto enumerador é invocado.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. Um objeto enumerável tem as seguintes características:An enumerable object has the following characteristics:

  • Ele implementa IEnumerable e IEnumerable<T>, onde T é o tipo yield do iterador.It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator.
  • Ele é inicializado com uma cópia dos valores de argumento (se houver) e o valor de instância passado para o membro da função.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.

Um objeto enumerável normalmente é uma instância de uma classe enumerável gerada pelo compilador que encapsula o código no bloco do iterador e implementa as interfaces enumeráveis, mas outros métodos de implementação são possíveis.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. Se uma classe enumerável for gerada pelo compilador, essa classe será aninhada, direta ou indiretamente, na classe que contém o membro da função, ela terá uma acessibilidade privada e terá um nome reservado para uso do 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).

Um objeto enumerável pode implementar mais interfaces do que aquelas especificadas acima.An enumerable object may implement more interfaces than those specified above. Em particular, um objeto enumerável também pode IEnumerator implementar IEnumerator<T>e, permitindo que ele sirva como um enumerável e um enumerador.In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. Nesse tipo de implementação, na primeira vez que um método de GetEnumerator objeto enumerável é invocado, o próprio objeto enumerável é retornado.In that type of implementation, the first time an enumerable object's GetEnumerator method is invoked, the enumerable object itself is returned. As invocações subsequentes do objeto GetEnumeratorEnumerable, se houver, retornam uma cópia do objeto Enumerable.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Assim, cada enumerador retornado tem seu próprio Estado e as alterações em um enumerador não afetarão outra.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another.

O método GetEnumeratorThe GetEnumerator method

Um objeto enumerável fornece uma implementação dos GetEnumerator métodos IEnumerable das interfaces e IEnumerable<T> .An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. Os dois GetEnumerator métodos compartilham uma implementação comum que adquire e retorna um objeto enumerador disponível.The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. O objeto enumerador é inicializado com os valores de argumento e o valor da instância salvos quando o objeto Enumerable foi inicializado, mas, caso contrário, o objeto enumerador funciona conforme descrito em objetos do 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.

Exemplo de implementaçãoImplementation example

Esta seção descreve uma possível implementação de iteradores em termos de construções C# padrão.This section describes a possible implementation of iterators in terms of standard C# constructs. A implementação descrita aqui se baseia nos mesmos princípios usados pelo compilador da Microsoft C# , mas não é por isso uma implementação obrigatória ou a única possível.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.

A classe Stack<T> a seguir implementa GetEnumerator seu método usando um iterador.The following Stack<T> class implements its GetEnumerator method using an iterator. O iterador enumera os elementos da pilha na ordem superior para a inferior.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];
    }
}

O GetEnumerator método pode ser convertido em uma instanciação de uma classe de enumerador gerada pelo compilador que encapsula o código no bloco do iterador, conforme mostrado a seguir.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();
        }
    }
}

Na tradução anterior, o código no bloco do iterador é transformado em um computador de estado e MoveNext colocado no método da classe do 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. Além disso, a variável i local é transformada em um campo no objeto Enumerator para que ele possa continuar a existir em invocações de. MoveNextFurthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext.

O exemplo a seguir imprime uma tabela de multiplicação simples dos inteiros de 1 a 10.The following example prints a simple multiplication table of the integers 1 through 10. O FromTo método no exemplo retorna um objeto enumerável e é implementado usando um 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();
        }
    }
}

O FromTo método pode ser convertido em uma instanciação de uma classe enumerável gerada pelo compilador que encapsula o código no bloco do iterador, conforme mostrado a seguir.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();
        }
    }
}

A classe Enumerable implementa as interfaces enumeráveis e as interfaces do enumerador, permitindo que ele sirva como um enumerável e um enumerador.The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. Na primeira vez que GetEnumerator o método é invocado, o próprio objeto Enumerable é retornado.The first time the GetEnumerator method is invoked, the enumerable object itself is returned. As invocações subsequentes do objeto GetEnumeratorEnumerable, se houver, retornam uma cópia do objeto Enumerable.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Assim, cada enumerador retornado tem seu próprio Estado e as alterações em um enumerador não afetarão outra.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. O Interlocked.CompareExchange método é usado para garantir a operação thread-safe.The Interlocked.CompareExchange method is used to ensure thread-safe operation.

Os from parâmetros to e são transformados em campos na classe Enumerable.The from and to parameters are turned into fields in the enumerable class. Como from é modificado no bloco do iterador, um __from campo adicional é introduzido para conter o valor inicial fornecido from para em 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.

O MoveNext método lançará InvalidOperationException um se for chamado quando __state for 0.The MoveNext method throws an InvalidOperationException if it is called when __state is 0. Isso protege contra o uso do objeto Enumerable como um objeto enumerador sem primeiro GetEnumeratorchamar.This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

O exemplo a seguir mostra uma classe de árvore simples.The following example shows a simple tree class. A Tree<T> classe implementa seu GetEnumerator método usando um iterador.The Tree<T> class implements its GetEnumerator method using an iterator. O iterador enumera os elementos da árvore em ordem de infixo.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();
    }
}

O GetEnumerator método pode ser convertido em uma instanciação de uma classe de enumerador gerada pelo compilador que encapsula o código no bloco do iterador, conforme mostrado a seguir.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();
        }
    }
}

O compilador gerado temporaries usado nas foreach instruções é __left dividido nos campos e __right do objeto enumerador.The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. O __state campo do objeto enumerador é cuidadosamente atualizado para que o método Dispose() correto seja chamado corretamente se uma exceção for lançada.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. Observe que não é possível escrever o código traduzido com instruções simples foreach .Note that it is not possible to write the translated code with simple foreach statements.

Funções assíncronasAsync functions

Um método (métodos) ou uma função anônima (expressões de função anônimas) com o async modificador é chamado de função Async.A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an async function. Em geral, o termo Async é usado para descrever qualquer tipo de função que tenha o async modificador.In general, the term async is used to describe any kind of function that has the async modifier.

É um erro de tempo de compilação para a lista de parâmetros formais de uma função Async para ref especificar out qualquer parâmetro ou.It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters.

O return_type de um método assíncrono deve ser void ou um tipo de tarefa.The return_type of an async method must be either void or a task type. Os tipos de tarefa System.Threading.Tasks.Task são e os tipos System.Threading.Tasks.Task<T>construídos a partir de.The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. Para fins de brevidade, neste capítulo, esses tipos são referenciados como Task e Task<T>, respectivamente.For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. Um método assíncrono que retorna um tipo de tarefa é considerado como sendo retornado por tarefa.An async method returning a task type is said to be task-returning.

A definição exata dos tipos de tarefa é a implementação definida, mas, no ponto de vista do idioma, um tipo de tarefa está em um dos Estados incompletos, com êxito ou com falha.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. Uma tarefa com falha registra uma exceção pertinente.A faulted task records a pertinent exception. Um êxito Task<T> registra um resultado do tipo T.A succeeded Task<T> records a result of type T. Os tipos de tarefa são awaitable e, portanto, podem ser os operandos das expressões Await (Await expressões).Task types are awaitable, and can therefore be the operands of await expressions (Await expressions).

Uma invocação de função assíncrona tem a capacidade de suspender a avaliação por meio de expressões Await (expressões Await) em seu corpo.An async function invocation has the ability to suspend evaluation by means of await expressions (Await expressions) in its body. Posteriormente, a avaliação pode ser retomada no ponto da expressão Await de suspensão por meio de um delegado de continuação.Evaluation may later be resumed at the point of the suspending await expression by means of a resumption delegate. O delegado de continuação é do tipo System.Actione, quando é invocado, a avaliação da invocação de função assíncrona retomará a partir da expressão Await onde ela parou.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. O chamador atual de uma invocação de função assíncrona é o chamador original se a invocação de função nunca foi suspensa ou o chamador mais recente do delegado de continuação, caso contrário.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.

Avaliação de uma função assíncrona de retorno de tarefaEvaluation of a task-returning async function

A invocação de uma função assíncrona de retorno de tarefa faz com que uma instância do tipo de tarefa retornada seja gerada.Invocation of a task-returning async function causes an instance of the returned task type to be generated. Isso é chamado de tarefa de retorno da função Async.This is called the return task of the async function. A tarefa está inicialmente em um estado incompleto.The task is initially in an incomplete state.

O corpo da função assíncrona é então avaliado até que seja suspenso (atingindo uma expressão Await) ou seja encerrado, no qual o controle de ponto é retornado ao chamador, junto com a tarefa de retorno.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.

Quando o corpo da função Async é encerrado, a tarefa de retorno é movida do estado incompleto:When the body of the async function terminates, the return task is moved out of the incomplete state:

  • Se o corpo da função terminar como o resultado de atingir uma instrução de retorno ou o final do corpo, qualquer valor de resultado será registrado na tarefa de retorno, que será colocado em um estado de êxito.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.
  • Se o corpo da função for encerrado como resultado de uma exceção não percebida (a instrução Throw), a exceção será registrada na tarefa de retorno que será colocada em um estado com falha.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.

Avaliação de uma função assíncrona de retorno nuloEvaluation of a void-returning async function

Se o tipo de retorno da função Async for void, a avaliação será diferente da seguinte maneira: Como nenhuma tarefa é retornada, a função comunica a conclusão e as exceções ao contexto de sincronizaçãodo thread atual.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. A definição exata do contexto de sincronização é dependente da implementação, mas é uma representação de "onde" o thread atual está em execução.The exact definition of synchronization context is implementation-dependent, but is a representation of "where" the current thread is running. O contexto de sincronização é notificado quando a avaliação de uma função assíncrona de retorno nulo começa, é concluída com êxito ou faz com que uma exceção não percebida seja gerada.The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.

Isso permite que o contexto Mantenha o controle de quantas funções assíncronas de retorno nulo estão em execução e para decidir como propagar exceções saindo delas.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.