ClassiClasses

Una classe è una struttura di dati che può contenere membri dati (costanti e campi), membri di funzione (metodi, proprietà, eventi, indicizzatori, operatori, costruttori di istanze, distruttori e costruttori statici) e tipi annidati.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. I tipi di classe supportano l'ereditarietà, un meccanismo in base al quale una classe derivata può estendere ed specializzare una classe base.Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class.

Dichiarazioni di classeClass declarations

Un class_declaration è un type_declaration (dichiarazioni di tipo) che dichiara una nuova 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 ';'?
    ;

Un class_declaration è costituito da un set facoltativo di attributi (attributi), seguito da un set facoltativo di class_modifier s (modificatori di classe), seguito da un partial modificatore facoltativo, seguito dalla parola chiave class e da un identificatore che denomina la classe, seguito da un type_parameter_list facoltativo (parametri di tipo), seguito da una specifica di class_base facoltativa (classe di base della classe), seguita da un set facoltativo di type_parameter_constraints_clause s (vincoli del parametro di tipo), seguito da un class_body (corpo della classe), facoltativamente seguito da un punto eA class_declaration consists of an optional set of attributes (Attributes), followed by an optional set of class_modifier s (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_clause s (Type parameter constraints), followed by a class_body (Class body), optionally followed by a semicolon.

Una dichiarazione di classe non può fornire type_parameter_constraints_clause s a meno che non fornisca anche una type_parameter_list.A class declaration cannot supply type_parameter_constraints_clause s unless it also supplies a type_parameter_list.

Una dichiarazione di classe che fornisce un type_parameter_list è una dichiarazione di classe generica.A class declaration that supplies a type_parameter_list is a generic class declaration. Inoltre, qualsiasi classe annidata all'interno di una dichiarazione di classe generica o di una dichiarazione di struct generica è a sua volta una dichiarazione di classe generica, poiché è necessario fornire i parametri di tipo per il tipo contenitore per creare un tipo costruito.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.

Modificatori di classeClass modifiers

Un class_declaration può includere facoltativamente una sequenza di modificatori di 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
    ;

Si tratta di un errore in fase di compilazione perché lo stesso modificatore venga visualizzato più volte in una dichiarazione di classe.It is a compile-time error for the same modifier to appear multiple times in a class declaration.

Il new modificatore è consentito nelle classi annidate.The new modifier is permitted on nested classes. Specifica che la classe nasconde un membro ereditato con lo stesso nome, come descritto nel nuovo modificatore.It specifies that the class hides an inherited member by the same name, as described in The new modifier. Si tratta di un errore in fase di compilazione perché il new modificatore venga visualizzato in una dichiarazione di classe che non è una dichiarazione di classe annidata.It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration.

I public protected internal modificatori,, e private controllano l'accessibilità della classe.The public, protected, internal, and private modifiers control the accessibility of the class. A seconda del contesto in cui si verifica la dichiarazione di classe, alcuni modificatori potrebbero non essere consentiti (accessibilità dichiarata).Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (Declared accessibility).

I abstract sealed static modificatori, e sono descritti nelle sezioni seguenti.The abstract, sealed and static modifiers are discussed in the following sections.

Classi astratteAbstract classes

Il abstract modificatore viene usato per indicare che una classe è incompleta e che deve essere usata solo come classe di base.The abstract modifier is used to indicate that a class is incomplete and that it is intended to be used only as a base class. Una classe astratta differisce da una classe non astratta nei modi seguenti:An abstract class differs from a non-abstract class in the following ways:

  • Non è possibile creare direttamente un'istanza di una classe astratta e si tratta di un errore in fase di compilazione per utilizzare l' new operatore in una classe astratta.An abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class. Sebbene sia possibile avere variabili e valori i cui tipi in fase di compilazione sono astratti, tali variabili e valori dovranno necessariamente essere null o contenere riferimenti a istanze di classi non astratte derivate dai tipi astratti.While it is possible to have variables and values whose compile-time types are abstract, such variables and values will necessarily either be null or contain references to instances of non-abstract classes derived from the abstract types.
  • Una classe astratta è consentita (ma non obbligatoria) per contenere membri astratti.An abstract class is permitted (but not required) to contain abstract members.
  • Una classe astratta non può essere sealed.An abstract class cannot be sealed.

Quando una classe non astratta viene derivata da una classe astratta, la classe non astratta deve includere implementazioni effettive di tutti i membri astratti ereditati, ignorando in tal modo i membri astratti.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. Nell'esempioIn the example

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

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

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

la classe astratta A introduce un metodo astratto F .the abstract class A introduces an abstract method F. BLa classe introduce un metodo aggiuntivo G , ma poiché non fornisce un'implementazione di F , B deve essere dichiarato anche abstract.Class B introduces an additional method G, but since it doesn't provide an implementation of F, B must also be declared abstract. CLa classe esegue l'override F di e fornisce un'implementazione effettiva.Class C overrides F and provides an actual implementation. Poiché non sono presenti membri astratti in C , C è consentito (ma non obbligatorio) non essere astratto.Since there are no abstract members in C, C is permitted (but not required) to be non-abstract.

Classi sealedSealed classes

Il sealed modificatore viene usato per impedire la derivazione da una classe.The sealed modifier is used to prevent derivation from a class. Si verifica un errore in fase di compilazione se viene specificata una classe sealed come classe base di un'altra classe.A compile-time error occurs if a sealed class is specified as the base class of another class.

Una classe sealed non può essere anche una classe astratta.A sealed class cannot also be an abstract class.

Il sealed modificatore viene utilizzato principalmente per evitare derivazioni indesiderate, ma consente anche determinate ottimizzazioni in fase di esecuzione.The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. In particolare, poiché una classe sealed non dispone mai di classi derivate, è possibile trasformare le chiamate del membro della funzione virtuale in istanze di classe sealed in chiamate non virtuali.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.

Classi staticheStatic classes

Il static modificatore viene usato per contrassegnare la classe dichiarata come classe statica.The static modifier is used to mark the class being declared as a static class. Non è possibile creare un'istanza di una classe statica. non può essere usata come tipo e può contenere solo membri statici.A static class cannot be instantiated, cannot be used as a type and can contain only static members. Solo una classe statica può contenere dichiarazioni di metodi di estensione (metodi di estensione).Only a static class can contain declarations of extension methods (Extension methods).

Una dichiarazione di classe statica è soggetta alle restrizioni seguenti:A static class declaration is subject to the following restrictions:

  • Una classe statica non può includere un sealed abstract modificatore o.A static class may not include a sealed or abstract modifier. Si noti, tuttavia, che poiché una classe statica non può essere creata o derivata da, si comporta come se fosse sealed e abstract.Note, however, that since a static class cannot be instantiated or derived from, it behaves as if it was both sealed and abstract.
  • Una classe statica non può includere una specifica class_base (specifica di base della classe) e non può specificare in modo esplicito una classe base o un elenco di interfacce implementate.A static class may not include a class_base specification (Class base specification) and cannot explicitly specify a base class or a list of implemented interfaces. Una classe statica eredita in modo implicito dal tipo object .A static class implicitly inherits from type object.
  • Una classe statica può contenere solo membri statici (membri statici e di istanza).A static class can only contain static members (Static and instance members). Si noti che le costanti e i tipi annidati sono classificati come membri statici.Note that constants and nested types are classified as static members.
  • Una classe statica non può avere membri con l' protected protected internal accessibilità o dichiarata.A static class cannot have members with protected or protected internal declared accessibility.

Si tratta di un errore in fase di compilazione per violare una qualsiasi di queste restrizioni.It is a compile-time error to violate any of these restrictions.

Una classe statica non ha costruttori di istanza.A static class has no instance constructors. Non è possibile dichiarare un costruttore di istanza in una classe statica e non viene fornito alcun costruttore di istanza predefinito (costruttori predefiniti) per una classe statica.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.

I membri di una classe statica non sono automaticamente statici e le dichiarazioni dei membri devono includere in modo esplicito un static modificatore (ad eccezione delle costanti e dei tipi annidati).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 una classe è annidata all'interno di una classe esterna statica, la classe annidata non è una classe statica a meno che non includa in modo esplicito un static modificatore.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.

Riferimento a tipi di classe staticiReferencing static class types

Un namespace_or_type_name (nomi di spazio dei nomi e di tipi) può fare riferimento a una classe statica seA namespace_or_type_name (Namespace and type names) is permitted to reference a static class if

  • Il namespace_or_type_name è T in una namespace_or_type_name nel formato T.I oThe namespace_or_type_name is the T in a namespace_or_type_name of the form T.I, or
  • Il namespace_or_type_name è T in una typeof_expression (elenchi di argomenti1) del modulo typeof(T) .The namespace_or_type_name is the T in a typeof_expression (Argument lists1) of the form typeof(T).

Un primary_expression (membri di funzione) può fare riferimento a una classe statica seA primary_expression (Function members) is permitted to reference a static class if

In qualsiasi altro contesto, si tratta di un errore in fase di compilazione per fare riferimento a una classe statica.In any other context it is a compile-time error to reference a static class. Ad esempio, è un errore per una classe statica da usare come classe base, un tipo costituente (tipi annidati) di un membro, un argomento di tipo generico o un vincolo di parametro di 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. Analogamente, una classe statica non può essere usata in un tipo di matrice, un tipo di puntatore, un'espressione new , un'espressione cast, un' is espressione, un'espressione, as un'espressione sizeof o un'espressione con valore predefinito.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.

Modificatore parzialePartial modifier

Il partial modificatore viene usato per indicare che questo class_declaration è una dichiarazione di tipo parziale.The partial modifier is used to indicate that this class_declaration is a partial type declaration. Più dichiarazioni di tipo parziale con lo stesso nome in uno spazio dei nomi di inclusione o in una dichiarazione di tipo combinano per formare una dichiarazione di tipo, seguendo le regole specificate nei tipi parziali.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.

La dichiarazione di una classe distribuita su segmenti separati del testo del programma può essere utile se questi segmenti vengono prodotti o conservati in contesti diversi.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. Ad esempio, una parte di una dichiarazione di classe può essere generata dal computer, mentre l'altra viene creata manualmente.For instance, one part of a class declaration may be machine generated, whereas the other is manually authored. La separazione testuale dei due impedisce l'aggiornamento da parte di uno degli aggiornamenti in conflitto con gli aggiornamenti.Textual separation of the two prevents updates by one from conflicting with updates by the other.

Parametri di tipoType parameters

Un parametro di tipo è un identificatore semplice che denota un segnaposto per un argomento di tipo fornito per creare un tipo costruito.A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. Un parametro di tipo è un segnaposto formale per un tipo che verrà fornito in un secondo momento.A type parameter is a formal placeholder for a type that will be supplied later. Al contrario, un argomento di tipo (argomenti di tipo) è il tipo effettivo che sostituisce il parametro di tipo quando viene creato un tipo costruito.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
    ;

Ogni parametro di tipo in una dichiarazione di classe definisce un nome nello spazio di dichiarazione (dichiarazioni) di tale classe.Each type parameter in a class declaration defines a name in the declaration space (Declarations) of that class. Quindi, non può avere lo stesso nome di un altro parametro di tipo o di un membro dichiarato in tale classe.Thus, it cannot have the same name as another type parameter or a member declared in that class. Un parametro di tipo non può avere lo stesso nome del tipo stesso.A type parameter cannot have the same name as the type itself.

Specifica di base della classeClass base specification

Una dichiarazione di classe può includere una specifica class_base , che definisce la classe di base diretta della classe e le interfacce (interfacce) direttamente implementate dalla 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)*
    ;

La classe base specificata in una dichiarazione di classe può essere un tipo di classe costruito (tipi costruiti).The base class specified in a class declaration can be a constructed class type (Constructed types). Una classe di base non può essere un parametro di tipo autonomamente, anche se può coinvolgere i parametri di tipo inclusi nell'ambito.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

Classi di baseBase classes

Quando un class_type viene incluso nella class_base, specifica la classe di base diretta della classe dichiarata.When a class_type is included in the class_base, it specifies the direct base class of the class being declared. Se una dichiarazione di classe non ha class_base o se il class_base elenca solo i tipi di interfaccia, si presuppone che la classe di base diretta sia object .If a class declaration has no class_base, or if the class_base lists only interface types, the direct base class is assumed to be object. Una classe eredita i membri dalla relativa classe di base diretta, come descritto in ereditarietà.A class inherits members from its direct base class, as described in Inheritance.

Nell'esempioIn the example

class A {}

class B: A {}

Ala classe è detta classe di base diretta di B e viene definita B derivata da A .class A is said to be the direct base class of B, and B is said to be derived from A. Poiché in A non viene specificata in modo esplicito una classe base diretta, la relativa classe di base diretta è implicita object .Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

Per un tipo di classe costruito, se nella dichiarazione di classe generica viene specificata una classe base, la classe base del tipo costruito viene ottenuta sostituendo, per ogni type_parameter nella dichiarazione della classe di base, l' type_argument corrispondente del tipo costruito.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. Date le dichiarazioni di classi genericheGiven the generic class declarations

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

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

la classe base del tipo costruito G<int> sarà B<string,int[]> .the base class of the constructed type G<int> would be B<string,int[]>.

La classe di base diretta di un tipo di classe deve essere accessibile almeno quanto il tipo di classe (domini di accessibilità).The direct base class of a class type must be at least as accessible as the class type itself (Accessibility domains). Ad esempio, si tratta di un errore in fase di compilazione per la public derivazione di una classe da una private internal classe o.For example, it is a compile-time error for a public class to derive from a private or internal class.

La classe di base diretta di un tipo di classe non può essere uno dei tipi seguenti: System.Array ,, System.Delegate System.MulticastDelegate , System.Enum o System.ValueType .The direct base class of a class type must not be any of the following types: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, or System.ValueType. Inoltre, una dichiarazione di classe generica non può essere utilizzata System.Attribute come classe base diretta o indiretta.Furthermore, a generic class declaration cannot use System.Attribute as a direct or indirect base class.

Quando si determina il significato della specifica della classe di base diretta A di una classe B , la classe di base diretta di B è temporaneamente considerata object .While determining the meaning of the direct base class specification A of a class B, the direct base class of B is temporarily assumed to be object. In modo intuitivo, questo garantisce che il significato di una specifica della classe base non possa dipendere in modo ricorsivo da se stesso.Intuitively this ensures that the meaning of a base class specification cannot recursively depend on itself. Esempio:The example:

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

class C : A<C.B> {}

è in errore poiché nella specifica della classe di base A<C.B> la classe di base diretta di C viene considerata object e, di conseguenza, in base alle regole dei nomi di spazio dei nomi e di tipo, C non è considerato un 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.

Le classi base di un tipo di classe sono la classe base diretta e le relative classi base.The base classes of a class type are the direct base class and its base classes. In altre parole, il set di classi di base è la chiusura transitiva della relazione della classe di base diretta.In other words, the set of base classes is the transitive closure of the direct base class relationship. Facendo riferimento all'esempio precedente, le classi di base di B sono A e object .Referring to the example above, the base classes of B are A and object. Nell'esempioIn the example

class A {...}

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

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

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

le classi di base di D<int> sono C<int[]> , B<IComparable<int[]>> , A e object .the base classes of D<int> are C<int[]>, B<IComparable<int[]>>, A, and object.

Ad eccezione della classe object , ogni tipo di classe dispone esattamente di una classe di base diretta.Except for class object, every class type has exactly one direct base class. La object classe non dispone di una classe di base diretta ed è la classe base finale di tutte le altre classi.The object class has no direct base class and is the ultimate base class of all other classes.

Quando una classe B deriva da una classe A , si tratta di un errore in fase di compilazione da A cui dipende B .When a class B derives from a class A, it is a compile-time error for A to depend on B. Una classe dipende direttamente da _ la relativa classe di base diretta, se presente, e _dipende direttamente*_ dalla classe in cui è immediatamente annidata (se presente).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). Data questa definizione, il set completo di classi da cui dipende una classe è la chiusura riflessiva e transitiva di _ dipende direttamente dalla relazione *.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.

L'esempio:The example

class A: A {}

è errato perché la classe dipende da se stessa.is erroneous because the class depends on itself. Analogamente, l'esempioLikewise, the example

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

è in errore perché le classi dipendono da se stesse.is in error because the classes circularly depend on themselves. Infine, l'esempioFinally, the example

class A: B.C {}

class B: A
{
    public class C {}
}

genera un errore in fase di compilazione perché A dipende da B.C (la relativa classe di base diretta), che dipende da B (la classe che la contiene immediatamente), che dipende in modo circolare da A .results in a compile-time error because A depends on B.C (its direct base class), which depends on B (its immediately enclosing class), which circularly depends on A.

Si noti che una classe non dipende dalle classi annidate al suo interno.Note that a class does not depend on the classes that are nested within it. Nell'esempioIn the example

class A
{
    class B: A {}
}

B dipende da A (perché A è la classe di base diretta e la classe che la contiene immediatamente), ma A non dipende da B (poiché non B è una classe di base o una classe di inclusione di 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). L'esempio è quindi valido.Thus, the example is valid.

Non è possibile derivare da una sealed classe.It is not possible to derive from a sealed class. Nell'esempioIn the example

sealed class A {}

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

Bla classe è in errore perché tenta di derivare dalla sealed classe A .class B is in error because it attempts to derive from the sealed class A.

Implementazioni di interfacceInterface implementations

Una specifica class_base può includere un elenco di tipi di interfaccia, nel qual caso viene definito che la classe implementi direttamente i tipi di interfaccia specificati.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. Le implementazioni dell'interfaccia sono illustrate ulteriormente nelle implementazioni dell'interfaccia.Interface implementations are discussed further in Interface implementations.

Vincoli di parametro di tipoType parameter constraints

Le dichiarazioni di tipi e metodi generici possono facoltativamente specificare vincoli di parametro di tipo includendo type_parameter_constraints_clause s.Generic type and method declarations can optionally specify type parameter constraints by including type_parameter_constraints_clause s.

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' '(' ')'
    ;

Ogni type_parameter_constraints_clause è costituito dal token where , seguito dal nome di un parametro di tipo, seguito da due punti e dall'elenco di vincoli per quel parametro di 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. Per ogni parametro di tipo può essere presente al massimo una where clausola e le where clausole possono essere elencate in qualsiasi ordine.There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. Analogamente get ai set token e in una funzione di accesso alla proprietà, il where token non è una parola chiave.Like the get and set tokens in a property accessor, the where token is not a keyword.

L'elenco di vincoli specificati in una where clausola può includere uno dei seguenti componenti, in questo ordine: un singolo vincolo primario, uno o più vincoli secondari e il vincolo del costruttore, 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().

Un vincolo primario può essere un tipo di classe o il vincolo di tipo Reference _ class o il _vincolo di tipo valore*_ struct .A primary constraint can be a class type or the reference type constraint _ class or the _value type constraint*_ struct. Un vincolo secondario può essere un _type_parameter * o INTERFACE_TYPE.A secondary constraint can be a _type_parameter* or interface_type.

Il vincolo del tipo di riferimento specifica che un argomento di tipo utilizzato per il parametro di tipo deve essere un tipo riferimento.The reference type constraint specifies that a type argument used for the type parameter must be a reference type. Tutti i tipi di classe, i tipi di interfaccia, i tipi di delegati, i tipi di matrici e i parametri di tipo noti come un tipo di riferimento, come definito di seguito, soddisfano questo vincolo.All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint.

Il vincolo di tipo valore specifica che un argomento di tipo utilizzato per il parametro di tipo deve essere un tipo valore non nullable.The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. Tutti i tipi struct non nullable, i tipi enum e i parametri di tipo con il vincolo di tipo valore soddisfano questo vincolo.All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Si noti che anche se classificato come tipo valore, un tipo Nullable (tipi nullable) non soddisfa il vincolo di tipo valore.Note that although classified as a value type, a nullable type (Nullable types) does not satisfy the value type constraint. Un parametro di tipo con il vincolo di tipo valore non può avere anche il constructor_constraint.A type parameter having the value type constraint cannot also have the constructor_constraint.

I tipi di puntatore non sono mai consentiti come argomenti di tipo e non sono considerati conformi al tipo di riferimento o ai vincoli del tipo di valore.Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints.

Se un vincolo è un tipo di classe, un tipo di interfaccia o un parametro di tipo, tale tipo specifica un "tipo di base" minimo che ogni argomento di tipo usato per quel parametro di tipo deve supportare.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. Ogni volta che viene usato un tipo costruito o un metodo generico, l'argomento di tipo viene confrontato con i vincoli sul parametro di tipo in fase di compilazione.Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. L'argomento di tipo fornito deve soddisfare le condizioni descritte nei vincoli soddisfacenti.The type argument supplied must satisfy the conditions described in Satisfying constraints.

Un vincolo class_type deve soddisfare le regole seguenti:A class_type constraint must satisfy the following rules:

  • Il tipo deve essere un tipo di classe.The type must be a class type.
  • Il tipo non deve essere sealed .The type must not be sealed.
  • Il tipo non deve essere uno dei tipi seguenti: System.Array , System.Delegate , System.Enum o System.ValueType .The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • Il tipo non deve essere object .The type must not be object. Poiché tutti i tipi derivano da object , un vincolo di questo tipo non avrà alcun effetto se fosse consentito.Because all types derive from object, such a constraint would have no effect if it were permitted.
  • Al massimo un vincolo per un determinato parametro di tipo può essere un tipo classe.At most one constraint for a given type parameter can be a class type.

Un tipo specificato come vincolo INTERFACE_TYPE deve soddisfare le regole seguenti:A type specified as an interface_type constraint must satisfy the following rules:

  • Il tipo deve essere un tipo di interfaccia.The type must be an interface type.
  • Un tipo non deve essere specificato più di una volta in una where clausola specificata.A type must not be specified more than once in a given where clause.

In entrambi i casi, il vincolo può coinvolgere qualsiasi parametro di tipo della dichiarazione del tipo o del metodo associato come parte di un tipo costruito e può coinvolgere il tipo dichiarato.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.

Qualsiasi classe o tipo di interfaccia specificato come vincolo di parametro di tipo deve essere almeno accessibile (vincoli di accessibilità) del tipo o del metodo generico dichiarato.Any class or interface type specified as a type parameter constraint must be at least as accessible (Accessibility constraints) as the generic type or method being declared.

Un tipo specificato come vincolo type_parameter deve soddisfare le regole seguenti:A type specified as a type_parameter constraint must satisfy the following rules:

  • Il tipo deve essere un parametro di tipo.The type must be a type parameter.
  • Un tipo non deve essere specificato più di una volta in una where clausola specificata.A type must not be specified more than once in a given where clause.

Inoltre, non deve essere presente alcun ciclo nel grafico delle dipendenze di parametri di tipo, in cui la dipendenza è una relazione transitiva definita da:In addition there must be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:

  • Se un parametro T di tipo viene usato come vincolo per il parametro di tipo, S S dipende da T .If a type parameter T is used as a constraint for type parameter S then S depends on T.
  • Se un parametro di tipo S dipende da un parametro di tipo T e dipende da T un parametro di tipo, U S dipende da 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.

Data questa relazione, si tratta di un errore in fase di compilazione affinché un parametro di tipo dipenda da solo (direttamente o indirettamente).Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly).

Tutti i vincoli devono essere coerenti tra i parametri di tipo dipendente.Any constraints must be consistent among dependent type parameters. Se il parametro S di tipo dipende dal parametro di tipo T :If type parameter S depends on type parameter T then:

  • T non deve avere il vincolo di tipo valore.T must not have the value type constraint. In caso contrario, T è effettivamente sealed, quindi S verrebbe forzato a essere lo stesso tipo di T , eliminando la necessità di due parametri di 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 ha il vincolo di tipo valore T , quindi non deve avere un vincolo class_type .If S has the value type constraint then T must not have a class_type constraint.
  • Se S ha un vincolo di class_type A e T ha un vincolo di class_type , è necessario che sia presente una conversione di B identità o una conversione di un riferimento implicito da A a B o da una conversione di riferimento implicita da 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 dipende anche dal parametro di tipo U e U ha un vincolo di class_type A e T ha un vincolo di class_type , è necessario che sia presente una conversione di B identità o un riferimento implicito da a A o da B a una conversione di riferimento implicita da B a A .If S also depends on type parameter U and U has a class_type constraint A and T has a class_type constraint B then there must be an identity conversion or implicit reference conversion from A to B or an implicit reference conversion from B to A.

SIl vincolo di tipo di valore e il vincolo di tipo di riferimento sono validi per l'oggetto T .It is valid for S to have the value type constraint and T to have the reference type constraint. Questo limite è effettivamente limitato T ai tipi System.Object ,, e a System.ValueType System.Enum qualsiasi tipo di interfaccia.Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type.

Se la where clausola per un parametro di tipo include un vincolo del costruttore, che ha il formato new() , è possibile usare l' new operatore per creare istanze del tipo (espressioni dicreazione di oggetti).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). Qualsiasi argomento di tipo usato per un parametro di tipo con un vincolo del costruttore deve avere un costruttore pubblico senza parametri (questo costruttore esiste in modo implicito per qualsiasi tipo di valore) o essere un parametro di tipo con il vincolo del tipo di valore o il vincolo del costruttore (vedere vincoli del parametro di tipo per informazioni dettagliate).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).

Di seguito sono riportati alcuni esempi di vincoli: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()
{
    ...
}

L'esempio seguente è in errore perché causa una circolare nel grafico delle dipendenze dei parametri di 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
{
    ...
}

Gli esempi seguenti illustrano situazioni non valide aggiuntive: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
{
    ...
}

La classe base effettiva di un parametro di tipo T è definita nel modo seguente:The effective base class of a type parameter T is defined as follows:

  • Se T non ha vincoli primari o vincoli di parametro di tipo, la relativa classe di base effettiva è object .If T has no primary constraints or type parameter constraints, its effective base class is object.
  • Se T ha il vincolo di tipo valore, la relativa classe di base effettiva è System.ValueType .If T has the value type constraint, its effective base class is System.ValueType.
  • Se T ha un vincolo class_type C ma nessun vincolo type_parameter , la relativa classe di base effettiva è C .If T has a class_type constraint C but no type_parameter constraints, its effective base class is C.
  • Se T non dispone di un vincolo di class_type ma ha uno o più vincoli di type_parameter , la relativa classe di base effettiva è il tipo più incluso, ovvero gli operatori di conversione rimossi, nel set di classi di base valide dei vincoli di type_parameter .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. Le regole di coerenza assicurano che esista un tipo più incluso.The consistency rules ensure that such a most encompassed type exists.
  • Se T dispone di un vincolo di class_type e di uno o più vincoli di type_parameter , la relativa classe di base effettiva è il tipo più incluso (operatori di conversione Lift) nel set costituito dal vincolo class_type di T e dalle classi di base valide dei vincoli di 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. Le regole di coerenza assicurano che esista un tipo più incluso.The consistency rules ensure that such a most encompassed type exists.
  • Se T ha il vincolo di tipo di riferimento ma non class_type vincoli, la relativa classe di base effettiva è object .If T has the reference type constraint but no class_type constraints, its effective base class is object.

Ai fini di queste regole, se T ha un vincolo V che è un value_type, usare invece il tipo di base più specifico di V che è un class_type.For the purpose of these rules, if T has a constraint V that is a value_type, use instead the most specific base type of V that is a class_type. Questo problema non può mai verificarsi in un vincolo specificato in modo esplicito, ma può verificarsi quando i vincoli di un metodo generico vengono ereditati in modo implicito da una dichiarazione di metodo che esegue l'override o da un'implementazione esplicita di un metodo di interfaccia.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.

Queste regole assicurano che la classe di base effettiva sia sempre un class_type.These rules ensure that the effective base class is always a class_type.

Il set di interfacce effettivo di un parametro di tipo T viene definito nel modo seguente:The effective interface set of a type parameter T is defined as follows:

  • Se T non ha secondary_constraints, il set di interfacce effettivo è vuoto.If T has no secondary_constraints, its effective interface set is empty.
  • Se T ha INTERFACE_TYPE vincoli ma nessun vincolo type_parameter , il set di interfacce effettivo è il set di vincoli di 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 non dispone di vincoli INTERFACE_TYPE ma presenta vincoli di type_parameter , il set di interfacce effettivo è l'Unione dei set di interfaccia effettivi dei vincoli di 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 dispone di vincoli di INTERFACE_TYPE e vincoli di type_parameter , il set di interfacce effettivo è l'Unione del set di INTERFACE_TYPE vincoli e dei set di interfaccia effettivi dei vincoli type_parameter .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.

Un parametro di tipo è noto come tipo di riferimento se ha il vincolo di tipo riferimento o la classe di base effettiva non object è System.ValueType o.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.

I valori di un tipo di parametro di tipo vincolato possono essere utilizzati per accedere ai membri di istanza impliciti nei vincoli.Values of a constrained type parameter type can be used to access the instance members implied by the constraints. Nell'esempioIn the example

interface IPrintable
{
    void Print();
}

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

i metodi di IPrintable possono essere richiamati direttamente in x perché T è vincolato a implementare sempre IPrintable .the methods of IPrintable can be invoked directly on x because T is constrained to always implement IPrintable.

Corpo della classeClass body

Il class_body di una classe definisce i membri di tale classe.The class_body of a class defines the members of that class.

class_body
    : '{' class_member_declaration* '}'
    ;

Tipi parzialiPartial types

Una dichiarazione di tipo può essere divisa tra più dichiarazioni di tipo parziale.A type declaration can be split across multiple partial type declarations. La dichiarazione di tipo viene costruita dalle sue parti seguendo le regole in questa sezione, in quanto viene considerata come una singola dichiarazione durante il resto dell'elaborazione del programma in fase di compilazione e in fase di esecuzione.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.

Una class_declaration, struct_declaration o interface_declaration rappresenta una dichiarazione di tipo parziale se include un partial modificatore.A class_declaration, struct_declaration or interface_declaration represents a partial type declaration if it includes a partial modifier. partial non è una parola chiave e agisce solo come modificatore se viene visualizzato immediatamente prima di una delle parole class chiave struct o interface in una dichiarazione di tipo o prima del tipo void in una dichiarazione di metodo.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. In altri contesti può essere usato come identificatore normale.In other contexts it can be used as a normal identifier.

Ogni parte di una dichiarazione di tipo parziale deve includere un partial modificatore.Each part of a partial type declaration must include a partial modifier. Deve avere lo stesso nome ed essere dichiarato nello stesso spazio dei nomi o nella stessa dichiarazione di tipo delle altre parti.It must have the same name and be declared in the same namespace or type declaration as the other parts. Il partial modificatore indica che altre parti della dichiarazione di tipo possono esistere altrove, ma l'esistenza di tali parti aggiuntive non è un requisito; è valido per un tipo con una sola dichiarazione per includere il partial modificatore.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.

Tutte le parti di un tipo parziale devono essere compilate insieme in modo che sia possibile unire le parti in fase di compilazione in una singola dichiarazione di 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. I tipi parziali in particolare non consentono l'estensione di tipi già compilati.Partial types specifically do not allow already compiled types to be extended.

I tipi annidati possono essere dichiarati in più parti tramite il partial modificatore.Nested types may be declared in multiple parts by using the partial modifier. In genere, il tipo contenitore viene dichiarato utilizzando anche partial e ogni parte del tipo annidato viene dichiarata in una parte diversa del tipo che lo contiene.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.

Il partial modificatore non è consentito nelle dichiarazioni di delegato o enum.The partial modifier is not permitted on delegate or enum declarations.

AttributiAttributes

Gli attributi di un tipo parziale vengono determinati combinando, in un ordine non specificato, gli attributi di ogni parte.The attributes of a partial type are determined by combining, in an unspecified order, the attributes of each of the parts. Se un attributo viene inserito su più parti, equivale a specificare l'attributo più volte nel tipo.If an attribute is placed on multiple parts, it is equivalent to specifying the attribute multiple times on the type. Ad esempio, le due parti:For example, the two parts:

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

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

sono equivalenti a una dichiarazione come:are equivalent to a declaration such as:

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

Gli attributi dei parametri di tipo vengono combinati in modo simile.Attributes on type parameters combine in a similar fashion.

ModificatoriModifiers

Quando una dichiarazione di tipo parziale include una specifica di accessibilità, ovvero i public protected internal modificatori,, e, private deve essere conforme a tutte le altre parti che includono una specifica di accessibilità.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 nessuna parte di un tipo parziale include una specifica di accessibilità, al tipo viene assegnata l'accessibilità predefinita appropriata (accessibilità dichiarata).If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (Declared accessibility).

Se una o più dichiarazioni parziali di un tipo annidato includono un new modificatore, non viene segnalato alcun avviso se il tipo annidato nasconde un membro ereditato (nascosto tramite ereditarietà).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 una o più dichiarazioni parziali di una classe includono un abstract modificatore, la classe viene considerata astratta (classi astratte).If one or more partial declarations of a class include an abstract modifier, the class is considered abstract (Abstract classes). In caso contrario, la classe viene considerata non astratta.Otherwise, the class is considered non-abstract.

Se una o più dichiarazioni parziali di una classe includono un sealed modificatore, la classe viene considerata sealed (classi sealed).If one or more partial declarations of a class include a sealed modifier, the class is considered sealed (Sealed classes). In caso contrario, la classe viene considerata non sealed.Otherwise, the class is considered unsealed.

Si noti che una classe non può essere sia astratta che sealed.Note that a class cannot be both abstract and sealed.

Quando il unsafe modificatore viene utilizzato in una dichiarazione di tipo parziale, solo quella particolare parte viene considerata un contesto non sicuro (contesti non sicuri).When the unsafe modifier is used on a partial type declaration, only that particular part is considered an unsafe context (Unsafe contexts).

Parametri di tipo e vincoliType parameters and constraints

Se un tipo generico è dichiarato in più parti, ogni parte deve indicare i parametri di tipo.If a generic type is declared in multiple parts, each part must state the type parameters. Ogni parte deve avere lo stesso numero di parametri di tipo e lo stesso nome per ogni parametro di tipo, nell'ordine.Each part must have the same number of type parameters, and the same name for each type parameter, in order.

Quando una dichiarazione di tipo generico parziale include vincoli ( where clausole), i vincoli devono essere concordati con tutte le altre parti che includono vincoli.When a partial generic type declaration includes constraints (where clauses), the constraints must agree with all other parts that include constraints. In particolare, ogni parte che include vincoli deve contenere vincoli per lo stesso set di parametri di tipo e per ogni parametro di tipo i set di vincoli primari, secondari e del costruttore devono essere equivalenti.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. Due set di vincoli sono equivalenti se contengono gli stessi membri.Two sets of constraints are equivalent if they contain the same members. Se nessuna parte di un tipo generico parziale specifica vincoli di parametro di tipo, i parametri di tipo vengono considerati non vincolati.If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.

L'esempio:The 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>
{
    ...
}

è corretto perché le parti che includono vincoli (le prime due) specificano in modo efficace lo stesso set di vincoli PRIMARY, Secondary e Constructor per lo stesso set di parametri di tipo, rispettivamente.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 una dichiarazione di classe parziale include una specifica della classe di base, deve essere conforme a tutte le altre parti che includono una specifica della 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 nessuna parte di una classe parziale include una specifica della classe base, la classe di base diventa System.Object (classi base).If no part of a partial class includes a base class specification, the base class becomes System.Object (Base classes).

Interfacce di baseBase interfaces

Il set di interfacce di base per un tipo dichiarato in più parti è l'Unione delle interfacce di base specificate in ogni parte.The set of base interfaces for a type declared in multiple parts is the union of the base interfaces specified on each part. Una particolare interfaccia di base può essere denominata una sola volta in ogni parte, ma è consentito per più parti denominare le stesse interfacce di 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 essere presente solo un'implementazione dei membri di qualsiasi interfaccia di base specificata.There must only be one implementation of the members of any given base interface.

Nell'esempioIn the example

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

partial class C: IC {...}

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

il set di interfacce di base per la classe C è IA , IB e IC .the set of base interfaces for class C is IA, IB, and IC.

In genere, ogni parte fornisce un'implementazione delle interfacce dichiarate su tale parte; Tuttavia, questo non è un requisito.Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. Una parte può fornire l'implementazione per un'interfaccia dichiarata in una parte diversa: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
{
    ...
}

MembriMembers

Fatta eccezione per i metodi parziali (metodi parziali), il set di membri di un tipo dichiarato in più parti è semplicemente l'Unione del set di membri dichiarato in ogni 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. I corpi di tutte le parti della dichiarazione di tipo condividono lo stesso spazio di dichiarazione (dichiarazioni) e l'ambito di ogni membro (ambiti) si estende ai corpi di tutte le parti.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. Il dominio di accessibilità di qualsiasi membro include sempre tutte le parti del tipo di inclusione. un private membro dichiarato in una parte è accessibile liberamente da un'altra 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. Si tratta di un errore in fase di compilazione per dichiarare lo stesso membro in più di una parte del tipo, a meno che tale membro non sia un tipo con il partial modificatore.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;
    }
}

L'ordinamento dei membri all'interno di un tipo è raramente significativo per il codice C#, ma può essere significativo in caso di interazione con altri linguaggi e ambienti.The ordering of members within a type is rarely significant to C# code, but may be significant when interfacing with other languages and environments. In questi casi, l'ordinamento dei membri all'interno di un tipo dichiarato in più parti non è definito.In these cases, the ordering of members within a type declared in multiple parts is undefined.

Metodi parzialiPartial methods

I metodi parziali possono essere definiti in una parte di una dichiarazione di tipo e implementati in un altro.Partial methods can be defined in one part of a type declaration and implemented in another. L'implementazione è facoltativa. Se nessuna parte implementa il metodo parziale, la dichiarazione del metodo parziale e tutte le chiamate a tale metodo vengono rimosse dalla dichiarazione del tipo risultante dalla combinazione delle parti.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.

I metodi parziali non possono definire modificatori di accesso, ma sono implicitamente private .Partial methods cannot define access modifiers, but are implicitly private. Il tipo restituito deve essere void e i relativi parametri non possono avere il out modificatore.Their return type must be void, and their parameters cannot have the out modifier. L'identificatore partial viene riconosciuto come una parola chiave speciale in una dichiarazione di metodo solo se viene visualizzato immediatamente prima del void tipo; in caso contrario, può essere usato come identificatore normale.The identifier partial is recognized as a special keyword in a method declaration only if it appears right before the void type; otherwise it can be used as a normal identifier. Un metodo parziale non può implementare in modo esplicito i metodi di interfaccia.A partial method cannot explicitly implement interface methods.

Esistono due tipi di dichiarazioni di metodo parziali: se il corpo della dichiarazione di metodo è un punto e virgola, la dichiarazione viene definita *definendo una dichiarazione di metodo parziale _.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 il corpo viene fornito come _block *, la dichiarazione viene definita come dichiarazione di metodo parziale di implementazione.If the body is given as a _block*, the declaration is said to be an implementing partial method declaration. Nelle parti di una dichiarazione di tipo può essere presente una sola dichiarazione di metodo parziale di definizione con una determinata firma. può essere presente una sola dichiarazione di implementazione del metodo parziale con una firma specificata.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 viene fornita una dichiarazione di metodo parziale di implementazione, deve esistere una dichiarazione di metodo parziale di definizione corrispondente e le dichiarazioni devono corrispondere come specificato nell'esempio seguente: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:

  • Le dichiarazioni devono avere gli stessi modificatori (anche se non necessariamente nello stesso ordine), il nome del metodo, il numero di parametri di tipo e il numero di parametri.The declarations must have the same modifiers (although not necessarily in the same order), method name, number of type parameters and number of parameters.
  • I parametri corrispondenti nelle dichiarazioni devono avere gli stessi modificatori (anche se non necessariamente nello stesso ordine) e gli stessi tipi (differenze di modulo nei nomi dei parametri di 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).
  • I parametri di tipo corrispondenti nelle dichiarazioni devono avere gli stessi vincoli (differenze di modulo nei nomi dei parametri di tipo).Corresponding type parameters in the declarations must have the same constraints (modulo differences in type parameter names).

Una dichiarazione di metodo parziale di implementazione può comparire nella stessa parte della dichiarazione di metodo parziale di definizione corrispondente.An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.

Solo un metodo parziale di definizione fa parte della risoluzione dell'overload.Only a defining partial method participates in overload resolution. Di conseguenza, indipendentemente dal fatto che venga fornita una dichiarazione di implementazione, le espressioni di chiamata potrebbero risolversi in chiamate del metodo parziale.Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Poiché un metodo parziale restituisce sempre void , le espressioni di chiamata saranno sempre istruzioni di espressione.Because a partial method always returns void, such invocation expressions will always be expression statements. Inoltre, poiché un metodo parziale è implicitamente private , tali istruzioni si verificheranno sempre all'interno di una delle parti della dichiarazione di tipo all'interno della quale viene dichiarato il metodo parziale.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 nessuna parte di una dichiarazione di tipo parziale contiene una dichiarazione di implementazione per un determinato metodo parziale, qualsiasi istruzione di espressione che la richiama viene semplicemente rimossa dalla dichiarazione di tipo combinato.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. Pertanto, l'espressione di chiamata, incluse le espressioni costitutive, non ha alcun effetto in fase di esecuzione.Thus the invocation expression, including any constituent expressions, has no effect at run-time. Anche il metodo parziale viene rimosso e non sarà un membro della dichiarazione di tipo combinato.The partial method itself is also removed and will not be a member of the combined type declaration.

Se esiste una dichiarazione di implementazione per un determinato metodo parziale, le chiamate dei metodi parziali vengono mantenute.If an implementing declaration exist for a given partial method, the invocations of the partial methods are retained. Il metodo parziale genera una dichiarazione di metodo simile alla dichiarazione di metodo parziale di implementazione, ad eccezione di quanto segue:The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

  • Il partial modificatore non è inclusoThe partial modifier is not included
  • Gli attributi nella dichiarazione di metodo risultante sono gli attributi combinati della definizione e della dichiarazione di metodo parziale di implementazione in ordine non specificato.The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. I duplicati non vengono rimossi.Duplicates are not removed.
  • Gli attributi sui parametri della dichiarazione di metodo risultante sono gli attributi combinati dei parametri corrispondenti della definizione e la dichiarazione di metodo parziale di implementazione in ordine non specificato.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. I duplicati non vengono rimossi.Duplicates are not removed.

Se viene fornita una dichiarazione di definizione ma non una dichiarazione di implementazione per un metodo parziale M, si applicano le restrizioni seguenti:If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:

I metodi parziali sono utili per consentire a una parte di una dichiarazione di tipo di personalizzare il comportamento di un'altra parte, ad esempio, uno generato da uno strumento.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. Si consideri la seguente dichiarazione di classe parziale: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 questa classe viene compilata senza altre parti, le dichiarazioni di metodo parziale di definizione e le relative chiamate verranno rimosse e la dichiarazione della classe combinata risultante sarà equivalente alla seguente: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; }
    }
}

Si supponga che, tuttavia, venga fornita un'altra parte, che fornisce dichiarazioni di implementazione dei metodi parziali: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);
    }
}

La dichiarazione di classe combinata risultante sarà equivalente alla seguente: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);
    }
}

Associazione di nomiName binding

Sebbene ogni parte di un tipo estensibile debba essere dichiarata all'interno dello stesso spazio dei nomi, le parti vengono in genere scritte in diverse dichiarazioni dello spazio dei nomi.Although each part of an extensible type must be declared within the same namespace, the parts are typically written within different namespace declarations. usingÈ pertanto possibile che siano presenti direttive diverse (direttive using) per ogni parte.Thus, different using directives (Using directives) may be present for each part. Quando si interpretano nomi semplici (inferenza del tipo) in una parte, using vengono considerate solo le direttive delle dichiarazioni dello spazio dei nomi che racchiudono tale parte.When interpreting simple names (Type inference) within one part, only the using directives of the namespace declaration(s) enclosing that part are considered. Ciò può comportare che lo stesso identificatore abbia significati diversi in parti diverse: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
    }
}

Membri di classeClass members

I membri di una classe sono costituiti dai membri introdotti dal class_member_declaration s e dai membri ereditati dalla classe di base diretta.The members of a class consist of the members introduced by its class_member_declaration s 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
    ;

I membri di un tipo di classe sono divisi nelle categorie seguenti:The members of a class type are divided into the following categories:

  • Costanti, che rappresentano i valori costanti associati alla classe (costanti).Constants, which represent constant values associated with the class (Constants).
  • Campi, ovvero le variabili della classe (campi).Fields, which are the variables of the class (Fields).
  • Metodi, che implementano i calcoli e le azioni che possono essere eseguiti dalla classe (Metodi).Methods, which implement the computations and actions that can be performed by the class (Methods).
  • Proprietà che definiscono le caratteristiche denominate e le azioni associate alla lettura e alla scrittura di tali caratteristiche (Proprietà).Properties, which define named characteristics and the actions associated with reading and writing those characteristics (Properties).
  • Eventi, che definiscono le notifiche che possono essere generate dalla classe (eventi).Events, which define notifications that can be generated by the class (Events).
  • Indicizzatori, che consentono di indicizzare le istanze della classe nello stesso modo (sintatticamente) come matrici (indicizzatori).Indexers, which permit instances of the class to be indexed in the same way (syntactically) as arrays (Indexers).
  • Operatori, che definiscono gli operatori di espressione che possono essere applicati alle istanze della classe (operatori).Operators, which define the expression operators that can be applied to instances of the class (Operators).
  • Costruttori di istanze, che implementano le azioni necessarie per inizializzare istanze della classe (costruttori di istanza)Instance constructors, which implement the actions required to initialize instances of the class (Instance constructors)
  • Distruttori, che implementano le azioni da eseguire prima che le istanze della classe vengano eliminate definitivamente (distruttori).Destructors, which implement the actions to be performed before instances of the class are permanently discarded (Destructors).
  • Costruttori statici, che implementano le azioni necessarie per inizializzare la classe stessa (costruttori statici).Static constructors, which implement the actions required to initialize the class itself (Static constructors).
  • Tipi, che rappresentano i tipi locali della classe (tipi annidati).Types, which represent the types that are local to the class (Nested types).

I membri che possono contenere codice eseguibile sono collettivamente noti come membri di funzione del tipo di classe.Members that can contain executable code are collectively known as the function members of the class type. I membri della funzione di un tipo di classe sono i metodi, le proprietà, gli eventi, gli indicizzatori, gli operatori, i costruttori di istanza, i distruttori e i costruttori statici del tipo di 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.

Un class_declaration crea un nuovo spazio di dichiarazione (dichiarazioni) e i class_member_declaration contenuta immediatamente dal class_declaration introducono nuovi membri in questo spazio di dichiarazione.A class_declaration creates a new declaration space (Declarations), and the class_member_declaration s immediately contained by the class_declaration introduce new members into this declaration space. Le regole seguenti si applicano a class_member_declaration s:The following rules apply to class_member_declaration s:

  • I costruttori di istanza, i distruttori e i costruttori statici devono avere lo stesso nome della classe che lo contiene immediatamente.Instance constructors, destructors and static constructors must have the same name as the immediately enclosing class. Tutti gli altri membri devono avere nomi diversi dal nome della classe che lo contiene immediatamente.All other members must have names that differ from the name of the immediately enclosing class.
  • Il nome di una costante, un campo, una proprietà, un evento o un tipo deve essere diverso dai nomi di tutti gli altri membri dichiarati nella stessa 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.
  • Il nome di un metodo deve essere diverso da quello dei nomi di tutti gli altri metodi non dichiarati nella stessa classe.The name of a method must differ from the names of all other non-methods declared in the same class. Inoltre, la firma (firme e overload) di un metodo deve essere diversa dalle firme di tutti gli altri metodi dichiarati nella stessa classe e due metodi dichiarati nella stessa classe potrebbero non avere firme che differiscono solo per 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.
  • La firma di un costruttore di istanza deve essere diversa dalle firme di tutti gli altri costruttori di istanza dichiarati nella stessa classe e due costruttori dichiarati nella stessa classe potrebbero non avere firme che differiscono solo per ref e out .The signature of an instance constructor must differ from the signatures of all other instance constructors declared in the same class, and two constructors declared in the same class may not have signatures that differ solely by ref and out.
  • La firma di un indicizzatore deve essere diversa dalle firme di tutti gli altri indicizzatori dichiarati nella stessa classe.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • La firma di un operatore deve essere diversa dalle firme di tutti gli altri operatori dichiarati nella stessa classe.The signature of an operator must differ from the signatures of all other operators declared in the same class.

I membri ereditati di un tipo di classe (ereditarietà) non fanno parte dello spazio della dichiarazione di una classe.The inherited members of a class type (Inheritance) are not part of the declaration space of a class. Pertanto, una classe derivata può dichiarare un membro con lo stesso nome o firma di un membro ereditato, che in effetti nasconde il membro ereditato.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).

Tipo di istanzaThe instance type

A ogni dichiarazione di classe è associato un tipo associato (tipi associati e non associati), il tipo di istanza.Each class declaration has an associated bound type (Bound and unbound types), the instance type. Per una dichiarazione di classe generica, il tipo di istanza viene creato creando un tipo costruito (tipi costruiti) dalla dichiarazione del tipo, con ciascuno degli argomenti di tipo forniti che corrispondono al parametro di tipo corrispondente.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. Poiché il tipo di istanza utilizza i parametri di tipo, può essere utilizzato solo se i parametri di tipo sono inclusi nell'ambito. ovvero, all'interno della dichiarazione di 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. Il tipo di istanza è il tipo di this per il codice scritto all'interno della dichiarazione di classe.The instance type is the type of this for code written inside the class declaration. Per le classi non generiche, il tipo di istanza è semplicemente la classe dichiarata.For non-generic classes, the instance type is simply the declared class. Di seguito vengono illustrate diverse dichiarazioni di classe insieme ai relativi tipi di istanza: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

Membri di tipi costruitiMembers of constructed types

I membri non ereditati di un tipo costruito vengono ottenuti sostituendo, per ogni type_parameter nella dichiarazione del membro, l' type_argument corrispondente del tipo costruito.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. Il processo di sostituzione è basato sul significato semantico delle dichiarazioni di tipo e non è semplicemente una sostituzione testuale.The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.

Ad esempio, data la dichiarazione di classe genericaFor 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) {...}
}

il tipo costruito Gen<int[],IComparable<string>> ha i membri seguenti: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) {...}

Il tipo del membro a nella dichiarazione di classe generica Gen è "matrice bidimensionale di T ", pertanto il tipo del membro a nel tipo costruito precedente è "matrice bidimensionale di una matrice unidimensionale di int ", o int[,][] .The type of the member a in the generic class declaration Gen is "two-dimensional array of T", so the type of the member a in the constructed type above is "two-dimensional array of one-dimensional array of int", or int[,][].

All'interno dei membri della funzione di istanza, il tipo di this è il tipo di istanza (il tipo di istanza) della dichiarazione che lo contiene.Within instance function members, the type of this is the instance type (The instance type) of the containing declaration.

Tutti i membri di una classe generica possono usare parametri di tipo di qualsiasi classe contenitore, direttamente o come parte di un tipo costruito.All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. Quando un particolare tipo costruito chiuso (tipi aperti e chiusi) viene utilizzato in fase di esecuzione, ogni utilizzo di un parametro di tipo viene sostituito con l'argomento di tipo effettivo fornito al tipo costruito.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. Ad esempio: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
    }
}

EreditarietàInheritance

Una classe eredita i membri del tipo di classe di base diretta.A class inherits the members of its direct base class type. L'ereditarietà indica che una classe contiene in modo implicito tutti i membri del relativo tipo di classe di base diretta, ad eccezione dei costruttori di istanza, dei distruttori e dei costruttori statici della classe di 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. Alcuni aspetti importanti dell'ereditarietà sono:Some important aspects of inheritance are:

  • L'ereditarietà è transitiva.Inheritance is transitive. Se C è derivato da B ed B è derivato da A , C eredita i membri dichiarati in, nonché B i membri dichiarati in A .If C is derived from B, and B is derived from A, then C inherits the members declared in B as well as the members declared in A.
  • Una classe derivata estende la relativa classe di base diretta.A derived class extends its direct base class. Una classe derivata può aggiungere nuovi membri a quelli ereditati, ma non può rimuovere la definizione di un membro ereditato.A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • I costruttori di istanza, i distruttori e i costruttori statici non vengono ereditati, ma tutti gli altri membri sono, indipendentemente dall'accessibilità dichiarata (accesso ai membri).Instance constructors, destructors, and static constructors are not inherited, but all other members are, regardless of their declared accessibility (Member access). Tuttavia, a seconda dell'accessibilità dichiarata, i membri ereditati potrebbero non essere accessibili in una classe derivata.However, depending on their declared accessibility, inherited members might not be accessible in a derived class.
  • Una classe derivata può nascondere (nascosto tramite ereditarietà) i membri ereditati dichiarando i nuovi membri con lo stesso nome o la stessa firma.A derived class can hide (Hiding through inheritance) inherited members by declaring new members with the same name or signature. Si noti tuttavia che nascondere un membro ereditato non rimuove quel membro, ma rende semplicemente inaccessibile tale membro direttamente tramite la classe derivata.Note however that hiding an inherited member does not remove that member—it merely makes that member inaccessible directly through the derived class.
  • Un'istanza di una classe contiene un set di tutti i campi di istanza dichiarati nella classe e le relative classi di base e una conversione implicita (conversioni di riferimenti implicite) esiste da un tipo di classe derivata a uno dei relativi tipi di classe di 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. Pertanto, un riferimento a un'istanza di una classe derivata può essere considerato come un riferimento a un'istanza di una delle relative classi di base.Thus, a reference to an instance of some derived class can be treated as a reference to an instance of any of its base classes.
  • Una classe può dichiarare metodi, proprietà e indicizzatori virtuali e le classi derivate possono eseguire l'override dell'implementazione di questi membri di funzione.A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. Ciò consente alle classi di presentare un comportamento polimorfico in cui le azioni eseguite dalla chiamata di un membro di funzione variano a seconda del tipo di runtime dell'istanza tramite cui viene richiamato il membro della funzione.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.

Il membro ereditato di un tipo di classe costruito è costituito dai membri del tipo di classe di base immediato (classi base), che viene trovato sostituendo gli argomenti di tipo del tipo costruito per ogni occorrenza dei parametri di tipo corrispondenti nella specifica 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. Questi membri, a loro volta, vengono trasformati sostituendo, per ogni type_parameter nella dichiarazione del membro, l' type_argument corrispondente della specifica 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) {...}
}

Nell'esempio precedente, il tipo costruito D<int> ha un membro non ereditato public int G(string s) ottenuto sostituendo l'argomento di tipo int per il parametro di tipo T .In the above example, the constructed type D<int> has a non-inherited member public int G(string s) obtained by substituting the type argument int for the type parameter T. D<int> dispone anche di un membro ereditato dalla dichiarazione di classe B .D<int> also has an inherited member from the class declaration B. Questo membro ereditato è determinato dalla prima determinazione del tipo di classe di base di B<int[]> D<int> sostituendo int per T nella specifica della classe base B<T[]> .This inherited member is determined by first determining the base class type B<int[]> of D<int> by substituting int for T in the base class specification B<T[]>. Quindi, come argomento di tipo per B , int[] viene sostituito con U in public U F(long index) , cedendo il membro ereditato public int[] F(long index) .Then, as a type argument to B, int[] is substituted for U in public U F(long index), yielding the inherited member public int[] F(long index).

Nuovo modificatoreThe new modifier

Un class_member_declaration è autorizzato a dichiarare un membro con lo stesso nome o firma di un membro ereditato.A class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. Quando si verifica questo problema, viene detto che il membro della classe derivata nasconde il membro della classe base.When this occurs, the derived class member is said to hide the base class member. Nascondere un membro ereditato non viene considerato un errore, ma il compilatore genera un avviso.Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. Per non visualizzare l'avviso, la dichiarazione del membro della classe derivata può includere un new modificatore per indicare che il membro derivato deve nascondere il membro di 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. Questo argomento viene trattato ulteriormente in modo nascosto tramite ereditarietà.This topic is discussed further in Hiding through inheritance.

Se un new modificatore è incluso in una dichiarazione che non nasconde un membro ereditato, viene emesso un avviso a tale effetto.If a new modifier is included in a declaration that doesn't hide an inherited member, a warning to that effect is issued. Questo avviso viene eliminato rimuovendo il new modificatore.This warning is suppressed by removing the new modifier.

Modificatori di accessoAccess modifiers

Una class_member_declaration può avere uno dei cinque tipi possibili di accessibilità dichiarata (accessibilitàdichiarata): public , protected internal , protected , internal o private .A class_member_declaration can have any one of the five possible kinds of declared accessibility (Declared accessibility): public, protected internal, protected, internal, or private. Ad eccezione della protected internal combinazione, si tratta di un errore in fase di compilazione per specificare più di un modificatore di accesso.Except for the protected internal combination, it is a compile-time error to specify more than one access modifier. Quando in un class_member_declaration non sono inclusi modificatori di accesso, private viene utilizzato.When a class_member_declaration does not include any access modifiers, private is assumed.

Tipi costitutiviConstituent types

I tipi utilizzati nella dichiarazione di un membro sono denominati tipi costitutivi di tale membro.Types that are used in the declaration of a member are called the constituent types of that member. I tipi costitutivi possibili sono il tipo di una costante, un campo, una proprietà, un evento o un indicizzatore, il tipo restituito di un metodo o un operatore e i tipi di parametro di un metodo, un indicizzatore, un operatore o un costruttore di istanza.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. I tipi costitutivi di un membro devono essere accessibili almeno quanto il membro stesso (vincoli di accessibilità).The constituent types of a member must be at least as accessible as that member itself (Accessibility constraints).

Membri statici e di istanzaStatic and instance members

I membri di una classe sono *membri statici _ o _ membri di istanza *.Members of a class are either static members _ or _instance members**. In generale, è utile considerare i membri statici come appartenenti ai tipi di classe e ai membri di istanza come appartenenti a oggetti (istanze di tipi di 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 un campo, un metodo, una proprietà, un evento, un operatore o una dichiarazione di costruttore include un static modificatore, dichiara un membro statico.When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. Inoltre, una costante o una dichiarazione di tipo dichiara in modo implicito un membro statico.In addition, a constant or type declaration implicitly declares a static member. I membri statici presentano le seguenti caratteristiche:Static members have the following characteristics:

  • Quando si fa riferimento a un membro statico M in un member_access (accesso ai membri) del form E.M , E deve indicare un tipo che contiene 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. Si tratta di un errore in fase di compilazione per la E denotazione di un'istanza di.It is a compile-time error for E to denote an instance.
  • Un campo statico identifica esattamente un percorso di archiviazione da condividere con tutte le istanze di un determinato tipo di classe closed.A static field identifies exactly one storage location to be shared by all instances of a given closed class type. Indipendentemente dal numero di istanze di un determinato tipo di classe chiusa, esiste una sola copia di un campo statico.No matter how many instances of a given closed class type are created, there is only ever one copy of a static field.
  • Un membro di funzione statica (metodo, proprietà, evento, operatore o costruttore) non funziona in un'istanza specifica e si tratta di un errore in fase di compilazione a cui fare riferimento this in un membro di funzione di questo tipo.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 un campo, un metodo, una proprietà, un evento, un indicizzatore, un costruttore o una dichiarazione di distruttore non include un static modificatore, dichiara un membro di istanza.When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. Un membro di istanza viene talvolta denominato membro non statico. I membri di istanza hanno le caratteristiche seguenti:(An instance member is sometimes called a non-static member.) Instance members have the following characteristics:

  • Quando M si fa riferimento a un membro di istanza in un member_access (accesso ai membri) del form E.M , E deve indicare un'istanza di un tipo contenente 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. Si tratta di un errore in fase di binding per E per indicare un tipo.It is a binding-time error for E to denote a type.
  • Ogni istanza di una classe contiene un set separato di tutti i campi di istanza della classe.Every instance of a class contains a separate set of all instance fields of the class.
  • Un membro della funzione di istanza (metodo, proprietà, indicizzatore, costruttore di istanza o distruttore) opera su un'istanza specificata della classe e questa istanza è accessibile come this (questo accesso).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).

Nell'esempio seguente vengono illustrate le regole per l'accesso ai membri statici e di istanza: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
    }
}

Il F Metodo Mostra che in un membro della funzione di istanza, un simple_name (nomi semplici) può essere usato per accedere ai membri di istanza e ai membri statici.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. Il G Metodo Mostra che in un membro della funzione statica, si tratta di un errore in fase di compilazione per accedere a un membro di istanza tramite un simple_name.The G method shows that in a static function member, it is a compile-time error to access an instance member through a simple_name. Il Main Metodo Mostra che in un member_access (accesso ai membri), i membri dell'istanza devono essere accessibili tramite le istanze e i membri statici devono essere accessibili tramite i tipi.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.

Tipi annidatiNested types

Un tipo dichiarato all'interno di una dichiarazione di classe o struct viene chiamato *tipo annidato _.A type declared within a class or struct declaration is called a *nested type _. Un tipo dichiarato all'interno di un'unità di compilazione o di uno spazio dei nomi è denominato _ tipo non annidato *.A type that is declared within a compilation unit or namespace is called a _*non-nested type**.

Nell'esempioIn the example

using System;

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

Bla classe è un tipo annidato perché viene dichiarata all'interno della classe A e A la classe è un tipo non annidato perché viene dichiarata all'interno di un'unità di compilazione.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 completoFully qualified name

Il nome completo (nomicompleti) per un tipo annidato è S.N dove S è il nome completo del tipo in cui è dichiarato il tipo N .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.

Accessibilità dichiarataDeclared accessibility

I tipi non annidati possono avere public o internal dichiarati accessibilità e hanno internal dichiarato l'accessibilità per impostazione predefinita.Non-nested types can have public or internal declared accessibility and have internal declared accessibility by default. I tipi annidati possono avere anche queste forme di accessibilità dichiarata, oltre a una o più forme aggiuntive di accessibilità dichiarate, a seconda che il tipo che lo contiene sia una classe o uno struct:Nested types can have these forms of declared accessibility too, plus one or more additional forms of declared accessibility, depending on whether the containing type is a class or struct:

  • Un tipo annidato dichiarato in una classe può avere una qualsiasi delle cinque forme di accessibilità dichiarata ( public ,, protected internal protected , internal o private ) e, come altri membri della classe, per impostazione predefinita è l' private accessibilità dichiarata.A nested type that is declared in a class can have any of five forms of declared accessibility (public, protected internal, protected, internal, or private) and, like other class members, defaults to private declared accessibility.
  • Un tipo annidato dichiarato in uno struct può avere tre forme di accessibilità dichiarata ( public , internal o private ) e, come altri membri struct, per impostazione predefinita è l' private accessibilità dichiarata.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.

L'esempio:The 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 {...} }
}

dichiara una classe annidata privata Node .declares a private nested class Node.

NascondereHiding

Un tipo annidato può nascondere, ovvero nascondere, unmembro di base.A nested type may hide (Name hiding) a base member. Il new modificatore è consentito nelle dichiarazioni di tipo annidato in modo che il nascondiglio possa essere espresso in modo esplicito.The new modifier is permitted on nested type declarations so that hiding can be expressed explicitly. L'esempio:The 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 una classe annidata M che nasconde il metodo M definito in Base .shows a nested class M that hides the method M defined in Base.

Questo accessothis access

Un tipo annidato e il relativo tipo che lo contiene non hanno una relazione speciale per quanto riguarda THIS_ACCESS (questo accesso).A nested type and its containing type do not have a special relationship with regard to this_access (This access). In particolare, this all'interno di un tipo annidato non può essere usato per fare riferimento ai membri di istanza del tipo che lo contiene.Specifically, this within a nested type cannot be used to refer to instance members of the containing type. Nei casi in cui un tipo annidato deve accedere ai membri dell'istanza del tipo che lo contiene, è possibile fornire l'accesso specificando l'oggetto this per l'istanza del tipo che lo contiene come argomento del costruttore per il tipo annidato.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. Vedere l'esempio seguente:The 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 questa tecnica.shows this technique. Un'istanza di C Crea un'istanza di Nested e passa il relativo this costruttore a per Nested fornire un accesso successivo ai C membri dell'istanza di.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.

Accesso a membri privati e protetti del tipo che lo contieneAccess to private and protected members of the containing type

Un tipo annidato può accedere a tutti i membri accessibili al tipo che lo contiene, inclusi i membri del tipo che lo contiene private e l' protected accessibilità dichiarata.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. L'esempio:The 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 una classe C che contiene una classe annidata Nested .shows a class C that contains a nested class Nested. All'interno di Nested , il metodo G chiama il metodo statico F definito in C e F ha un'accessibilità dichiarata privata.Within Nested, the method G calls the static method F defined in C, and F has private declared accessibility.

Un tipo annidato può accedere anche a membri protetti definiti in un tipo di base del tipo che lo contiene.A nested type also may access protected members defined in a base type of its containing type. Nell'esempioIn the example

using System;

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

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

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

la classe annidata Derived.Nested accede al metodo protetto F definito nella Derived classe di base, Base , chiamando tramite un'istanza di Derived .the nested class Derived.Nested accesses the protected method F defined in Derived's base class, Base, by calling through an instance of Derived.

Tipi annidati in classi genericheNested types in generic classes

Una dichiarazione di classe generica può contenere dichiarazioni di tipo annidate.A generic class declaration can contain nested type declarations. I parametri di tipo della classe contenitore possono essere utilizzati all'interno dei tipi annidati.The type parameters of the enclosing class can be used within the nested types. Una dichiarazione di tipo annidato può contenere parametri di tipo aggiuntivi che si applicano solo al tipo annidato.A nested type declaration can contain additional type parameters that apply only to the nested type.

Ogni dichiarazione di tipo contenuta in una dichiarazione di classe generica è implicitamente una dichiarazione di tipo generico.Every type declaration contained within a generic class declaration is implicitly a generic type declaration. Quando si scrive un riferimento a un tipo annidato all'interno di un tipo generico, il tipo costruito che lo contiene, inclusi i relativi argomenti di tipo, deve essere denominato.When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, must be named. Tuttavia, dall'interno della classe esterna, il tipo annidato può essere usato senza qualificazione; il tipo di istanza della classe esterna può essere utilizzato in modo implicito quando si costruisce il tipo annidato.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. Nell'esempio seguente vengono illustrate tre diversi modi corretti per fare riferimento a un tipo costruito creato da Inner . i primi due sono equivalenti: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
    }
}

Sebbene non sia uno stile di programmazione valido, un parametro di tipo in un tipo annidato può nascondere un membro o un parametro di tipo dichiarato nel tipo esterno: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
    }
}

Nomi di membri riservatiReserved member names

Per semplificare l'implementazione della fase di esecuzione di C# sottostante, per ogni dichiarazione del membro di origine che è una proprietà, un evento o un indicizzatore, l'implementazione deve riservare due firme del metodo in base al tipo di dichiarazione del membro, al nome e al relativo 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. Si tratta di un errore in fase di compilazione per un programma che dichiara un membro la cui firma corrisponde a una di queste firme riservate, anche se l'implementazione della fase di esecuzione sottostante non utilizza tali prenotazioni.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.

I nomi riservati non introducono dichiarazioni, quindi non partecipano alla ricerca di membri.The reserved names do not introduce declarations, thus they do not participate in member lookup. Tuttavia, le firme del metodo riservato associate a una dichiarazione partecipano all'ereditarietà (ereditarietà) e possono essere nascoste con il new modificatore (il nuovo modificatore).However, a declaration's associated reserved method signatures do participate in inheritance (Inheritance), and can be hidden with the new modifier (The new modifier).

La prenotazione di questi nomi svolge tre scopi:The reservation of these names serves three purposes:

  • Per consentire all'implementazione sottostante di usare un identificatore comune come nome di metodo per ottenere o impostare l'accesso alla funzionalità del linguaggio C#.To allow the underlying implementation to use an ordinary identifier as a method name for get or set access to the C# language feature.
  • Per consentire ad altre lingue di interagire con un identificatore ordinario come nome di metodo per ottenere o impostare l'accesso alla funzionalità del linguaggio C#.To allow other languages to interoperate using an ordinary identifier as a method name for get or set access to the C# language feature.
  • Per garantire che l'origine accettata da un compilatore conforme venga accettata da un'altra, rendendo le specifiche dei nomi di membro riservati coerenti in tutte le implementazioni di C#.To help ensure that the source accepted by one conforming compiler is accepted by another, by making the specifics of reserved member names consistent across all C# implementations.

La dichiarazione di un distruttore (distruttori) causa anche la riservatezza di una firma (nomi dei membri riservati ai distruttori).The declaration of a destructor (Destructors) also causes a signature to be reserved (Member names reserved for destructors).

Nomi dei membri riservati per le proprietàMember names reserved for properties

Per una proprietà P (Proprietà) di tipo T , sono riservate le firme seguenti:For a property P (Properties) of type T, the following signatures are reserved:

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

Entrambe le firme sono riservate, anche se la proprietà è di sola lettura o di sola scrittura.Both signatures are reserved, even if the property is read-only or write-only.

Nell'esempioIn the example

using System;

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

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

    new public void set_P(int value) {
    }
}

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

una classe A definisce una proprietà di sola lettura P , riservando in tal modo le firme per i get_P set_P metodi e.a class A defines a read-only property P, thus reserving signatures for get_P and set_P methods. Una classe B deriva da A e nasconde entrambe le firme riservate.A class B derives from A and hides both of these reserved signatures. L'esempio produce l'output:The example produces the output:

123
123
456

Nomi dei membri riservati per gli eventiMember names reserved for events

Per un evento E (Events) del tipo delegato T , vengono riservate le firme seguenti:For an event E (Events) of delegate type T, the following signatures are reserved:

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

Nomi dei membri riservati per gli indicizzatoriMember names reserved for indexers

Per un indicizzatore (indicizzatori) di tipo T con parameter-list L , sono riservate le firme seguenti: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);

Entrambe le firme sono riservate, anche se l'indicizzatore è di sola lettura o di sola scrittura.Both signatures are reserved, even if the indexer is read-only or write-only.

Il nome del membro Item è inoltre riservato.Furthermore the member name Item is reserved.

Nomi dei membri riservati per i distruttoriMember names reserved for destructors

Per una classe che contiene un distruttore (distruttori), è riservata la firma seguente:For a class containing a destructor (Destructors), the following signature is reserved:

void Finalize();

CostantiConstants

*Constant _ è un membro di classe che rappresenta un valore costante, ovvero un valore che può essere calcolato in fase di compilazione.A *constant _ is a class member that represents a constant value: a value that can be computed at compile-time. Una _constant_declaration * introduce una o più costanti di un tipo specificato.A _constant_declaration* introduces one or more constants of a given type.

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

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

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Un constant_declaration può includere un set di attributi (attributi), un new modificatore (il nuovo modificatore) e una combinazione valida dei quattro modificatori di accesso (modificatori di accesso).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). Gli attributi e i modificatori si applicano a tutti i membri dichiarati dal constant_declaration.The attributes and modifiers apply to all of the members declared by the constant_declaration. Sebbene le costanti siano considerate membri statici, un constant_declaration non richiede né consente un static modificatore.Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. È un errore che lo stesso modificatore venga visualizzato più volte in una dichiarazione di costante.It is an error for the same modifier to appear multiple times in a constant declaration.

Il tipo di una constant_declaration specifica il tipo di membri introdotti dalla dichiarazione.The type of a constant_declaration specifies the type of the members introduced by the declaration. Il tipo è seguito da un elenco di constant_declarator s, ciascuno dei quali introduce un nuovo membro.The type is followed by a list of constant_declarator s, each of which introduces a new member. Un constant_declarator è costituito da un identificatore che assegna un nome al membro, seguito da un = token "", seguito da un constant_expression (espressioni costanti) che fornisce il valore del 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.

Il tipo specificato in una dichiarazione di costante deve essere sbyte ,, byte short , ushort , int , uint , long , ulong , char , float , double , decimal , bool , string , un enum_type o una reference_type.The type specified in a constant declaration must be sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, an enum_type, or a reference_type. Ogni constant_expression deve restituire un valore del tipo di destinazione o di un tipo che può essere convertito nel tipo di destinazione mediante una conversione implicita (conversioni implicite).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).

Il tipo di una costante deve essere accessibile almeno quanto la costante (vincoli di accessibilità).The type of a constant must be at least as accessible as the constant itself (Accessibility constraints).

Il valore di una costante viene ottenuto in un'espressione utilizzando un simple_name (nomi semplici) o una member_access (accesso ai membri).The value of a constant is obtained in an expression using a simple_name (Simple names) or a member_access (Member access).

Una costante può partecipare a una constant_expression.A constant can itself participate in a constant_expression. Pertanto, una costante può essere usata in qualsiasi costrutto che richiede un constant_expression.Thus, a constant may be used in any construct that requires a constant_expression. Esempi di tali costrutti includono case etichette, goto case istruzioni, enum dichiarazioni di membri, attributi e altre dichiarazioni di costanti.Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

Come descritto in espressioni costanti, un constant_expression è un'espressione che può essere valutata interamente in fase di compilazione.As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time. Poiché l'unico modo per creare un valore non null di un reference_type diverso da string consiste nell'applicare l' new operatore e poiché l' new operatore non è consentito in una constant_expression, l'unico valore possibile per le costanti di reference_type s diverso da 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_type s other than string is null.

Quando si desidera un nome simbolico per un valore costante, ma quando il tipo di tale valore non è consentito in una dichiarazione di costante oppure quando il valore non può essere calcolato in fase di compilazione da un constant_expression, è readonly possibile utilizzare un campo (campi di sola lettura).When a symbolic name for a constant value is desired, but when the type of that value is not permitted in a constant declaration, or when the value cannot be computed at compile-time by a constant_expression, a readonly field (Readonly fields) may be used instead.

Una dichiarazione di costante che dichiara più costanti è equivalente a più dichiarazioni di costanti singole con gli stessi attributi, modificatori e tipo.A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. Ad esempio:For 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;
}

È consentito che le costanti dipendano da altre costanti all'interno dello stesso programma purché le dipendenze non siano di natura circolare.Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. Il compilatore dispone automaticamente di una valutazione delle dichiarazioni di costanti nell'ordine appropriato.The compiler automatically arranges to evaluate the constant declarations in the appropriate order. Nell'esempioIn 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;
}

il compilatore valuta prima di tutto A.Y , quindi valuta B.Z e infine valuta A.X , producendo i valori 10 , 11 e 12 .the compiler first evaluates A.Y, then evaluates B.Z, and finally evaluates A.X, producing the values 10, 11, and 12. Le dichiarazioni di costanti possono dipendere da costanti di altri programmi, ma tali dipendenze sono possibili solo in una direzione.Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction. Facendo riferimento all'esempio precedente, se A e B sono stati dichiarati in programmi distinti, è possibile che A.X dipenda da B.Z , ma B.Z potrebbe non dipendere simultaneamente da A.Y .Referring to the example above, if A and B were declared in separate programs, it would be possible for A.X to depend on B.Z, but B.Z could then not simultaneously depend on A.Y.

CampiFields

Un *Field _ è un membro che rappresenta una variabile associata a un oggetto o a una classe.A *field _ is a member that represents a variable associated with an object or class. Un _field_declaration * introduce uno o più campi di un tipo specificato.A _field_declaration* introduces one or more fields of a given type.

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

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

variable_declarators
    : variable_declarator (',' variable_declarator)*
    ;

variable_declarator
    : identifier ('=' variable_initializer)?
    ;

variable_initializer
    : expression
    | array_initializer
    ;

Un field_declaration può includere un set di attributi (attributi), un new modificatore (il nuovo modificatore), una combinazione valida dei quattro modificatori di accesso (modificatori di accesso) e un static modificatore (campi statici e di istanza).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). Inoltre, un field_declaration può includere un readonly modificatore (campidi sola lettura) o un volatile modificatore (campi volatili) ma non entrambi.In addition, a field_declaration may include a readonly modifier (Readonly fields) or a volatile modifier (Volatile fields) but not both. Gli attributi e i modificatori si applicano a tutti i membri dichiarati dal field_declaration.The attributes and modifiers apply to all of the members declared by the field_declaration. È un errore che lo stesso modificatore venga visualizzato più volte in una dichiarazione di campo.It is an error for the same modifier to appear multiple times in a field declaration.

Il tipo di una field_declaration specifica il tipo di membri introdotti dalla dichiarazione.The type of a field_declaration specifies the type of the members introduced by the declaration. Il tipo è seguito da un elenco di variable_declarator s, ciascuno dei quali introduce un nuovo membro.The type is followed by a list of variable_declarator s, each of which introduces a new member. Un variable_declarator è costituito da un identificatore che assegna un nome a tale membro, seguito facoltativamente da un = token "" e da un variable_initializer (inizializzatori di variabile) che fornisce il valore iniziale di tale 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.

Il tipo di un campo deve essere accessibile almeno quanto il campo stesso (vincoli di accessibilità).The type of a field must be at least as accessible as the field itself (Accessibility constraints).

Il valore di un campo viene ottenuto in un'espressione utilizzando un simple_name (nomi semplici) o una member_access (accesso ai membri).The value of a field is obtained in an expression using a simple_name (Simple names) or a member_access (Member access). Il valore di un campo non di sola lettura viene modificato utilizzando un' assegnazione (operatori di assegnazione).The value of a non-readonly field is modified using an assignment (Assignment operators). Il valore di un campo non di sola lettura può essere ottenuto e modificato usando gli operatori di incremento e decremento suffisso (operatori di incremento e decremento suffisso) e gli operatori di incremento e decremento del prefisso (operatori di incremento e decrementodel prefisso).The value of a non-readonly field can be both obtained and modified using postfix increment and decrement operators (Postfix increment and decrement operators) and prefix increment and decrement operators (Prefix increment and decrement operators).

Una dichiarazione di campo che dichiara più campi equivale a più dichiarazioni di singoli campi con gli stessi attributi, modificatori e tipo.A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. Ad esempio:For 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;
}

Campi statici e di istanzaStatic and instance fields

Quando una dichiarazione di campo include un static modificatore, i campi introdotti dalla dichiarazione sono *campi statici _.When a field declaration includes a static modifier, the fields introduced by the declaration are *static fields _. Quando non static è presente alcun modificatore, i campi introdotti dalla dichiarazione sono campi di istanza.When no static modifier is present, the fields introduced by the declaration are instance fields. I campi statici e di istanza sono due dei diversi tipi di variabili (variabili) supportati da C# e, a volte, sono denominati rispettivamente come variabili statiche e _ variabili di istanza *.Static fields and instance fields are two of the several kinds of variables (Variables) supported by C#, and at times they are referred to as static variables and _*instance variables**, respectively.

Un campo statico non fa parte di un'istanza specifica; viene invece condiviso tra tutte le istanze di un tipo chiuso (tipi aperti e chiusi).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). Indipendentemente dal numero di istanze di un tipo di classe chiuso, esiste una sola copia di un campo statico per il dominio dell'applicazione associato.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.

Ad esempio:For example:

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

    public C() {
        count++;
    }

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

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

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

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

Un campo di istanza appartiene a un'istanza di.An instance field belongs to an instance. In particolare, ogni istanza di una classe contiene un set separato di tutti i campi di istanza di tale classe.Specifically, every instance of a class contains a separate set of all the instance fields of that class.

Quando si fa riferimento a un campo in un member_access (accesso ai membri) del modulo E.M , se M è un campo statico, E deve indicare un tipo che contiene M E se M è un campo di istanza, e deve indicare un'istanza di un tipo contenente 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.

Le differenze tra i membri statici e i membri di istanza sono illustrate ulteriormente nei membri statici e di istanza.The differences between static and instance members are discussed further in Static and instance members.

Campi di sola letturaReadonly fields

Quando un field_declaration include un readonly modificatore, i campi introdotti dalla dichiarazione sono campi di sola lettura.When a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Le assegnazioni dirette ai campi di sola lettura possono essere eseguite solo come parte della dichiarazione o in un costruttore di istanza o in un costruttore statico nella stessa 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. Un campo di sola lettura può essere assegnato più volte in questi contesti. In particolare, le assegnazioni dirette a un readonly campo sono consentite solo nei contesti seguenti:(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:

  • Nell' variable_declarator che introduce il campo (includendo una variable_initializer nella dichiarazione).In the variable_declarator that introduces the field (by including a variable_initializer in the declaration).
  • Per un campo di istanza, nei costruttori di istanza della classe che contiene la dichiarazione di campo; per un campo statico, nel costruttore statico della classe che contiene la dichiarazione di 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. Questi sono anche gli unici contesti in cui è possibile passare un readonly campo come out ref parametro o.These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

Il tentativo di assegnare a un readonly campo o passarlo come out parametro o ref in qualsiasi altro contesto è un errore in fase di compilazione.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.

Utilizzo di campi di sola lettura statici per le costantiUsing static readonly fields for constants

Un static readonly campo è utile quando si desidera un nome simbolico per un valore costante, ma quando il tipo del valore non è consentito in una const dichiarazione o quando il valore non può essere calcolato in fase di compilazione.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. Nell'esempioIn 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;
    }
}

i Black White membri,, Red , Green e Blue non possono essere dichiarati come const membri perché i relativi valori non possono essere calcolati in fase di compilazione.the Black, White, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. Tuttavia, dichiarando static readonly invece ha lo stesso effetto.However, declaring them static readonly instead has much the same effect.

Controllo delle versioni di costanti e campi di sola lettura staticiVersioning of constants and static readonly fields

Le costanti e i campi di sola lettura hanno una semantica di controllo delle versioni binaria diversa.Constants and readonly fields have different binary versioning semantics. Quando un'espressione fa riferimento a una costante, il valore della costante viene ottenuto in fase di compilazione, ma quando un'espressione fa riferimento a un campo di sola lettura, il valore del campo non viene ottenuto fino alla fase di esecuzione.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. Si consideri un'applicazione costituita da due programmi distinti: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);
        }
    }
}

Gli Program1 Program2 spazi dei nomi e indicano due programmi compilati separatamente.The Program1 and Program2 namespaces denote two programs that are compiled separately. Poiché Program1.Utils.X viene dichiarato come un campo statico di sola lettura, il valore restituito dall' Console.WriteLine istruzione non è noto in fase di compilazione, ma viene invece ottenuto in fase di esecuzione.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. Pertanto, se il valore di X viene modificato e Program1 viene ricompilato, l' Console.WriteLine istruzione restituirà il nuovo valore anche se Program2 non viene ricompilato.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. Tuttavia, era X una costante, il valore di X sarebbe stato ottenuto al momento della Program2 compilazione e rimarrebbe inalterato dalle modifiche apportate in Program1 fino a quando non Program2 viene ricompilato.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.

Campi volatiliVolatile fields

Quando un field_declaration include un volatile modificatore, i campi introdotti dalla dichiarazione sono campi volatili.When a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields.

Per i campi non volatili, le tecniche di ottimizzazione che riordinano le istruzioni possono provocare risultati imprevisti e imprevedibili nei programmi multithread che accedono a campi senza sincronizzazione, ad esempio quelli forniti dall' lock_statement (istruzione 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). Queste ottimizzazioni possono essere eseguite dal compilatore, dal sistema della fase di esecuzione o dall'hardware.These optimizations can be performed by the compiler, by the run-time system, or by hardware. Per i campi volatili, tali ottimizzazioni di riordino sono limitate:For volatile fields, such reordering optimizations are restricted:

  • Una lettura di un campo volatile è detta lettura volatile.A read of a volatile field is called a volatile read. Una lettura volatile ha una "semantica di acquisizione"; ovvero, è garantita prima di tutti i riferimenti alla memoria che si verificano dopo la sequenza di istruzioni.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.
  • Una scrittura di un campo volatile è detta scrittura volatile.A write of a volatile field is called a volatile write. Una scrittura volatile presenta una "semantica di rilascio"; ovvero, è garantito che avvenga dopo i riferimenti alla memoria prima dell'istruzione di scrittura nella sequenza di istruzioni.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.

Queste limitazioni garantiscono che tutti i thread considereranno scritture di tipo volatile eseguite da altri thread nell'ordine in cui sono stati eseguiti.These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. Non è necessaria un'implementazione conforme per fornire un singolo ordinamento totale delle scritture volatili, come visto da tutti i thread di esecuzione.A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. Il tipo di un campo volatile deve essere uno dei seguenti:The type of a volatile field must be one of the following:

  • Reference_type.A reference_type.
  • Tipo,,,,,,,,, byte sbyte short ushort int uint char float bool System.IntPtr o System.UIntPtr .The type byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr, or System.UIntPtr.
  • Un enum_type avere un tipo di base enum di byte , sbyte , short , ushort , int o uint .An enum_type having an enum base type of byte, sbyte, short, ushort, int, or uint.

L'esempio:The 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;
            }
        }
    }
}

produce l'output:produces the output:

result = 143

In questo esempio, il metodo Main avvia un nuovo thread che esegue il metodo Thread2 .In this example, the method Main starts a new thread that runs the method Thread2. Questo metodo archivia un valore in un campo non volatile denominato result , quindi Archivia true nel campo volatile finished .This method stores a value into a non-volatile field called result, then stores true in the volatile field finished. Il thread principale attende che il campo finished sia impostato su true , quindi legge il campo result .The main thread waits for the field finished to be set to true, then reads the field result. Poiché finished è stato dichiarato volatile , il thread principale deve leggere il valore 143 dal campo result .Since finished has been declared volatile, the main thread must read the value 143 from the field result. Se il campo finished non è stato dichiarato volatile , è possibile che l'archivio result sia visibile al thread principale dopo l'archivio finished e quindi affinché il thread principale legga il valore 0 dal 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. La Dichiarazione finished come volatile campo impedisce tale incoerenza.Declaring finished as a volatile field prevents any such inconsistency.

Inizializzazione del campoField initialization

Il valore iniziale di un campo, sia che si tratti di un campo statico o di un campo di istanza, è il valore predefinito (valori predefiniti) del tipo del campo.The initial value of a field, whether it be a static field or an instance field, is the default value (Default values) of the field's type. Non è possibile osservare il valore di un campo prima che questa inizializzazione predefinita venga eseguita e un campo non viene mai "inizializzato".It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never "uninitialized". L'esempio:The 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);
    }
}

produce l'outputproduces the output

b = False, i = 0

Poiché b e i vengono inizializzati automaticamente sui valori predefiniti.because b and i are both automatically initialized to default values.

Inizializzatori di variabileVariable initializers

Le dichiarazioni di campo possono includere variable_initializer s.Field declarations may include variable_initializer s. Per i campi statici, gli inizializzatori di variabile corrispondono a istruzioni di assegnazione eseguite durante l'inizializzazione della classe.For static fields, variable initializers correspond to assignment statements that are executed during class initialization. Per i campi di istanza, gli inizializzatori di variabile corrispondono a istruzioni di assegnazione eseguite quando viene creata un'istanza della classe.For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

L'esempio:The 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);
    }
}

produce l'outputproduces the output

x = 1.4142135623731, i = 100, s = Hello

Poiché un'assegnazione x si verifica quando gli inizializzatori di campo statici eseguono e assegnazioni a i e s si verificano quando vengono eseguiti gli inizializzatori di campo dell'istanza.because an assignment to x occurs when static field initializers execute and assignments to i and s occur when the instance field initializers execute.

L'inizializzazione del valore predefinito descritta nell' inizializzazione del campo si verifica per tutti i campi, inclusi i campi con inizializzatori di variabile.The default value initialization described in Field initialization occurs for all fields, including fields that have variable initializers. Pertanto, quando una classe viene inizializzata, tutti i campi statici della classe vengono prima inizializzati sui valori predefiniti e quindi gli inizializzatori di campo statici vengono eseguiti in ordine testuale.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. Allo stesso modo, quando viene creata un'istanza di una classe, tutti i campi di istanza in tale istanza vengono innanzitutto inizializzati sui rispettivi valori predefiniti, quindi gli inizializzatori di campo dell'istanza vengono eseguiti in ordine testuale.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.

È possibile che i campi statici con inizializzatori di variabile siano osservati nello stato del valore predefinito.It is possible for static fields with variable initializers to be observed in their default value state. Tuttavia, questo è fortemente sconsigliato come una cosa di stile.However, this is strongly discouraged as a matter of style. L'esempio:The 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);
    }
}

presenta questo comportamento.exhibits this behavior. Nonostante le definizioni circolari di a e b, il programma è valido.Despite the circular definitions of a and b, the program is valid. Produce l'outputIt results in the output

a = 1, b = 2

Poiché i campi statici a e b vengono inizializzati su 0 (il valore predefinito per int ) prima dell'esecuzione degli inizializzatori.because the static fields a and b are initialized to 0 (the default value for int) before their initializers are executed. Quando l'inizializzatore per a viene eseguito, il valore di b è zero, quindi a viene inizializzato su 1 .When the initializer for a runs, the value of b is zero, and so a is initialized to 1. Quando l'inizializzatore per b viene eseguito, il valore di a è già 1 , quindi b viene inizializzato su 2 .When the initializer for b runs, the value of a is already 1, and so b is initialized to 2.

Inizializzazione campo staticoStatic field initialization

Gli inizializzatori di variabile di campo statici di una classe corrispondono a una sequenza di assegnazioni che vengono eseguite nell'ordine testuale in cui vengono visualizzate nella dichiarazione di 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 nella classe è presente un costruttore statico (costruttori statici), l'esecuzione degli inizializzatori di campo statici viene eseguita immediatamente prima dell'esecuzione del costruttore statico.If a static constructor (Static constructors) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. In caso contrario, gli inizializzatori di campo statici vengono eseguiti in un tempo dipendente dall'implementazione prima del primo utilizzo di un campo statico di tale 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. L'esempio: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
{
    public static int X = Test.F("Init A");
}

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

potrebbe produrre l'output:might produce either the output:

Init A
Init B
1 1

o l'output:or the output:

Init B
Init A
1 1

dato che l'esecuzione dell' X inizializzatore e dell' Y inizializzatore dell'oggetto può essere eseguito in entrambi i casi, è necessario che si verifichino solo prima dei riferimenti a tali campi.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. Tuttavia, nell'esempio: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");
}

l'output deve essere:the output must be:

Init B
Init A
1 1

Poiché le regole per l'esecuzione dei costruttori statici (come definito nei costruttori statici) forniscono il B costruttore statico (e B di conseguenza gli inizializzatori di campo statici) che devono essere eseguiti prima degli A inizializzatori di campo e del costruttore statico di.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.

Inizializzazione campo istanzaInstance field initialization

Gli inizializzatori di variabile di campo dell'istanza di una classe corrispondono a una sequenza di assegnazioni che vengono eseguite immediatamente dopo l'immissione in uno dei costruttori di istanza (inizializzatori di costruttori) di tale 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. Gli inizializzatori di variabile vengono eseguiti nell'ordine testuale in cui vengono visualizzati nella dichiarazione della classe.The variable initializers are executed in the textual order in which they appear in the class declaration. La creazione e il processo di inizializzazione dell'istanza della classe vengono descritti ulteriormente nei costruttori di istanza.The class instance creation and initialization process is described further in Instance constructors.

Un inizializzatore di variabile per un campo di istanza non può fare riferimento all'istanza in fase di creazione.A variable initializer for an instance field cannot reference the instance being created. Pertanto, si tratta di un errore in fase di compilazione a cui fare riferimento this in un inizializzatore di variabile, poiché si tratta di un errore in fase di compilazione per un inizializzatore di variabile che fa riferimento a qualsiasi membro di istanza tramite un simple_name.Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple_name. Nell'esempioIn the example

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

l'inizializzatore di variabile per y restituisce un errore in fase di compilazione perché fa riferimento a un membro dell'istanza creata.the variable initializer for y results in a compile-time error because it references a member of the instance being created.

MetodiMethods

Un *Method _ è un membro che implementa un calcolo o un'azione che può essere eseguita da un oggetto o da una classe.A *method _ is a member that implements a computation or action that can be performed by an object or class. I metodi vengono dichiarati usando _method_declaration * s:Methods are declared using _method_declaration*s:

method_declaration
    : method_header method_body
    ;

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

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

return_type
    : type
    | 'void'
    ;

member_name
    : identifier
    | interface_type '.' identifier
    ;

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

Un method_declaration può includere un set di attributi (attributi) e una combinazione valida dei quattro modificatori di accesso (modificatori di accesso), new (il nuovo modificatore), static (metodi statici e di istanza), virtual (metodi virtuali), (metodi di override override), sealed (Metodi sealed), abstract (metodi astratti) e extern modificatori (metodi esterni).A method_declaration may include a set of attributes (Attributes) and a valid combination of the four access modifiers (Access modifiers), the new (The new modifier), static (Static and instance methods), virtual (Virtual methods), override (Override methods), sealed (Sealed methods), abstract (Abstract methods), and extern (External methods) modifiers.

Una dichiarazione ha una combinazione valida di modificatori se si verificano tutte le condizioni seguenti:A declaration has a valid combination of modifiers if all of the following are true:

  • La dichiarazione include una combinazione valida di modificatori di accesso (modificatori di accesso).The declaration includes a valid combination of access modifiers (Access modifiers).
  • La dichiarazione non include lo stesso modificatore più volte.The declaration does not include the same modifier multiple times.
  • La dichiarazione include al massimo uno dei modificatori seguenti: static , virtual e override .The declaration includes at most one of the following modifiers: static, virtual, and override.
  • La dichiarazione include al massimo uno dei modificatori seguenti: new e override .The declaration includes at most one of the following modifiers: new and override.
  • Se la dichiarazione include il abstract modificatore, la dichiarazione non include i modificatori seguenti: static , virtual sealed o extern .If the declaration includes the abstract modifier, then the declaration does not include any of the following modifiers: static, virtual, sealed or extern.
  • Se la dichiarazione include il private modificatore, la dichiarazione non include i modificatori seguenti: virtual , override o abstract .If the declaration includes the private modifier, then the declaration does not include any of the following modifiers: virtual, override, or abstract.
  • Se la dichiarazione include il sealed modificatore, la dichiarazione include anche il override modificatore.If the declaration includes the sealed modifier, then the declaration also includes the override modifier.
  • Se la dichiarazione include il partial modificatore, non include i modificatori seguenti: new ,, public protected , internal , private , virtual , sealed , override , abstract o extern .If the declaration includes the partial modifier, then it does not include any of the following modifiers: new, public, protected, internal, private, virtual, sealed, override, abstract, or extern.

Un metodo con il async modificatore è una funzione asincrona e segue le regole descritte in funzioni asincrone.A method that has the async modifier is an async function and follows the rules described in Async functions.

Il return_type di una dichiarazione di metodo specifica il tipo di valore calcolato e restituito dal metodo.The return_type of a method declaration specifies the type of the value computed and returned by the method. Il return_type è void se il metodo non restituisce un valore.The return_type is void if the method does not return a value. Se la dichiarazione include il partial modificatore, il tipo restituito deve essere void .If the declaration includes the partial modifier, then the return type must be void.

Il MEMBER_NAME specifica il nome del metodo.The member_name specifies the name of the method. A meno che il metodo non sia un'implementazione esplicita di un membro di interfaccia (implementazioni esplicite di membri di interfaccia), il MEMBER_NAME è semplicemente un identificatore.Unless the method is an explicit interface member implementation (Explicit interface member implementations), the member_name is simply an identifier. Per un'implementazione esplicita di un membro di interfaccia, il MEMBER_NAME è costituito da un INTERFACE_TYPE seguito da un oggetto " . " e da un identificatore.For an explicit interface member implementation, the member_name consists of an interface_type followed by a "." and an identifier.

Il type_parameter_list facoltativo specifica i parametri di tipo del metodo (parametri di tipo).The optional type_parameter_list specifies the type parameters of the method (Type parameters). Se viene specificato un type_parameter_list , il metodo è un *metodo generico _.If a type_parameter_list is specified the method is a *generic method _. Se il metodo ha un extern modificatore, non è possibile specificare un _type_parameter_list *.If the method has an extern modifier, a _type_parameter_list* cannot be specified.

Il formal_parameter_list facoltativo specifica i parametri del metodo (parametri del metodo).The optional formal_parameter_list specifies the parameters of the method (Method parameters).

Il type_parameter_constraints_clause facoltativo s specifica i vincoli sui singoli parametri di tipo (vincoli di parametro di tipo) e può essere specificato solo se viene fornito anche un type_parameter_list e il metodo non ha un override modificatore.The optional type_parameter_constraints_clause s 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.

Il return_type e ciascuno dei tipi a cui si fa riferimento nella formal_parameter_list di un metodo deve essere accessibile almeno quanto il metodo stesso (vincoli di accessibilità).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).

Il method_body è un punto e virgola, un'istruzione * Body _ o un corpo dell' espressione.The method_body is either a semicolon, a statement body _ or an _expression body*_. Il corpo di un'istruzione è costituito da un _block *, che specifica le istruzioni da eseguire quando viene richiamato il metodo.A statement body consists of a _block*, which specifies the statements to execute when the method is invoked. Il corpo di un'espressione è costituito => da seguito da un' espressione e da un punto e virgola e indica una singola espressione da eseguire quando viene richiamato il metodo.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the method is invoked.

Per abstract extern i metodi e, il method_body è costituito semplicemente da un punto e virgola.For abstract and extern methods, the method_body consists simply of a semicolon. Per partial i metodi il method_body può essere costituito da un punto e virgola, da un corpo del blocco o da un corpo dell'espressione.For partial methods the method_body may consist of either a semicolon, a block body or an expression body. Per tutti gli altri metodi, il method_body è un corpo del blocco o un corpo dell'espressione.For all other methods, the method_body is either a block body or an expression body.

Se il method_body è costituito da un punto e virgola, la dichiarazione non può includere il async modificatore.If the method_body consists of a semicolon, then the declaration may not include the async modifier.

Il nome, l'elenco dei parametri di tipo e l'elenco di parametri formali di un metodo definiscono la firma (firme e overload) del metodo.The name, the type parameter list and the formal parameter list of a method define the signature (Signatures and overloading) of the method. In particolare, la firma di un metodo è costituita dal nome, dal numero di parametri di tipo e dal numero, dai modificatori e dai tipi dei parametri formali.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. Per questi scopi, qualsiasi parametro di tipo del metodo che si verifica nel tipo di un parametro formale viene identificato senza il relativo nome, ma in base alla posizione ordinale nell'elenco di argomenti di tipo del metodo. Il tipo restituito non fa parte della firma di un metodo, né i nomi dei parametri di tipo o i parametri formali.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.

Il nome di un metodo deve essere diverso da quello dei nomi di tutti gli altri metodi non dichiarati nella stessa classe.The name of a method must differ from the names of all other non-methods declared in the same class. Inoltre, la firma di un metodo deve essere diversa dalle firme di tutti gli altri metodi dichiarati nella stessa classe e due metodi dichiarati nella stessa classe potrebbero non avere firme che differiscono solo per ref e out .In addition, the signature of a method must differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out.

Il type_parameter s del metodo si trovano nell'ambito della method_declaration e possono essere usati per formare i tipi in tutto l'ambito in return_type, method_body e type_parameter_constraints_clause ma non negli attributi.The method's type_parameter s 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_clause s but not in attributes.

Tutti i parametri formali e i parametri di tipo devono avere nomi diversi.All formal parameters and type parameters must have different names.

Parametri del metodoMethod parameters

I parametri di un metodo, se presenti, sono dichiarati dal formal_parameter_list del metodo.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
    ;

L'elenco di parametri formali è costituito da uno o più parametri delimitati da virgole di cui solo l'ultimo può essere un parameter_array.The formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array.

Un fixed_parameter è costituito da un set facoltativo di attributi (attributi), ref un out this modificatore facoltativo o, un tipo, un identificatore e una default_argument facoltativa.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. Ogni fixed_parameter dichiara un parametro del tipo specificato con il nome specificato.Each fixed_parameter declares a parameter of the given type with the given name. Il this modificatore designa il metodo come metodo di estensione ed è consentito solo sul primo parametro di un metodo statico.The this modifier designates the method as an extension method and is only allowed on the first parameter of a static method. I metodi di estensione sono descritti ulteriormente nei metodi di estensione.Extension methods are further described in Extension methods.

Un fixed_parameter con una default_argument è noto come *parametro facoltativo , mentre un fixed_parameter * senza default_argument è un parametro * required.A fixed_parameter with a default_argument is known as an optional parameter _, whereas a _fixed_parameter without a default_argument is a *required parameter. Un parametro obbligatorio non può essere visualizzato dopo un parametro facoltativo in un _formal_parameter_list *.A required parameter may not appear after an optional parameter in a _formal_parameter_list*.

Un ref out parametro o non può avere un default_argument.A ref or out parameter cannot have a default_argument. L' espressione in un default_argument deve essere una delle seguenti:The expression in a default_argument must be one of the following:

  • a constant_expressiona constant_expression
  • espressione nel formato in new S() cui S è un tipo di valorean expression of the form new S() where S is a value type
  • espressione nel formato in default(S) cui S è un tipo di valorean expression of the form default(S) where S is a value type

L' espressione deve essere convertibile in modo implicito da un'identità o Conversione Nullable nel tipo di parametro.The expression must be implicitly convertible by an identity or nullable conversion to the type of the parameter.

Se si verificano parametri facoltativi in una dichiarazione di metodo parziale di implementazione (metodi parziali), un'implementazione esplicita di un membro di interfaccia (implementazioni esplicite di membri di interfaccia) o in una dichiarazione dell'indicizzatore a parametro singolo (indicizzatori) il compilatore deve restituire un avviso, poiché questi membri non possono mai essere richiamati in modo da consentire l'omissione degli argomentiIf optional parameters occur in an implementing partial method declaration (Partial methods) , an explicit interface member implementation (Explicit interface member implementations) or in a single-parameter indexer declaration (Indexers) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted.

Un parameter_array è costituito da un set facoltativo di attributi (attributi), da un params modificatore, da un array_type e da un identificatore.A parameter_array consists of an optional set of attributes (Attributes), a params modifier, an array_type, and an identifier. Una matrice di parametri dichiara un solo parametro del tipo di matrice specificato con il nome specificato.A parameter array declares a single parameter of the given array type with the given name. Il array_type di una matrice di parametri deve essere un tipo di matrice unidimensionale (tipi di matrice).The array_type of a parameter array must be a single-dimensional array type (Array types). In una chiamata al metodo, una matrice di parametri consente di specificare un solo argomento del tipo di matrice specificato oppure di specificare zero o più argomenti del tipo di elemento della matrice.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. Le matrici di parametri sono descritte ulteriormente in matrici di parametri.Parameter arrays are described further in Parameter arrays.

Una parameter_array può verificarsi dopo un parametro facoltativo, ma non può avere un valore predefinito. l'omissione degli argomenti per un parameter_array comporterebbe invece la creazione di una matrice vuota.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.

Nell'esempio seguente vengono illustrati diversi tipi di parametri: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
) { }

Nella formal_parameter_list per M , i è un parametro ref obbligatorio, d è un parametro di valore obbligatorio,, b s o e t sono parametri di valore facoltativi ed a è una matrice di parametri.In the formal_parameter_list for M, i is a required ref parameter, d is a required value parameter, b, s, o and t are optional value parameters and a is a parameter array.

Una dichiarazione di metodo crea uno spazio di dichiarazione separato per parametri, parametri di tipo e variabili locali.A method declaration creates a separate declaration space for parameters, type parameters and local variables. I nomi vengono introdotti in questo spazio di dichiarazione dall'elenco di parametri di tipo e dall'elenco di parametri formali del metodo e dalle dichiarazioni di variabili locali nel blocco del metodo.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. Per due membri di uno spazio di dichiarazione di metodo è necessario avere lo stesso nome.It is an error for two members of a method declaration space to have the same name. È un errore per lo spazio di dichiarazione del metodo e lo spazio di dichiarazione della variabile locale di uno spazio di dichiarazione annidato per contenere elementi con lo stesso 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.

Una chiamata al metodo (chiamate al metodo) crea una copia, specifica della chiamata, dei parametri formali e delle variabili locali del metodo e l'elenco di argomenti della chiamata assegna valori o riferimenti a variabili ai parametri formali appena creati.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. All'interno del blocco di un metodo è possibile fare riferimento ai parametri formali in base ai relativi identificatori nelle espressioni Simple_name (nomi semplici).Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (Simple names).

Sono disponibili quattro tipi di parametri formali:There are four kinds of formal parameters:

  • Parametri del valore, che vengono dichiarati senza modificatori.Value parameters, which are declared without any modifiers.
  • Parametri di riferimento, dichiarati con il ref modificatore.Reference parameters, which are declared with the ref modifier.
  • Parametri di output, dichiarati con il out modificatore.Output parameters, which are declared with the out modifier.
  • Matrici di parametri, dichiarate con il params modificatore.Parameter arrays, which are declared with the params modifier.

Come descritto in firme e overload, i ref out modificatori e fanno parte della firma di un metodo, ma il params modificatore non lo è.As described in Signatures and overloading, the ref and out modifiers are part of a method's signature, but the params modifier is not.

Parametri valoreValue parameters

Un parametro dichiarato senza modificatori è un parametro di valore.A parameter declared with no modifiers is a value parameter. Un parametro di valore corrisponde a una variabile locale che ottiene il valore iniziale dall'argomento corrispondente fornito nella chiamata al metodo.A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

Quando un parametro formale è un parametro di valore, l'argomento corrispondente in una chiamata al metodo deve essere un'espressione convertibile in modo implicito (conversioni implicite) nel tipo di parametro formale.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.

Un metodo è autorizzato a assegnare nuovi valori a un parametro di valore.A method is permitted to assign new values to a value parameter. Tali assegnazioni influiscono solo sul percorso di archiviazione locale rappresentato dal parametro value, ma non hanno alcun effetto sull'argomento effettivo specificato nella chiamata al metodo.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.

Parametri per riferimentoReference parameters

Un parametro dichiarato con un ref modificatore è un parametro di riferimento.A parameter declared with a ref modifier is a reference parameter. A differenza di un parametro di valore, un parametro di riferimento non crea un nuovo percorso di archiviazione.Unlike a value parameter, a reference parameter does not create a new storage location. Un parametro Reference rappresenta invece lo stesso percorso di archiviazione della variabile specificata come argomento nella chiamata al metodo.Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

Quando un parametro formale è un parametro di riferimento, l'argomento corrispondente in una chiamata al metodo deve essere costituito dalla parola chiave ref seguita da un variable_reference (regole precise per determinare l'assegnazione definita) dello stesso tipo del parametro formale.When a formal parameter is a reference parameter, the corresponding argument in a method invocation must consist of the keyword ref followed by a variable_reference (Precise rules for determining definite assignment) of the same type as the formal parameter. Una variabile deve essere assegnata definitivamente prima di poter essere passata come parametro di riferimento.A variable must be definitely assigned before it can be passed as a reference parameter.

All'interno di un metodo, un parametro di riferimento viene sempre considerato definitivamente assegnato.Within a method, a reference parameter is always considered definitely assigned.

Un metodo dichiarato come iteratore (iteratori) non può avere parametri di riferimento.A method declared as an iterator (Iterators) cannot have reference parameters.

L'esempio:The 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);
    }
}

produce l'outputproduces the output

i = 2, j = 1

Per la chiamata di Swap in Main , x rappresenta i e y rappresenta j .For the invocation of Swap in Main, x represents i and y represents j. Quindi, la chiamata ha l'effetto di scambiare i valori di i e j .Thus, the invocation has the effect of swapping the values of i and j.

In un metodo che accetta parametri di riferimento è possibile che più nomi rappresentino lo stesso percorso di archiviazione.In a method that takes reference parameters it is possible for multiple names to represent the same storage location. Nell'esempioIn the example

class A
{
    string s;

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

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

la chiamata di F in G passa un riferimento a s per a e b .the invocation of F in G passes a reference to s for both a and b. Pertanto, per tale chiamata, i nomi s , a e b fanno riferimento allo stesso percorso di archiviazione e le tre assegnazioni modificano il campo dell'istanza s .Thus, for that invocation, the names s, a, and b all refer to the same storage location, and the three assignments all modify the instance field s.

Parametri di outputOutput parameters

Un parametro dichiarato con un out modificatore è un parametro di output.A parameter declared with an out modifier is an output parameter. Analogamente a un parametro di riferimento, un parametro di output non crea un nuovo percorso di archiviazione.Similar to a reference parameter, an output parameter does not create a new storage location. Un parametro di output rappresenta invece lo stesso percorso di archiviazione della variabile specificata come argomento nella chiamata al metodo.Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

Quando un parametro formale è un parametro di output, l'argomento corrispondente in una chiamata al metodo deve essere costituito dalla parola chiave out seguita da un variable_reference (regole precise per determinare l'assegnazione definita) dello stesso tipo del parametro formale.When a formal parameter is an output parameter, the corresponding argument in a method invocation must consist of the keyword out followed by a variable_reference (Precise rules for determining definite assignment) of the same type as the formal parameter. Una variabile non deve essere definitivamente assegnata prima di poter essere passata come parametro di output, ma dopo una chiamata in cui una variabile è stata passata come parametro di output, la variabile viene considerata assegnata definitivamente.A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned.

All'interno di un metodo, come una variabile locale, un parametro di output viene inizialmente considerato non assegnato e deve essere assegnato definitivamente prima di utilizzare il relativo valore.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.

Ogni parametro di output di un metodo deve essere assegnato definitivamente prima che il metodo venga restituito.Every output parameter of a method must be definitely assigned before the method returns.

Un metodo dichiarato come metodo parziale (metodi parziali) o un iteratore (iteratori) non può avere parametri di output.A method declared as a partial method (Partial methods) or an iterator (Iterators) cannot have output parameters.

I parametri di output vengono in genere usati nei metodi che producono più valori restituiti.Output parameters are typically used in methods that produce multiple return values. Ad esempio: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);
    }
}

L'esempio produce l'output:The example produces the output:

c:\Windows\System\
hello.txt

Si noti che dir le name variabili e possono essere non assegnate prima che vengano passate a SplitPath e che siano considerate definitivamente assegnate dopo la chiamata.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.

Matrici di parametriParameter arrays

Un parametro dichiarato con un params modificatore è una matrice di parametri.A parameter declared with a params modifier is a parameter array. Se un elenco di parametri formali include una matrice di parametri, deve essere l'ultimo parametro nell'elenco e deve essere di un tipo di matrice unidimensionale.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. Ad esempio, i tipi string[] e string[][] possono essere usati come tipo di una matrice di parametri, ma il tipo string[,] non può essere.For example, the types string[] and string[][] can be used as the type of a parameter array, but the type string[,] can not. Non è possibile combinare il params modificatore con i modificatori ref e out .It is not possible to combine the params modifier with the modifiers ref and out.

Una matrice di parametri consente di specificare gli argomenti in uno dei due modi seguenti in una chiamata al metodo:A parameter array permits arguments to be specified in one of two ways in a method invocation:

  • L'argomento specificato per una matrice di parametri può essere una singola espressione convertibile in modo implicito (conversioni implicite) nel tipo di matrice di parametri.The argument given for a parameter array can be a single expression that is implicitly convertible (Implicit conversions) to the parameter array type. In questo caso, la matrice di parametri funge esattamente da parametro di valore.In this case, the parameter array acts precisely like a value parameter.
  • In alternativa, la chiamata può specificare zero o più argomenti per la matrice di parametri, dove ogni argomento è un'espressione che è implicitamente convertibile (conversioni implicite) nel tipo di elemento della matrice di parametri.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. In questo caso, la chiamata crea un'istanza del tipo di matrice di parametri con una lunghezza corrispondente al numero di argomenti, inizializza gli elementi dell'istanza di matrice con i valori di argomento specificati e usa l'istanza di matrice appena creata come argomento effettivo.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.

Eccetto per consentire un numero variabile di argomenti in una chiamata, una matrice di parametri è esattamente equivalente a un parametro valore (parametri valore) dello stesso 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.

L'esempio:The 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();
    }
}

produce l'outputproduces the output

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

La prima chiamata di F passa semplicemente la matrice a come parametro di valore.The first invocation of F simply passes the array a as a value parameter. La seconda chiamata di F crea automaticamente un elemento a quattro elementi int[] con i valori dell'elemento specificati e passa l'istanza di matrice come parametro di valore.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. Analogamente, la terza chiamata di F Crea un elemento zero int[] e passa l'istanza come parametro valore.Likewise, the third invocation of F creates a zero-element int[] and passes that instance as a value parameter. La seconda e la terza chiamata sono esattamente equivalenti alla scrittura:The second and third invocations are precisely equivalent to writing:

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

Quando si esegue la risoluzione dell'overload, un metodo con una matrice di parametri può essere applicabile nel formato normale o nella forma espansa (membro della funzione applicabile).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). Il form espanso di un metodo è disponibile solo se il formato normale del metodo non è applicabile e solo se un metodo applicabile con la stessa firma del form espanso non è già dichiarato nello stesso 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.

L'esempio:The 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);
    }
}

produce l'outputproduces the output

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

Nell'esempio, due delle possibili forme espanse del metodo con una matrice di parametri sono già incluse nella classe come metodi normali.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. Tali forme espanse pertanto non vengono considerate quando si esegue la risoluzione dell'overload e la prima e la terza chiamata al metodo selezionano quindi i metodi normali.These expanded forms are therefore not considered when performing overload resolution, and the first and third method invocations thus select the regular methods. Quando una classe dichiara un metodo con una matrice di parametri, non è insolito includere anche alcuni dei form espansi come metodi normali.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. In questo modo è possibile evitare l'allocazione di un'istanza di matrice che si verifica quando viene richiamata una forma espansa di un metodo con una matrice di parametri.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 il tipo di una matrice di parametri è object[] , una potenziale ambiguità si verifica tra il formato normale del metodo e il formato speso per un singolo object parametro.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. Il motivo dell'ambiguità è che un oggetto object[] è implicitamente convertibile nel tipo object .The reason for the ambiguity is that an object[] is itself implicitly convertible to type object. Tuttavia, l'ambiguità non presenta alcun problema poiché può essere risolta inserendo un cast se necessario.The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.

L'esempio:The 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);
    }
}

produce l'outputproduces the output

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

Nella prima e nell'ultima chiamata di F , il formato normale di F è applicabile perché esiste una conversione implicita dal tipo di argomento al tipo di parametro (entrambi sono di 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[]). Pertanto, la risoluzione dell'overload seleziona il formato normale di F e l'argomento viene passato come parametro di valore normale.Thus, overload resolution selects the normal form of F, and the argument is passed as a regular value parameter. Nella seconda e nella terza chiamata, il formato normale di F non è applicabile perché non esiste alcuna conversione implicita dal tipo di argomento al tipo di parametro (il tipo object non può essere convertito in modo implicito nel tipo object[] ).In the second and third invocations, the normal form of F is not applicable because no implicit conversion exists from the argument type to the parameter type (type object cannot be implicitly converted to type object[]). Tuttavia, il form espanso di F è applicabile, quindi è selezionato dalla risoluzione dell'overload.However, the expanded form of F is applicable, so it is selected by overload resolution. Di conseguenza, viene creato un solo elemento object[] dalla chiamata e il singolo elemento della matrice viene inizializzato con il valore dell'argomento specificato (che a sua volta è un riferimento a un oggetto 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[]).

Metodi statici e di istanzaStatic and instance methods

Quando una dichiarazione di metodo include un static modificatore, tale metodo viene definito come metodo statico.When a method declaration includes a static modifier, that method is said to be a static method. Quando non static è presente alcun modificatore, viene definito un metodo di istanza.When no static modifier is present, the method is said to be an instance method.

Un metodo statico non agisce su un'istanza specifica e si tratta di un errore in fase di compilazione a cui fare riferimento this in un metodo statico.A static method does not operate on a specific instance, and it is a compile-time error to refer to this in a static method.

Un metodo di istanza opera su una determinata istanza di una classe e a tale istanza è possibile accedere come this (questo accesso).An instance method operates on a given instance of a class, and that instance can be accessed as this (This access).

Quando si fa riferimento a un metodo in un member_access (accesso ai membri) del form E.M , se M è un metodo statico, E deve indicare un tipo che contiene M e se M è un metodo di istanza, E deve indicare un'istanza di un tipo contenente 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.

Le differenze tra i membri statici e i membri di istanza sono illustrate ulteriormente nei membri statici e di istanza.The differences between static and instance members are discussed further in Static and instance members.

Metodi virtualiVirtual methods

Quando una dichiarazione di metodo di istanza include un virtual modificatore, tale metodo viene definito come metodo virtuale.When an instance method declaration includes a virtual modifier, that method is said to be a virtual method. Quando non virtual è presente alcun modificatore, il metodo viene definito come metodo non virtuale.When no virtual modifier is present, the method is said to be a non-virtual method.

L'implementazione di un metodo non virtuale è invariante: l'implementazione è la stessa se il metodo viene richiamato su un'istanza della classe in cui è dichiarata o un'istanza di una classe derivata.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. Al contrario, l'implementazione di un metodo virtuale può essere sostituita dalle classi derivate.In contrast, the implementation of a virtual method can be superseded by derived classes. Il processo di sostituzione dell'implementazione di un metodo virtuale ereditato è noto come override di tale metodo (metodi di override).The process of superseding the implementation of an inherited virtual method is known as overriding that method (Override methods).

In una chiamata a un metodo virtuale, il tipo di runtime* dell'istanza per cui viene eseguita la chiamata determina l'implementazione del metodo effettivo da richiamare.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. In una chiamata a un metodo non virtuale, il tipo _ in fase di compilazione* dell'istanza è il fattore determinante.In a non-virtual method invocation, the _ compile-time type* of the instance is the determining factor. In termini precisi, quando un metodo denominato N viene richiamato con un elenco di argomenti A in un'istanza con un tipo in fase di compilazione C e un tipo in fase di esecuzione R (dove R è C o una classe derivata da C ), la chiamata viene elaborata come indicato di seguito: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:

  • Per prima cosa, la risoluzione dell'overload viene applicata a C , N e A per selezionare un metodo specifico M dal set di metodi dichiarato in e ereditato da C .First, overload resolution is applied to C, N, and A, to select a specific method M from the set of methods declared in and inherited by C. Questa operazione è descritta in chiamate al metodo.This is described in Method invocations.
  • Quindi, se M è un metodo non virtuale, M viene richiamato.Then, if M is a non-virtual method, M is invoked.
  • In caso contrario, M è un metodo virtuale e M R viene richiamata l'implementazione più derivata di rispetto a.Otherwise, M is a virtual method, and the most derived implementation of M with respect to R is invoked.

Per ogni metodo virtuale dichiarato in o ereditato da una classe, esiste un' implementazione più derivata del metodo rispetto a tale 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. L'implementazione più derivata di un metodo virtuale M rispetto a una classe R viene determinata nel modo seguente:The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • Se R contiene la virtual dichiarazione di introduzione di M , si tratta dell'implementazione più derivata di M .If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • In caso contrario, se R contiene un valore override di M , si tratta dell'implementazione più derivata di M .Otherwise, if R contains an override of M, then this is the most derived implementation of M.
  • In caso contrario, l'implementazione più derivata di rispetto M a R corrisponde all'implementazione più derivata di rispetto M alla classe di base diretta di 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.

Nell'esempio seguente vengono illustrate le differenze tra i metodi virtuali e non virtuali: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();
    }
}

Nell'esempio, A introduce un metodo non virtuale F e un metodo virtuale G .In the example, A introduces a non-virtual method F and a virtual method G. La classe B introduce un nuovo metodo non virtuale F , nascondendo quindi l'oggetto ereditato F ed esegue l'override del metodo ereditato G .The class B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. L'esempio produce l'output:The example produces the output:

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

Si noti che l'istruzione a.G() richiama B.G , non A.G .Notice that the statement a.G() invokes B.G, not A.G. Questo è dovuto al fatto che il tipo in fase di esecuzione dell'istanza (ovvero B ), non il tipo in fase di compilazione dell'istanza (ovvero A ), determina l'implementazione del metodo effettivo da richiamare.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.

Poiché i metodi possono nascondere i metodi ereditati, è possibile che una classe contenga diversi metodi virtuali con la stessa firma.Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. Questo non presenta un problema di ambiguità, dal momento che tutto tranne il metodo più derivato è nascosto.This does not present an ambiguity problem, since all but the most derived method are hidden. Nell'esempioIn 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();
    }
}

le C D classi e contengono due metodi virtuali con la stessa firma: quello introdotto da A e quello introdotto da 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. Il metodo introdotto da C nasconde il metodo ereditato da A .The method introduced by C hides the method inherited from A. Pertanto, la dichiarazione di override in D esegue l'override del metodo introdotto da C e non è possibile D eseguire l'override del metodo introdotto da 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. L'esempio produce l'output:The example produces the output:

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

Si noti che è possibile richiamare il metodo virtuale nascosto accedendo a un'istanza di D tramite un tipo meno derivato in cui il metodo non è nascosto.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.

Metodi di overrideOverride methods

Quando una dichiarazione di metodo di istanza include un override modificatore, il metodo viene definito metodo di override.When an instance method declaration includes an override modifier, the method is said to be an override method. Un metodo di override esegue l'override di un metodo virtuale ereditato con la stessa firma.An override method overrides an inherited virtual method with the same signature. Mentre una dichiarazione di metodo virtuale introduce un nuovo metodo, una dichiarazione di metodo di override specializza un metodo virtuale ereditato esistente specificando una nuova implementazione del metodo.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.

Il metodo sottoposto a override da una override dichiarazione è noto come metodo di base sottoposto a override.The method overridden by an override declaration is known as the overridden base method. Per un metodo di override M dichiarato in una classe C , il metodo di base sottoposto a override viene determinato esaminando ogni tipo di classe di base di C , a partire dal tipo di classe di base diretta di C e continuando con ogni tipo di classe base diretta successiva, finché in un determinato tipo di classe base viene individuato almeno un metodo accessibile con la stessa firma di M dopo la sostituzione di argomenti di tipoFor 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. Al fine di individuare il metodo di base sottoposto a override, un metodo viene considerato accessibile se è, se è, se è public protected protected internal o se è internal e dichiarato nello stesso programma di C .For the purposes of locating the overridden base method, a method is considered accessible if it is public, if it is protected, if it is protected internal, or if it is internal and declared in the same program as C.

Si verifica un errore in fase di compilazione a meno che non siano soddisfatte tutte le condizioni seguenti per una dichiarazione di override:A compile-time error occurs unless all of the following are true for an override declaration:

  • È possibile trovare un metodo di base sottoposto a override come descritto in precedenza.An overridden base method can be located as described above.
  • Esiste esattamente un metodo di base sottoposto a override.There is exactly one such overridden base method. Questa restrizione ha effetto solo se il tipo di classe base è un tipo costruito in cui la sostituzione di argomenti di tipo rende la firma di due metodi uguali.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.
  • Il metodo di base sottoposto a override è un metodo virtuale, astratto o di override.The overridden base method is a virtual, abstract, or override method. In altre parole, il metodo di base sottoposto a override non può essere statico o non virtuale.In other words, the overridden base method cannot be static or non-virtual.
  • Il metodo di base sottoposto a override non è un metodo sealed.The overridden base method is not a sealed method.
  • Il metodo di override e il metodo di base sottoposto a override hanno lo stesso tipo restituito.The override method and the overridden base method have the same return type.
  • La dichiarazione di override e il metodo di base sottoposto a override hanno la stessa accessibilità dichiarata.The override declaration and the overridden base method have the same declared accessibility. In altre parole, una dichiarazione di override non può modificare l'accessibilità del metodo virtuale.In other words, an override declaration cannot change the accessibility of the virtual method. Tuttavia, se il metodo di base sottoposto a override è protetto come interno e viene dichiarato in un assembly diverso rispetto all'assembly che contiene il metodo di override, l'accessibilità dichiarata del metodo di override deve essere protetta.However, if the overridden base method is protected internal and it is declared in a different assembly than the assembly containing the override method then the override method's declared accessibility must be protected.
  • La dichiarazione di override non specifica il parametro di tipo-Constraints-clausole.The override declaration does not specify type-parameter-constraints-clauses. I vincoli vengono invece ereditati dal metodo di base sottoposto a override.Instead the constraints are inherited from the overridden base method. Si noti che i vincoli che sono parametri di tipo nel metodo sottoposto a override possono essere sostituiti da argomenti di tipo nel vincolo ereditato.Note that constraints that are type parameters in the overridden method may be replaced by type arguments in the inherited constraint. Questo può causare vincoli che non sono validi quando specificati in modo esplicito, ad esempio tipi di valore o tipi sealed.This can lead to constraints that are not legal when explicitly specified, such as value types or sealed types.

Nell'esempio seguente viene illustrato il funzionamento delle regole di override per le classi generiche: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>
}

Una dichiarazione di override può accedere al metodo di base sottoposto a override utilizzando un base_access (accesso di base).An override declaration can access the overridden base method using a base_access (Base access). Nell'esempioIn 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);
    }
}

la base.PrintFields() chiamata in B richiama il PrintFields metodo dichiarato in A .the base.PrintFields() invocation in B invokes the PrintFields method declared in A. Un base_access Disabilita il meccanismo di chiamata virtuale e considera semplicemente il metodo di base come metodo non virtuale.A base_access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. Se la chiamata è B stata scritta ((A)this).PrintFields() , richiamerebbe in modo ricorsivo il PrintFields metodo dichiarato in B , non quello dichiarato in A , poiché PrintFields è virtuale e il tipo in fase di esecuzione di ((A)this) è B .Had the invocation in B been written ((A)this).PrintFields(), it would recursively invoke the PrintFields method declared in B, not the one declared in A, since PrintFields is virtual and the run-time type of ((A)this) is B.

Solo se si include un override modificatore, un metodo esegue l'override di un altro metodo.Only by including an override modifier can a method override another method. In tutti gli altri casi, un metodo con la stessa firma di un metodo ereditato nasconde semplicemente il metodo ereditato.In all other cases, a method with the same signature as an inherited method simply hides the inherited method. Nell'esempioIn the example

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

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

il F metodo in B non include un override modificatore e pertanto non esegue l'override del F metodo in A .the F method in B does not include an override modifier and therefore does not override the F method in A. Il F metodo in nasconde invece B il metodo in A e viene segnalato un avviso perché la dichiarazione non include un new modificatore.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.

Nell'esempioIn 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
}

il F metodo in B nasconde il metodo virtuale F ereditato da A .the F method in B hides the virtual F method inherited from A. Poiché il nuovo F in B ha accesso privato, l'ambito include solo il corpo della classe B e non si estende a C .Since the new F in B has private access, its scope only includes the class body of B and does not extend to C. Pertanto, la dichiarazione di F in C è consentita per eseguire l'override dell'oggetto F ereditato da A .Therefore, the declaration of F in C is permitted to override the F inherited from A.

Metodi sealedSealed methods

Quando una dichiarazione di metodo di istanza include un sealed modificatore, tale metodo viene definito come metodo sealed.When an instance method declaration includes a sealed modifier, that method is said to be a sealed method. Se una dichiarazione di metodo di istanza include il sealed modificatore, deve includere anche il override modificatore.If an instance method declaration includes the sealed modifier, it must also include the override modifier. L'utilizzo del sealed modificatore impedisce a una classe derivata di eseguire l'override del metodo.Use of the sealed modifier prevents a derived class from further overriding the method.

Nell'esempioIn the example

using System;

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

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

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

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

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

la classe B fornisce due metodi di override: un F metodo con il sealed modificatore e un G metodo che non lo è.the class B provides two override methods: an F method that has the sealed modifier and a G method that does not. Bl'uso del sealed modifier impedisce di eseguire C ulteriori override F .B's use of the sealed modifier prevents C from further overriding F.

Metodi astrattiAbstract methods

Quando una dichiarazione di metodo di istanza include un abstract modificatore, tale metodo viene definito metodo astratto.When an instance method declaration includes an abstract modifier, that method is said to be an abstract method. Anche se un metodo astratto è implicitamente un metodo virtuale, non può avere il modificatore virtual .Although an abstract method is implicitly also a virtual method, it cannot have the modifier virtual.

Una dichiarazione di metodo astratto introduce un nuovo metodo virtuale ma non fornisce un'implementazione di tale metodo.An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Al contrario, le classi derivate non astratte sono necessarie per fornire la propria implementazione eseguendo l'override del metodo.Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Poiché un metodo astratto non fornisce alcuna implementazione effettiva, il method_body di un metodo astratto è costituito semplicemente da un punto e virgola.Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon.

Le dichiarazioni di metodo abstract sono consentite solo nelle classi astratte (classi astratte).Abstract method declarations are only permitted in abstract classes (Abstract classes).

Nell'esempioIn the example

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

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

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

la Shape classe definisce la nozione astratta di un oggetto Shape geometrico che può disegnare se stesso.the Shape class defines the abstract notion of a geometrical shape object that can paint itself. Il Paint metodo è astratto perché non esiste un'implementazione predefinita significativa.The Paint method is abstract because there is no meaningful default implementation. Le Ellipse Box classi e sono Shape implementazioni concrete.The Ellipse and Box classes are concrete Shape implementations. Poiché queste classi sono non astratte, è necessario eseguire l'override del Paint metodo e fornire un'implementazione effettiva.Because these classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

Si tratta di un errore in fase di compilazione per un base_access (accesso di base) per fare riferimento a un metodo astratto.It is a compile-time error for a base_access (Base access) to reference an abstract method. Nell'esempioIn the example

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

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

viene segnalato un errore in fase di compilazione per la base.F() chiamata, perché fa riferimento a un metodo astratto.a compile-time error is reported for the base.F() invocation because it references an abstract method.

Una dichiarazione di metodo astratto è consentita per eseguire l'override di un metodo virtuale.An abstract method declaration is permitted to override a virtual method. Ciò consente a una classe astratta di forzare la riimplementazione del metodo nelle classi derivate e rende non disponibile l'implementazione originale del metodo.This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable. Nell'esempioIn 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");
    }
}

Ala classe dichiara un metodo virtuale, la classe B esegue l'override di questo metodo con un metodo astratto e C la classe esegue l'override del metodo astratto per fornire la propria implementazione.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.

Metodi esterniExternal methods

Quando una dichiarazione di metodo include un extern modificatore, tale metodo viene definito un *metodo esterno _.When a method declaration includes an extern modifier, that method is said to be an *external method _. I metodi esterni vengono implementati esternamente, in genere usando un linguaggio diverso da C#.External methods are implemented externally, typically using a language other than C#. Poiché una dichiarazione di metodo esterno non fornisce alcuna implementazione effettiva, il _method_body * di un metodo esterno è semplicemente costituito da un punto e virgola.Because an external method declaration provides no actual implementation, the _method_body* of an external method simply consists of a semicolon. Un metodo esterno potrebbe non essere generico.An external method may not be generic.

Il extern modificatore viene in genere usato in combinazione con un DllImport attributo (interoperabilità con componenti com e Win32), consentendo l'implementazione di metodi esterni tramite dll (librerie a collegamento dinamico).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). L'ambiente di esecuzione può supportare altri meccanismi in base ai quali è possibile fornire implementazioni di metodi esterni.The execution environment may support other mechanisms whereby implementations of external methods can be provided.

Quando un metodo esterno include un DllImport attributo, la dichiarazione del metodo deve includere anche un static modificatore.When an external method includes a DllImport attribute, the method declaration must also include a static modifier. Questo esempio illustra l'uso del extern modificatore e dell' DllImport attributo: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);
}

Metodi parziali (riepilogo)Partial methods (recap)

Quando una dichiarazione di metodo include un partial modificatore, tale metodo viene definito come metodo parziale.When a method declaration includes a partial modifier, that method is said to be a partial method. I metodi parziali possono essere dichiarati solo come membri di tipi parziali (tipi parziali) e sono soggetti a una serie di restrizioni.Partial methods can only be declared as members of partial types (Partial types), and are subject to a number of restrictions. I metodi parziali sono descritti ulteriormente in metodi parziali.Partial methods are further described in Partial methods.

Metodi di estensioneExtension methods

Quando il primo parametro di un metodo include il this modificatore, tale metodo viene definito come metodo di estensione.When the first parameter of a method includes the this modifier, that method is said to be an extension method. I metodi di estensione possono essere dichiarati solo in classi statiche non generiche non nidificate.Extension methods can only be declared in non-generic, non-nested static classes. Il primo parametro di un metodo di estensione non può avere modificatori diversi da this e il tipo di parametro non può essere un tipo di puntatore.The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type.

Di seguito è riportato un esempio di una classe statica che dichiara due metodi di estensione:The following is an example of a static class that declares two extension methods:

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

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

Un metodo di estensione è un metodo statico normale.An extension method is a regular static method. Inoltre, quando la classe statica che lo contiene è nell'ambito, un metodo di estensione può essere richiamato usando la sintassi di chiamata del metodo di istanza (chiamate al metodo di estensione), usando l'espressione Receiver come primo argomento.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.

Il programma seguente usa i metodi di estensione dichiarati in precedenza: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());
        }
    }
}

Il Slice metodo è disponibile in string[] e il ToInt32 metodo è disponibile in string , perché sono stati dichiarati come metodi di estensione.The Slice method is available on the string[], and the ToInt32 method is available on string, because they have been declared as extension methods. Il significato del programma è identico a quello riportato di seguito, usando le normali chiamate al metodo statico: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 del metodoMethod body

Il method_body di una dichiarazione di metodo è costituito da un corpo del blocco, da un corpo dell'espressione o da un punto e virgola.The method_body of a method declaration consists of either a block body, an expression body or a semicolon.

Il tipo di risultato di un metodo è void se il tipo restituito è void o se il metodo è asincrono e il tipo restituito è System.Threading.Tasks.Task .The result type of a method is void if the return type is void, or if the method is async and the return type is System.Threading.Tasks.Task. In caso contrario, il tipo di risultato di un metodo non asincrono è il tipo restituito e il tipo di risultato di un metodo asincrono con tipo restituito System.Threading.Tasks.Task<T> è T .Otherwise, the result type of a non-async method is its return type, and the result type of an async method with return type System.Threading.Tasks.Task<T> is T.

Quando un metodo ha un void tipo di risultato e un corpo del blocco, return le istruzioni (istruzione return) nel blocco non sono autorizzate a specificare un'espressione.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 l'esecuzione del blocco di un metodo void viene completata normalmente (ovvero, il controllo esce dalla fine del corpo del metodo), il metodo restituisce semplicemente al chiamante corrente.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 un metodo ha un void risultato e un corpo dell'espressione, l'espressione E deve essere un statement_expression e il corpo è esattamente equivalente a un corpo del blocco del form { 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 un metodo ha un tipo di risultato non void e un corpo del blocco, ogni return istruzione nel blocco deve specificare un'espressione convertibile in modo implicito nel tipo di risultato.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. L'endpoint di un corpo di blocco di un metodo di restituzione di valori non deve essere raggiungibile.The endpoint of a block body of a value-returning method must not be reachable. In altre parole, in un metodo di restituzione di valori con un corpo del blocco, il controllo non può propagarsi alla fine del corpo del metodo.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 un metodo ha un tipo di risultato non void e un corpo dell'espressione, l'espressione deve essere convertibile in modo implicito nel tipo di risultato e il corpo è esattamente equivalente a un corpo del blocco del form { return E; } .When a method has a non-void result type and an expression body, the expression must be implicitly convertible to the result type, and the body is exactly equivalent to a block body of the form { return E; }.

Nell'esempioIn 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;
}

il metodo che restituisce il valore F genera un errore in fase di compilazione perché il controllo può propagarsi alla fine del corpo del metodo.the value-returning F method results in a compile-time error because control can flow off the end of the method body. I G H metodi e sono corretti perché tutti i possibili percorsi di esecuzione terminano in un'istruzione return che specifica un valore restituito.The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value. Il I metodo è corretto, perché il corpo è equivalente a un blocco di istruzioni con una sola istruzione return.The I method is correct, because its body is equivalent to a statement block with just a single return statement in it.

Overload di un metodoMethod overloading

Le regole di risoluzione dell'overload del metodo sono descritte in inferenza del tipo.The method overload resolution rules are described in Type inference.

ProprietàProperties

Una *Property _ è un membro che fornisce l'accesso a una caratteristica di un oggetto o di una classe.A *property _ is a member that provides access to a characteristic of an object or a class. Esempi di proprietà includono la lunghezza di una stringa, la dimensione di un tipo di carattere, la didascalia di una finestra, il nome di un cliente e così via.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. Le proprietà sono un'estensione naturale dei campi, entrambi sono membri denominati con tipi associati e la sintassi per l'accesso a campi e proprietà è la stessa.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. A differenza dei campi, tuttavia, le proprietà non denotano posizioni di memoria,However, unlike fields, properties do not denote storage locations. Le proprietà hanno invece _ funzioni di accesso* che specificano le istruzioni da eseguire quando i relativi valori vengono letti o scritti.Instead, properties have _ accessors* that specify the statements to be executed when their values are read or written. Le proprietà forniscono pertanto un meccanismo per associare le azioni alla lettura e alla scrittura degli attributi di un oggetto. consentono inoltre di calcolare tali attributi.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.

Le proprietà vengono dichiarate usando property_declaration s:Properties are declared using property_declaration s:

property_declaration
    : attributes? property_modifier* type member_name property_body
    ;

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

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

property_initializer
    : '=' variable_initializer ';'
    ;

Un property_declaration può includere un set di attributi (attributi) e una combinazione valida dei quattro modificatori di accesso (modificatori di accesso), new (il nuovo modificatore), static (metodi statici e di istanza), virtual (metodi virtuali), (metodi di override override), sealed (Metodi sealed), abstract (metodi astratti) e extern modificatori (metodi esterni).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.

Le dichiarazioni di proprietà sono soggette alle stesse regole delle dichiarazioni di metodo (Metodi) per quanto concerne combinazioni valide di modificatori.Property declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Il tipo di una dichiarazione di proprietà specifica il tipo della proprietà introdotta dalla dichiarazione e il MEMBER_NAME specifica il nome della proprietà.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 meno che la proprietà non sia un'implementazione esplicita di un membro di interfaccia, il MEMBER_NAME è semplicemente un identificatore.Unless the property is an explicit interface member implementation, the member_name is simply an identifier. Per un'implementazione esplicita di un membro di interfaccia (implementazioni esplicite di membri di interfaccia), il MEMBER_NAME è costituito da un INTERFACE_TYPE seguito da un oggetto " . " e da un identificatore.For an explicit interface member implementation (Explicit interface member implementations), the member_name consists of an interface_type followed by a "." and an identifier.

Il tipo di una proprietà deve essere accessibile almeno quanto la proprietà stessa (vincoli di accessibilità).The type of a property must be at least as accessible as the property itself (Accessibility constraints).

Un property_body può essere costituito da un corpo della funzione di accesso* o da un corpo dell'espressione.A property_body may either consist of an accessor body _ or an _expression body*_. In un corpo della funzione di accesso, _accessor_declarations *, che deve essere racchiuso tra { token "" e " } ", dichiarare le funzioni di accesso (funzioni di accesso) della proprietà.In an accessor body, _accessor_declarations*, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Le funzioni di accesso specificano le istruzioni eseguibili associate alla lettura e alla scrittura della proprietà.The accessors specify the executable statements associated with reading and writing the property.

Il corpo di un'espressione costituito da => seguito da un' espressione E e da un punto e virgola è esattamente equivalente al corpo dell'istruzione { get { return E; } } e pertanto può essere utilizzato solo per specificare le proprietà di sola getter in cui il risultato del Getter viene fornito da un'unica espressione.An expression body consisting of => followed by an expression E and a semicolon is exactly equivalent to the statement body { get { return E; } }, and can therefore only be used to specify getter-only properties where the result of the getter is given by a single expression.

Un property_initializer può essere specificato solo per una proprietà implementata automaticamente (proprietà implementate automaticamente) e causa l'inizializzazione del campo sottostante di tali proprietà con il valore fornito dall' espressione.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.

Anche se la sintassi per l'accesso a una proprietà è identica a quella di un campo, una proprietà non viene classificata come variabile.Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Non è quindi possibile passare una proprietà come ref out argomento o.Thus, it is not possible to pass a property as a ref or out argument.

Quando una dichiarazione di proprietà include un extern modificatore, la proprietà viene definita una *proprietà esterna _.When a property declaration includes an extern modifier, the property is said to be an *external property _. Poiché una dichiarazione di proprietà esterna non fornisce alcuna implementazione effettiva, ogni _accessor_declarations * è costituito da un punto e virgola.Because an external property declaration provides no actual implementation, each of its _accessor_declarations* consists of a semicolon.

Proprietà statiche e di istanzaStatic and instance properties

Quando una dichiarazione di proprietà include un static modificatore, la proprietà viene definita una *proprietà statica _.When a property declaration includes a static modifier, the property is said to be a *static property _. Quando non static è presente alcun modificatore, la proprietà viene definita una proprietà _ instance *.When no static modifier is present, the property is said to be an _*instance property**.

Una proprietà statica non è associata a un'istanza specifica e si tratta di un errore in fase di compilazione a cui fare riferimento this nelle funzioni di accesso di una proprietà statica.A static property is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static property.

Una proprietà dell'istanza è associata a una determinata istanza di una classe e a tale istanza è possibile accedere come this (questo accesso) nelle funzioni di accesso di tale proprietà.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 si fa riferimento a una proprietà in un member_access (accesso ai membri) del form E.M , se M è una proprietà statica, E deve indicare un tipo che contiene M E se M è una proprietà dell'istanza, e deve indicare un'istanza di un tipo contenente 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.

Le differenze tra i membri statici e i membri di istanza sono illustrate ulteriormente nei membri statici e di istanza.The differences between static and instance members are discussed further in Static and instance members.

Funzioni di accessoAccessors

Il accessor_declarations di una proprietà specifica le istruzioni eseguibili associate alla lettura e alla scrittura della proprietà.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
    | ';'
    ;

Le dichiarazioni delle funzioni di accesso sono costituite da un get_accessor_declaration, da un set_accessor_declaration o da entrambi.The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Ogni dichiarazione di funzione di accesso è costituita dal token get o set seguito da un accessor_modifier facoltativo e da una accessor_body.Each accessor declaration consists of the token get or set followed by an optional accessor_modifier and an accessor_body.

L'uso di accessor_modifier s è regolato dalle restrizioni seguenti:The use of accessor_modifier s is governed by the following restrictions:

  • Non è possibile usare un accessor_modifier in un'interfaccia o in un'implementazione esplicita di un membro di interfaccia.An accessor_modifier may not be used in an interface or in an explicit interface member implementation.
  • Per una proprietà o un indicizzatore senza override modificatore, è consentito un accessor_modifier solo se la proprietà o l'indicizzatore ha entrambe le funzioni di accesso get e set e quindi è consentito solo in una di queste funzioni di accesso.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.
  • Per una proprietà o un indicizzatore che include un override modificatore, una funzione di accesso deve corrispondere all' accessor_modifier, se presente, della funzione di accesso sottoposta a override.For a property or indexer that includes an override modifier, an accessor must match the accessor_modifier, if any, of the accessor being overridden.
  • Il accessor_modifier deve dichiarare un'accessibilità strettamente più restrittiva dell'accessibilità dichiarata della proprietà o dell'indicizzatore stesso.The accessor_modifier must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. Per essere precisi:To be precise:
    • Se la proprietà o l'indicizzatore ha un'accessibilità dichiarata di public , il accessor_modifier può essere protected internal ,, internal protected o private .If the property or indexer has a declared accessibility of public, the accessor_modifier may be either protected internal, internal, protected, or private.
    • Se la proprietà o l'indicizzatore ha un'accessibilità dichiarata di protected internal , il accessor_modifier può essere internal , protected o private .If the property or indexer has a declared accessibility of protected internal, the accessor_modifier may be either internal, protected, or private.
    • Se la proprietà o l'indicizzatore ha un'accessibilità dichiarata di internal o protected , il accessor_modifier deve essere private .If the property or indexer has a declared accessibility of internal or protected, the accessor_modifier must be private.
    • Se la proprietà o l'indicizzatore ha un'accessibilità dichiarata di private , non è possibile usare accessor_modifier .If the property or indexer has a declared accessibility of private, no accessor_modifier may be used.

Per abstract extern le proprietà e, il accessor_body per ogni funzione di accesso specificata è semplicemente un punto e virgola.For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. Una proprietà non astratta e non extern può avere ogni accessor_body essere un punto e virgola, nel qual caso si tratta di una proprietà * implementata automaticamente _ (proprietà implementate 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). Una proprietà implementata automaticamente deve avere almeno una funzione di accesso get.An automatically implemented property must have at least a get accessor. Per le funzioni di accesso di qualsiasi altra proprietà non astratta e non extern, il _accessor_body * è un blocco che specifica le istruzioni da eseguire quando viene richiamata la funzione di accesso corrispondente.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.

Una get funzione di accesso corrisponde a un metodo senza parametri con un valore restituito del tipo di proprietà.A get accessor corresponds to a parameterless method with a return value of the property type. Fatta eccezione per la destinazione di un'assegnazione, quando si fa riferimento a una proprietà in un'espressione, get viene richiamata la funzione di accesso della proprietà per calcolare il valore della proprietà (valori di espressioni).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). Il corpo di una get funzione di accesso deve essere conforme alle regole per i metodi di restituzione di valori descritti nel corpo del metodo.The body of a get accessor must conform to the rules for value-returning methods described in Method body. In particolare, tutte le return istruzioni nel corpo di una get funzione di accesso devono specificare un'espressione convertibile in modo implicito nel tipo della proprietà.In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. Inoltre, l'endpoint di una get funzione di accesso non deve essere raggiungibile.Furthermore, the endpoint of a get accessor must not be reachable.

Una set funzione di accesso corrisponde a un metodo con un singolo parametro di valore del tipo di proprietà e un void tipo restituito.A set accessor corresponds to a method with a single value parameter of the property type and a void return type. Il parametro implicito di una set funzione di accesso è sempre denominato value .The implicit parameter of a set accessor is always named value. Quando si fa riferimento a una proprietà come destinazione di un'assegnazione (operatori di assegnazione) o come operando di ++ o -- (operatori di incremento e decremento suffisso, operatori di incremento e decremento del prefisso), la funzione di set accesso viene richiamata con un argomento (il cui valore è quello del lato destro dell'assegnazione o l'operando dell' ++ -- operatore OR) che fornisce il nuovo valore (assegnazione sempliceWhen 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). Il corpo di una set funzione di accesso deve essere conforme alle regole per i void metodi descritti nel corpo del metodo.The body of a set accessor must conform to the rules for void methods described in Method body. In particolare, return le istruzioni nel set corpo della funzione di accesso non sono autorizzate a specificare un'espressione.In particular, return statements in the set accessor body are not permitted to specify an expression. Poiché una set funzione di accesso include implicitamente un parametro denominato value , si tratta di un errore in fase di compilazione per una variabile locale o una dichiarazione di costante in una set funzione di accesso con tale 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.

In base alla presenza o all'assenza delle get set funzioni di accesso e, una proprietà viene classificata come segue:Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • Una proprietà che include sia una get funzione di accesso sia una set funzione di accesso è detta proprietà di lettura/scrittura .A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • Una proprietà che ha solo una get funzione di accesso è detta proprietà di sola lettura .A property that has only a get accessor is said to be a read-only property. Si tratta di un errore in fase di compilazione perché una proprietà di sola lettura sia la destinazione di un'assegnazione.It is a compile-time error for a read-only property to be the target of an assignment.
  • Una proprietà che ha solo una set funzione di accesso è detta proprietà di sola scrittura .A property that has only a set accessor is said to be a write-only property. Fatta eccezione per la destinazione di un'assegnazione, si tratta di un errore in fase di compilazione per fare riferimento a una proprietà di sola scrittura in un'espressione.Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression.

Nell'esempioIn 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
    }
}

il Button controllo dichiara una proprietà pubblica Caption .the Button control declares a public Caption property. La get funzione di accesso della Caption proprietà restituisce la stringa archiviata nel caption campo privato.The get accessor of the Caption property returns the string stored in the private caption field. La set funzione di accesso controlla se il nuovo valore è diverso dal valore corrente e, in tal caso, archivia il nuovo valore e ridisegna il controllo.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. Le proprietà seguono spesso il modello illustrato in precedenza: la get funzione di accesso restituisce semplicemente un valore archiviato in un campo privato e la set funzione di accesso modifica tale campo privato e quindi esegue le azioni aggiuntive necessarie per aggiornare completamente lo stato dell'oggetto.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.

Data la Button classe precedente, di seguito è riportato un esempio di utilizzo della Caption proprietà: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

In questo caso, la set funzione di accesso viene richiamata assegnando un valore alla proprietà e la get funzione di accesso viene richiamata facendo riferimento alla proprietà in un'espressione.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.

Le get set funzioni di accesso e di una proprietà non sono membri distinti e non è possibile dichiarare separatamente le funzioni di accesso di una proprietà.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. Di conseguenza, non è possibile per le due funzioni di accesso di una proprietà di lettura/scrittura avere un'accessibilità diversa.As such, it is not possible for the two accessors of a read-write property to have different accessibility. L'esempio:The 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; }
    }
}

non dichiara una singola proprietà di lettura/scrittura.does not declare a single read-write property. Dichiara invece due proprietà con lo stesso nome, una sola lettura e una sola scrittura.Rather, it declares two properties with the same name, one read-only and one write-only. Poiché due membri dichiarati nella stessa classe non possono avere lo stesso nome, nell'esempio viene generato un errore in fase di compilazione.Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

Quando una classe derivata dichiara una proprietà con lo stesso nome di una proprietà ereditata, la proprietà derivata nasconde la proprietà ereditata in relazione alla lettura e alla scrittura.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. Nell'esempioIn the example

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

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

la P Proprietà in B nasconde la P Proprietà in A rispetto alla lettura e alla scrittura.the P property in B hides the P property in A with respect to both reading and writing. Quindi, nelle istruzioniThus, 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

l'assegnazione a b.P causa la segnalazione di un errore in fase di compilazione, poiché la P proprietà di sola lettura in B nasconde la P proprietà di sola scrittura in 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. Si noti, tuttavia, che è possibile usare un cast per accedere alla P Proprietà Hidden.Note, however, that a cast can be used to access the hidden P property.

A differenza dei campi pubblici, le proprietà forniscono una separazione tra lo stato interno di un oggetto e la relativa interfaccia pubblica.Unlike public fields, properties provide a separation between an object's internal state and its public interface. Si consideri l'esempio seguente: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; }
    }
}

Qui, la Label classe usa due int campi, x e y , per archiviare la relativa posizione.Here, the Label class uses two int fields, x and y, to store its location. Il percorso è esposto pubblicamente sia come X proprietà che Y come proprietà e come Location proprietà di tipo Point .The location is publicly exposed both as an X and a Y property and as a Location property of type Point. Se, in una versione futura di Label , diventa più comodo archiviare la posizione come Point internamente, è possibile apportare la modifica senza influire sull'interfaccia pubblica della 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; }
    }
}

Aveva x y invece public readonly campi, sarebbe stato impossibile apportare tale modifica alla Label classe.Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

L'esposizione dello stato attraverso le proprietà non è necessariamente meno efficiente rispetto all'esposizione diretta dei campi.Exposing state through properties is not necessarily any less efficient than exposing fields directly. In particolare, quando una proprietà non è virtuale e contiene solo una piccola quantità di codice, l'ambiente di esecuzione può sostituire le chiamate alle funzioni di accesso con il codice effettivo delle funzioni di accesso.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. Questo processo è noto come incorporamento e rende l'accesso alle proprietà efficiente come l'accesso al campo, ma conserva la maggiore flessibilità delle proprietà.This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

Poiché get la chiamata di una funzione di accesso è concettualmente equivalente alla lettura del valore di un campo, viene considerato uno stile di programmazione non valido per le get funzioni di accesso che hanno effetti collaterali osservabili.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. Nell'esempioIn the example

class Counter
{
    private int next;

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

il valore della Next proprietà dipende dal numero di volte in cui è stato eseguito l'accesso alla proprietà.the value of the Next property depends on the number of times the property has previously been accessed. Pertanto, l'accesso alla proprietà produce un effetto collaterale osservabile e la proprietà deve essere implementata come metodo.Thus, accessing the property produces an observable side-effect, and the property should be implemented as a method instead.

La convenzione "nessun effetto collaterale" per le get funzioni di accesso non significa che le get funzioni di accesso devono essere sempre scritte in modo da restituire semplicemente i valori archiviati nei campi.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. getLe funzioni di accesso spesso calcolano il valore di una proprietà tramite l'accesso a più campi o la chiamata di metodi.Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. Tuttavia, una get funzione di accesso progettata correttamente non esegue alcuna azione che causi modifiche osservabili nello stato dell'oggetto.However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

Le proprietà possono essere usate per ritardare l'inizializzazione di una risorsa fino al momento in cui viene fatto riferimento per la prima volta.Properties can be used to delay initialization of a resource until the moment it is first referenced. Ad esempio: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;
        }
    }
}

La Console classe contiene tre proprietà, In , Out e Error , che rappresentano rispettivamente i dispositivi di input, di output e di errore standard.The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. Esponendo tali membri come proprietà, Console è possibile che la classe ritardi l'inizializzazione fino a quando non vengono effettivamente utilizzati.By exposing these members as properties, the Console class can delay their initialization until they are actually used. Ad esempio, al primo riferimento alla Out proprietà, come inFor example, upon first referencing the Out property, as in

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

TextWriterviene creato il sottostante per il dispositivo di output.the underlying TextWriter for the output device is created. Tuttavia, se l'applicazione non fa riferimento alle In Error proprietà e, non viene creato alcun oggetto per tali dispositivi.But if the application makes no reference to the In and Error properties, then no objects are created for those devices.

Proprietà implementate automaticamenteAutomatically implemented properties

Una proprietà implementata automaticamente (o una proprietà automatica per Short), è una proprietà non-extern non astratta con corpi delle funzioni di accesso solo punto e virgola.An automatically implemented property (or auto-property for short), is a non-abstract non-extern property with semicolon-only accessor bodies. Le proprietà automatiche devono avere una funzione di accesso get e possono facoltativamente avere una funzione di accesso set.Auto-properties must have a get accessor and can optionally have a set accessor.

Quando una proprietà viene specificata come proprietà implementata automaticamente, un campo sottostante nascosto è automaticamente disponibile per la proprietà e le funzioni di accesso sono implementate per la lettura e la scrittura in quel campo sottostante.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 la proprietà auto non dispone di una funzione di accesso set, il campo sottostante viene considerato readonly (campidi sola lettura).If the auto-property has no set accessor, the backing field is considered readonly (Readonly fields). Analogamente a un readonly campo, è anche possibile assegnare una proprietà auto solo Get a nel corpo di un costruttore della classe contenitore.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. Tale assegnazione viene assegnata direttamente al campo di supporto ReadOnly della proprietà.Such an assignment assigns directly to the readonly backing field of the property.

Una proprietà automatica può facoltativamente avere un property_initializer, che viene applicato direttamente al campo sottostante come variable_initializer (inizializzatori di variabile).An auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (Variable initializers).

L'esempio seguente:The following example:

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

equivale alla dichiarazione seguente: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; } }
}

L'esempio seguente:The following example:

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

equivale alla dichiarazione seguente: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; }
}

Si noti che le assegnazioni al campo ReadOnly sono valide, perché si verificano all'interno del costruttore.Notice that the assignments to the readonly field are legal, because they occur within the constructor.

AccessibilitàAccessibility

Se una funzione di accesso dispone di un accessor_modifier, il dominio di accessibilità (domini di accessibilità) della funzione di accesso viene determinato mediante l'accessibilità dichiarata del 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 una funzione di accesso non dispone di un accessor_modifier, il dominio di accessibilità della funzione di accesso viene determinato dall'accessibilità dichiarata della proprietà o dell'indicizzatore.If an accessor does not have an accessor_modifier, the accessibility domain of the accessor is determined from the declared accessibility of the property or indexer.

La presenza di un accessor_modifier non interessa mai la ricerca dei membri (operatori) o la risoluzione dell'overload (risoluzione dell'overload).The presence of an accessor_modifier never affects member lookup (Operators) or overload resolution (Overload resolution). I modificatori della proprietà o dell'indicizzatore determinano sempre la proprietà o l'indicizzatore a cui è associato, indipendentemente dal contesto dell'accesso.The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access.

Una volta selezionata una proprietà o un indicizzatore specifico, vengono utilizzati i domini di accessibilità delle funzioni di accesso specifiche per determinare se l'utilizzo è valido: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 l'utilizzo è come valore (valori di espressioni), la get funzione di accesso deve esistere ed essere accessibile.If the usage is as a value (Values of expressions), the get accessor must exist and be accessible.
  • Se l'utilizzo è come destinazione di un'assegnazione semplice (assegnazione semplice), la set funzione di accesso deve esistere ed essere accessibile.If the usage is as the target of a simple assignment (Simple assignment), the set accessor must exist and be accessible.
  • Se l'utilizzo è come destinazione dell'assegnazione composta (assegnazione composta) o come destinazione degli ++ -- operatori OR (membri difunzione,9, espressioni di chiamata), entrambe le funzioni di get accesso e la funzione di accesso set devono esistere ed essere accessibili.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.

Nell'esempio seguente la proprietà A.Text è nascosta dalla proprietà B.Text , anche in contesti in cui viene chiamata solo la set funzione di accesso.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. Al contrario, la proprietà B.Count non è accessibile alla classe M , pertanto A.Count viene invece utilizzata la proprietà accessibile.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
    }
}

Una funzione di accesso usata per implementare un'interfaccia potrebbe non avere un accessor_modifier.An accessor that is used to implement an interface may not have an accessor_modifier. Se per implementare un'interfaccia viene usata una sola funzione di accesso, l'altra funzione di accesso può essere dichiarata con un accessor_modifier:If only one accessor is used to implement an interface, the other accessor may be declared with an accessor_modifier:

public interface I
{
    string Prop { get; }
}

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

Funzioni di accesso alle proprietà virtuali, sealed, override e astratteVirtual, sealed, override, and abstract property accessors

Una virtual dichiarazione di proprietà specifica che le funzioni di accesso della proprietà sono virtuali.A virtual property declaration specifies that the accessors of the property are virtual. Il virtual modificatore si applica a entrambe le funzioni di accesso di una proprietà di lettura/scrittura. non è possibile che una sola funzione di accesso di una proprietà di lettura/scrittura sia virtuale.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.

Una abstract dichiarazione di proprietà specifica che le funzioni di accesso della proprietà sono virtuali, ma non fornisce un'implementazione effettiva delle funzioni di accesso.An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. Al contrario, le classi derivate non astratte sono necessarie per fornire la propria implementazione per le funzioni di accesso eseguendo l'override della proprietà.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. Poiché una funzione di accesso per una dichiarazione di proprietà astratta non fornisce alcuna implementazione effettiva, il accessor_body è semplicemente costituito da un punto e virgola.Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon.

Una dichiarazione di proprietà che include entrambi abstract i override modificatori e specifica che la proprietà è astratta ed esegue l'override di una proprietà di base.A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. Anche le funzioni di accesso di tale proprietà sono astratte.The accessors of such a property are also abstract.

Le dichiarazioni di proprietà astratte sono consentite solo nelle classi astratte (classi astratte). È possibile eseguire l'override delle funzioni di accesso di una proprietà virtuale ereditata in una classe derivata includendo una dichiarazione di proprietà che specifica una override direttiva.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. Questa operazione è nota come dichiarazione di proprietà che esegue l'override.This is known as an overriding property declaration. Una dichiarazione di proprietà che esegue l'override non dichiara una nuova proprietà.An overriding property declaration does not declare a new property. Ma semplicemente specializza le implementazioni delle funzioni di accesso di una proprietà virtuale esistente.Instead, it simply specializes the implementations of the accessors of an existing virtual property.

Una dichiarazione di proprietà che esegue l'override deve specificare esattamente gli stessi modificatori di accessibilità, tipo e nome della proprietà ereditata.An overriding property declaration must specify the exact same accessibility modifiers, type, and name as the inherited property. Se la proprietà ereditata ha solo una singola funzione di accesso (ad esempio, se la proprietà ereditata è di sola lettura o di sola scrittura), la proprietà che esegue l'override deve includere solo tale funzione di accesso.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 la proprietà ereditata include entrambe le funzioni di accesso (ad esempio, se la proprietà ereditata è di lettura/scrittura), la proprietà che esegue l'override può includere una singola funzione di accesso o entrambe le funzioni di accesso.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.

Una dichiarazione di proprietà che esegue l'override può includere il sealed modificatore.An overriding property declaration may include the sealed modifier. L'uso di questo modificatore impedisce a una classe derivata di eseguire ulteriormente l'override della proprietà.Use of this modifier prevents a derived class from further overriding the property. Anche le funzioni di accesso di una proprietà sealed sono sealed.The accessors of a sealed property are also sealed.

Ad eccezione delle differenze nella sintassi della dichiarazione e della chiamata, le funzioni di accesso Virtual, sealed, override e abstract si comportano esattamente come i metodi virtual, sealed, override e abstract.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. In particolare, le regole descritte in metodi virtuali, metodi di override, Metodi sealede metodi astratti si applicano come se le funzioni di accesso fossero metodi di un form corrispondente:Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form:

  • Una get funzione di accesso corrisponde a un metodo senza parametri con un valore restituito del tipo di proprietà e gli stessi modificatori della proprietà che lo contiene.A get accessor corresponds to a parameterless method with a return value of the property type and the same modifiers as the containing property.
  • Una set funzione di accesso corrisponde a un metodo con un singolo parametro di valore del tipo di proprietà, un void tipo restituito e gli stessi modificatori della proprietà che lo contiene.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.

Nell'esempioIn 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 è una proprietà di sola lettura virtuale, Y è una proprietà di lettura/scrittura virtuale ed Z è una proprietà astratta di lettura/scrittura.X is a virtual read-only property, Y is a virtual read-write property, and Z is an abstract read-write property. Poiché Z è astratto, la classe che lo contiene A deve anche essere dichiarata astratta.Because Z is abstract, the containing class A must also be declared abstract.

Una classe che deriva da A viene mostrata di seguito: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; }
    }
}

Qui, le dichiarazioni di X , Y e eseguono l' Z override delle dichiarazioni di proprietà.Here, the declarations of X, Y, and Z are overriding property declarations. Ogni dichiarazione di proprietà corrisponde esattamente ai modificatori di accessibilità, al tipo e al nome della proprietà ereditata corrispondente.Each property declaration exactly matches the accessibility modifiers, type, and name of the corresponding inherited property. La get funzione di accesso di X e la set funzione di accesso di Y usano la base parola chiave per accedere alle funzioni di accesso ereditate.The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. La dichiarazione di Z esegue l'override di entrambe le funzioni di accesso astratte, pertanto non esistono membri di funzioni astratte in attesa in B e B può essere una classe non astratta.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 una proprietà viene dichiarata come override , tutte le funzioni di accesso sottoposte a override devono essere accessibili al codice che esegue l'override.When a property is declared as an override, any overridden accessors must be accessible to the overriding code. Inoltre, l'accessibilità dichiarata sia della proprietà che dell'indicizzatore e delle funzioni di accesso devono corrispondere a quelle del membro e delle funzioni di accesso sottoposti a override.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. Ad esempio: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
    }
}

EventiEvents

Un *Event _ è un membro che consente a un oggetto o a una classe di fornire notifiche.An *event _ is a member that enables an object or class to provide notifications. I client possono alleghino il codice eseguibile per gli eventi fornendo i gestori eventi* *.Clients can attach executable code for events by supplying _*event handlers**.

Gli eventi vengono dichiarati usando event_declaration s:Events are declared using event_declaration s:

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

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

event_accessor_declarations
    : add_accessor_declaration remove_accessor_declaration
    | remove_accessor_declaration add_accessor_declaration
    ;

add_accessor_declaration
    : attributes? 'add' block
    ;

remove_accessor_declaration
    : attributes? 'remove' block
    ;

Un event_declaration può includere un set di attributi (attributi) e una combinazione valida dei quattro modificatori di accesso (modificatori di accesso), new (il nuovo modificatore), static (metodi statici e di istanza), virtual (metodi virtuali), (metodi di override override), sealed (Metodi sealed), abstract (metodi astratti) e extern modificatori (metodi esterni).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.

Le dichiarazioni di evento sono soggette alle stesse regole delle dichiarazioni di metodo (Metodi) per quanto concerne combinazioni valide di modificatori.Event declarations are subject to the same rules as method declarations (Methods) with regard to valid combinations of modifiers.

Il tipo di una dichiarazione di evento deve essere un delegate_type (tipi di riferimento) e tale delegate_type deve essere accessibile almeno quanto l'evento stesso (vincoli di accessibilità).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).

Una dichiarazione di evento può includere event_accessor_declarations.An event declaration may include event_accessor_declarations. Tuttavia, in caso contrario, per gli eventi non esterni, non astratti, il compilatore li fornisce automaticamente (eventi simili ai campi); per gli eventi extern, le funzioni di accesso vengono fornite esternamente.However, if it does not, for non-extern, non-abstract events, the compiler supplies them automatically (Field-like events); for extern events, the accessors are provided externally.

Una dichiarazione di evento che omette event_accessor_declarations definisce uno o più eventi, uno per ogni variable_declarator.An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarator s. Gli attributi e i modificatori si applicano a tutti i membri dichiarati da tale event_declaration.The attributes and modifiers apply to all of the members declared by such an event_declaration.

Si tratta di un errore in fase di compilazione per un event_declaration includere sia il abstract modificatore che il event_accessor_declarations delimitato da parentesi graffe.It is a compile-time error for an event_declaration to include both the abstract modifier and brace-delimited event_accessor_declarations.

Quando una dichiarazione di evento include un extern modificatore, l'evento viene definito un evento * esterno _.When an event declaration includes an extern modifier, the event is said to be an *external event _. Poiché una dichiarazione di evento esterno non fornisce alcuna implementazione effettiva, è un errore per includere sia il extern modificatore che _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*.

Si tratta di un errore in fase di compilazione per un variable_declarator di una dichiarazione di evento con un abstract external modificatore o per includere una variable_initializer.It is a compile-time error for a variable_declarator of an event declaration with an abstract or external modifier to include a variable_initializer.

Un evento può essere usato come operando sinistro degli operatori += e -= (Assegnazione di eventi).An event can be used as the left-hand operand of the += and -= operators (Event assignment). Questi operatori vengono utilizzati rispettivamente per il collegamento di gestori eventi a o per rimuovere i gestori eventi da un evento e i modificatori di accesso dell'evento controllano i contesti in cui sono consentite tali operazioni.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.

Poiché += e -= sono le uniche operazioni consentite su un evento all'esterno del tipo che dichiara l'evento, il codice esterno può aggiungere e rimuovere gestori per un evento, ma non può in alcun altro modo ottenere o modificare l'elenco sottostante di gestori eventi.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.

In un'operazione del form x += y o x -= y , quando x è un evento e il riferimento viene eseguito all'esterno del tipo che contiene la dichiarazione di x , il risultato dell'operazione è di tipo void (in contrapposizione al tipo di x , con il valore di x dopo l'assegnazione).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). Questa regola impedisce al codice esterno di esaminare indirettamente il delegato sottostante di un evento.This rule prohibits external code from indirectly examining the underlying delegate of an event.

Nell'esempio seguente viene illustrato il modo in cui i gestori eventi sono collegati alle istanze della Button 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
    }
}

In questo caso, il LoginDialog costruttore di istanze crea due Button istanze e connette i gestori eventi agli Click eventi.Here, the LoginDialog instance constructor creates two Button instances and attaches event handlers to the Click events.

Eventi simili a campiField-like events

All'interno del testo del programma della classe o dello struct che contiene la dichiarazione di un evento, è possibile usare alcuni eventi come i campi.Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. Per poter essere usati in questo modo, un evento non deve essere abstract o extern e non deve includere in modo esplicito 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. Un evento di questo tipo può essere utilizzato in qualsiasi contesto che consenta un campo.Such an event can be used in any context that permits a field. Il campo contiene un delegato (delegati) che fa riferimento all'elenco di gestori di eventi che sono stati aggiunti all'evento.The field contains a delegate (Delegates) which refers to the list of event handlers that have been added to the event. Se non è stato aggiunto alcun gestore eventi, il campo contiene null .If no event handlers have been added, the field contains null.

Nell'esempioIn 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 viene usato come campo all'interno della Button classe.Click is used as a field within the Button class. Come illustrato nell'esempio, il campo può essere esaminato, modificato e utilizzato nelle espressioni di chiamata dei delegati.As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. Il OnClick metodo nella Button classe "genera" l' Click evento.The OnClick method in the Button class "raises" the Click event. Generare un evento equivale a richiamare il delegato rappresentato dall'evento. Non sono quindi necessari speciali costrutti di linguaggio per generare eventi.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. Si noti che la chiamata al delegato è preceduta da un controllo che garantisce che il delegato sia diverso da null.Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

Al di fuori della dichiarazione della Button classe, il Click membro può essere usato solo sul lato sinistro degli += -= operatori e, come inOutside 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(...);

che aggiunge un delegato all'elenco chiamate dell' Click evento ewhich appends a delegate to the invocation list of the Click event, and

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

che rimuove un delegato dall'elenco chiamate dell' Click evento.which removes a delegate from the invocation list of the Click event.

Quando si compila un evento di tipo campo, il compilatore crea automaticamente lo spazio di archiviazione per conservare il delegato e crea funzioni di accesso per l'evento che aggiungono o rimuovono gestori eventi nel campo delegato.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. Le operazioni di aggiunta e rimozione sono thread-safe e possono essere eseguite, ma non necessarie, durante l'esecuzione del blocco (istruzione lock) nell'oggetto contenitore per un evento di istanza o l'oggetto tipo (espressioni dicreazione di oggetti anonimi) per un evento statico.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.

Pertanto, una dichiarazione di evento di istanza nel formato seguente:Thus, an instance event declaration of the form:

class X
{
    public event D Ev;
}

verrà compilato in un valore 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 */
        }
    }
}

All'interno della classe X , i riferimenti a Ev sul lato sinistro degli += -= operatori e provocano la chiamata delle funzioni di accesso Add e Remove.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. Tutti gli altri riferimenti a Ev vengono compilati in modo da fare riferimento al campo nascosto __Ev (accesso ai membri).All other references to Ev are compiled to reference the hidden field __Ev instead (Member access). Il nome " __Ev " è arbitrario. il campo nascosto potrebbe avere qualsiasi nome o nessun nome.The name "__Ev" is arbitrary; the hidden field could have any name or no name at all.

Funzioni di accesso agli eventiEvent accessors

Le dichiarazioni di evento in genere omettono event_accessor_declarations, come nell' Button esempio precedente.Event declarations typically omit event_accessor_declarations, as in the Button example above. Una situazione in cui è necessario eseguire questa operazione riguarda il caso in cui il costo di archiviazione di un campo per ogni evento non è accettabile.One situation for doing so involves the case in which the storage cost of one field per event is not acceptable. In questi casi, una classe può includere event_accessor_declarations e usare un meccanismo privato per archiviare l'elenco dei gestori eventi.In such cases, a class can include event_accessor_declarations and use a private mechanism for storing the list of event handlers.

Il event_accessor_declarations di un evento specifica le istruzioni eseguibili associate all'aggiunta e alla rimozione di gestori eventi.The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers.

Le dichiarazioni delle funzioni di accesso sono costituite da un add_accessor_declaration e una remove_accessor_declaration.The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Ogni dichiarazione di funzione di accesso è costituita dal token add o remove seguito da un blocco.Each accessor declaration consists of the token add or remove followed by a block. Il blocco associato a un add_accessor_declaration specifica le istruzioni da eseguire quando viene aggiunto un gestore eventi e il blocco associato a una remove_accessor_declaration specifica le istruzioni da eseguire quando viene rimosso un gestore eventi.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.

Ogni add_accessor_declaration e remove_accessor_declaration corrisponde a un metodo con un singolo parametro di valore del tipo di evento e un void tipo restituito.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. Il parametro implicito di una funzione di accesso eventi è denominato value .The implicit parameter of an event accessor is named value. Quando un evento viene utilizzato in un'assegnazione di evento, viene utilizzata la funzione di accesso eventi appropriata.When an event is used in an event assignment, the appropriate event accessor is used. In particolare, se l'operatore di assegnazione è += , viene usata la funzione di accesso Add e se l'operatore di assegnazione è, -= viene usata la funzione di accesso remove.Specifically, if the assignment operator is += then the add accessor is used, and if the assignment operator is -= then the remove accessor is used. In entrambi i casi, l'operando destro dell'operatore di assegnazione viene usato come argomento della funzione di accesso dell'evento.In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. Il blocco di un add_accessor_declaration o un remove_accessor_declaration deve essere conforme alle regole per i void metodi descritti nel corpo del metodo.The block of an add_accessor_declaration or a remove_accessor_declaration must conform to the rules for void methods described in Method body. In particolare, le return istruzioni in un blocco di questo tipo non sono autorizzate a specificare un'espressione.In particular, return statements in such a block are not permitted to specify an expression.

Poiché una funzione di accesso agli eventi include implicitamente un parametro denominato value , si tratta di un errore in fase di compilazione per una variabile locale o una costante dichiarata in una funzione di accesso a un evento con tale 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.

Nell'esempioIn the example

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

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

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

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

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

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

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

la Control classe implementa un meccanismo di archiviazione interno per gli eventi.the Control class implements an internal storage mechanism for events. Il AddEventHandler metodo associa un valore delegato a una chiave, il GetEventHandler metodo restituisce il delegato attualmente associato a una chiave e il RemoveEventHandler metodo rimuove un delegato come gestore eventi per l'evento specificato.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. Presumibilmente, il meccanismo di archiviazione sottostante è progettato in modo che non vi sia alcun costo per l'associazione di un null valore delegato a una chiave, quindi gli eventi non gestiti non utilizzano alcuna risorsa di archiviazione.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.

Eventi statici e di istanzaStatic and instance events

Quando una dichiarazione di evento include un static modificatore, viene definito un **evento statico***.When an event declaration includes a static modifier, the event is said to be a *static event _. Quando non static è presente alcun modificatore, l'evento viene definito come un evento * * dell'istanza* *.When no static modifier is present, the event is said to be an _*instance event**.

Un evento statico non è associato a un'istanza specifica e si tratta di un errore in fase di compilazione a cui fare riferimento this nelle funzioni di accesso di un evento statico.A static event is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static event.

Un evento di istanza è associato a una determinata istanza di una classe e a questa istanza è possibile accedere come this (questo accesso) nelle funzioni di accesso dell'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 si fa riferimento a un evento in un member_access (accesso ai membri) del form E.M , se M è un evento statico, E deve indicare un tipo che contiene M E se M è un evento di istanza, e deve indicare un'istanza di un tipo contenente 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.

Le differenze tra i membri statici e i membri di istanza sono illustrate ulteriormente nei membri statici e di istanza.The differences between static and instance members are discussed further in Static and instance members.

Funzioni di accesso a eventi virtuali, sealed, override e astrattiVirtual, sealed, override, and abstract event accessors

Una virtual dichiarazione di evento specifica che le funzioni di accesso di tale evento sono virtuali.A virtual event declaration specifies that the accessors of that event are virtual. Il virtual modificatore si applica a entrambe le funzioni di accesso di un evento.The virtual modifier applies to both accessors of an event.

Una abstract dichiarazione di evento specifica che le funzioni di accesso dell'evento sono virtuali, ma non fornisce un'implementazione effettiva delle funzioni di accesso.An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. Al contrario, le classi derivate non astratte sono necessarie per fornire la propria implementazione per le funzioni di accesso eseguendo l'override dell'evento.Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. Poiché una dichiarazione di evento astratto non fornisce alcuna implementazione effettiva, non può fornire event_accessor_declarations delimitati da parentesi graffe.Because an abstract event declaration provides no actual implementation, it cannot provide brace-delimited event_accessor_declarations.

Una dichiarazione di evento che include entrambi abstract i override modificatori e specifica che l'evento è astratto ed esegue l'override di un evento di base.An event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. Anche le funzioni di accesso di tale evento sono astratte.The accessors of such an event are also abstract.

Le dichiarazioni di eventi astratti sono consentite solo nelle classi astratte (classi astratte).Abstract event declarations are only permitted in abstract classes (Abstract classes).

Le funzioni di accesso di un evento virtuale ereditato possono essere sottoposte a override in una classe derivata includendo una dichiarazione di evento che specifica un override modificatore.The accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an override modifier. Questa operazione è nota come dichiarazione di evento di override.This is known as an overriding event declaration. Una dichiarazione di evento che esegue l'override non dichiara un nuovo evento.An overriding event declaration does not declare a new event. Ma semplicemente specializza le implementazioni delle funzioni di accesso di un evento virtuale esistente.Instead, it simply specializes the implementations of the accessors of an existing virtual event.

Una dichiarazione di evento di override deve specificare esattamente gli stessi modificatori di accessibilità, tipo e nome dell'evento sottoposto a override.An overriding event declaration must specify the exact same accessibility modifiers, type, and name as the overridden event.

Una dichiarazione di evento di override può includere il sealed modificatore.An overriding event declaration may include the sealed modifier. L'uso di questo modificatore impedisce a una classe derivata di eseguire ulteriormente l'override dell'evento.Use of this modifier prevents a derived class from further overriding the event. Anche le funzioni di accesso di un evento Sealed sono sealed.The accessors of a sealed event are also sealed.

Si tratta di un errore in fase di compilazione per la dichiarazione di un evento che esegue l'override per includere un new modificatore.It is a compile-time error for an overriding event declaration to include a new modifier.

Ad eccezione delle differenze nella sintassi della dichiarazione e della chiamata, le funzioni di accesso Virtual, sealed, override e abstract si comportano esattamente come i metodi virtual, sealed, override e abstract.Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. In particolare, le regole descritte in metodi virtuali, metodi di override, Metodi sealede metodi astratti si applicano come se le funzioni di accesso fossero metodi di un form corrispondente.Specifically, the rules described in Virtual methods, Override methods, Sealed methods, and Abstract methods apply as if accessors were methods of a corresponding form. Ogni funzione di accesso corrisponde a un metodo con un singolo parametro di valore del tipo di evento, un void tipo restituito e gli stessi modificatori dell'evento che lo contiene.Each accessor corresponds to a method with a single value parameter of the event type, a void return type, and the same modifiers as the containing event.

IndicizzatoriIndexers

Un *indicizzatore _ è un membro che consente l'indicizzazione di un oggetto nello stesso modo in cui si trova una matrice.An *indexer _ is a member that enables an object to be indexed in the same way as an array. Gli indicizzatori vengono dichiarati usando _indexer_declaration * s:Indexers are declared using _indexer_declaration*s:

indexer_declaration
    : attributes? indexer_modifier* indexer_declarator indexer_body
    ;

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

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

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

Un indexer_declaration può includere un set di attributi (attributi) e una combinazione valida dei quattro modificatori di accesso (modificatori di accesso), new (il nuovo modificatore), virtual (metodi virtuali), (metodi di override override), sealed (Metodi sealed), abstract (metodi astratti) e extern modificatori (metodi esterni).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.

Le dichiarazioni dell'indicizzatore sono soggette alle stesse regole delle dichiarazioni di metodo (Metodi) per quanto riguarda le combinazioni valide di modificatori, con la sola eccezione che il modificatore static non è consentito in una dichiarazione dell'indicizzatore.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.

I modificatori virtual , override e si escludono abstract a vicenda, tranne che in un caso.The modifiers virtual, override, and abstract are mutually exclusive except in one case. I abstract override modificatori e possono essere usati insieme in modo che un indicizzatore astratto possa eseguire l'override di uno virtuale.The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one.

Il tipo di una dichiarazione di indicizzatore specifica il tipo di elemento dell'indicizzatore introdotto dalla dichiarazione.The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. A meno che l'indicizzatore non sia un'implementazione esplicita di un membro di interfaccia, il tipo è seguito dalla parola chiave this .Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. Per un'implementazione esplicita di un membro di interfaccia, il tipo è seguito da un INTERFACE_TYPE, da " . " e dalla parola chiave this .For an explicit interface member implementation, the type is followed by an interface_type, a ".", and the keyword this. A differenza di altri membri, gli indicizzatori non hanno nomi definiti dall'utente.Unlike other members, indexers do not have user-defined names.

Il formal_parameter_list specifica i parametri dell'indicizzatore.The formal_parameter_list specifies the parameters of the indexer. L'elenco di parametri formali di un indicizzatore corrisponde a quello di un metodo (parametri del metodo), ad eccezione del fatto che è necessario specificare almeno un parametro e che i ref out modificatori di parametro e non sono consentiti.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.

Il tipo di un indicizzatore e ognuno dei tipi a cui si fa riferimento nella formal_parameter_list deve essere accessibile almeno quanto l'indicizzatore stesso (vincoli di accessibilità).The type of an indexer and each of the types referenced in the formal_parameter_list must be at least as accessible as the indexer itself (Accessibility constraints).

Un indexer_body può essere costituito da un corpo della funzione di accesso* o da un corpo dell'espressione.An indexer_body may either consist of an accessor body _ or an _expression body*_. In un corpo della funzione di accesso, _accessor_declarations *, che deve essere racchiuso tra { token "" e " } ", dichiarare le funzioni di accesso (funzioni di accesso) della proprietà.In an accessor body, _accessor_declarations*, which must be enclosed in "{" and "}" tokens, declare the accessors (Accessors) of the property. Le funzioni di accesso specificano le istruzioni eseguibili associate alla lettura e alla scrittura della proprietà.The accessors specify the executable statements associated with reading and writing the property.

Il corpo di un'espressione costituito da " => " seguito da un'espressione E e da un punto e virgola è esattamente equivalente al corpo dell'istruzione { get { return E; } } e pertanto può essere utilizzato solo per specificare gli indicizzatori solo Get in cui il risultato del Getter viene fornito da un'unica espressione.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.

Anche se la sintassi per l'accesso a un indicizzatore è identica a quella di un elemento di matrice, un elemento indicizzatore non viene classificato come variabile.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. Non è quindi possibile passare un elemento indicizzatore come ref out argomento o.Thus, it is not possible to pass an indexer element as a ref or out argument.

L'elenco di parametri formali di un indicizzatore definisce la firma (firme e overload) dell'indicizzatore.The formal parameter list of an indexer defines the signature (Signatures and overloading) of the indexer. In particolare, la firma di un indicizzatore è costituita dal numero e dai tipi dei parametri formali.Specifically, the signature of an indexer consists of the number and types of its formal parameters. Il tipo di elemento e i nomi dei parametri formali non fanno parte della firma di un indicizzatore.The element type and names of the formal parameters are not part of an indexer's signature.

La firma di un indicizzatore deve essere diversa dalle firme di tutti gli altri indicizzatori dichiarati nella stessa classe.The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

Gli indicizzatori e le proprietà sono molto simili, ma si differenziano nei modi seguenti:Indexers and properties are very similar in concept, but differ in the following ways:

  • Una proprietà viene identificata in base al nome, mentre un indicizzatore viene identificato dalla firma.A property is identified by its name, whereas an indexer is identified by its signature.
  • È possibile accedere a una proprietà tramite un simple_name (nomi semplici) o una member_access (accesso ai membri), mentre un elemento indicizzatore è accessibile tramite un element_access (accesso all'indicizzatore).A property is accessed through a simple_name (Simple names) or a member_access (Member access), whereas an indexer element is accessed through an element_access (Indexer access).
  • Una proprietà può essere un static membro, mentre un indicizzatore è sempre un membro di istanza.A property can be a static member, whereas an indexer is always an instance member.
  • Una get funzione di accesso di una proprietà corrisponde a un metodo senza parametri, mentre una get funzione di accesso di un indicizzatore corrisponde a un metodo con lo stesso elenco di parametri formali dell'indicizzatore.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.
  • Una set funzione di accesso di una proprietà corrisponde a un metodo con un solo parametro denominato value , mentre una set funzione di accesso di un indicizzatore corrisponde a un metodo con lo stesso elenco di parametri formali dell'indicizzatore, oltre a un parametro aggiuntivo denominato 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.
  • Si tratta di un errore in fase di compilazione che consente a una funzione di accesso dell'indicizzatore di dichiarare una variabile locale con lo stesso nome di un parametro dell'indicizzatore.It is a compile-time error for an indexer accessor to declare a local variable with the same name as an indexer parameter.
  • In una dichiarazione di proprietà che esegue l'override, viene eseguito l'accesso alla proprietà ereditata utilizzando la sintassi base.P , dove P è il nome della proprietà.In an overriding property declaration, the inherited property is accessed using the syntax base.P, where P is the property name. In una dichiarazione di indicizzatore che esegue l'override, viene eseguito l'accesso all'indicizzatore ereditato usando la sintassi base[E] , dove E è un elenco di espressioni separate da virgole.In an overriding indexer declaration, the inherited indexer is accessed using the syntax base[E], where E is a comma separated list of expressions.
  • Non esiste alcun concetto di "indicizzatore implementato automaticamente".There is no concept of an "automatically implemented indexer". È un errore avere un indicizzatore non astratto e non esterno con funzioni di accesso punto e virgola.It is an error to have a non-abstract, non-external indexer with semicolon accessors.

A parte queste differenze, tutte le regole definite nelle funzioni di accesso e le proprietà implementate automaticamente si applicano alle funzioni di accesso dell'indicizzatore, oltre che alle funzioni di accesso alle proprietà.Aside from these differences, all rules defined in Accessors and Automatically implemented properties apply to indexer accessors as well as to property accessors.

Quando una dichiarazione di indicizzatore include un extern modificatore, l'indicizzatore viene definito un *indicizzatore esterno _.When an indexer declaration includes an extern modifier, the indexer is said to be an *external indexer _. Poiché una dichiarazione di indicizzatore esterno non fornisce alcuna implementazione effettiva, ogni _accessor_declarations * è costituito da un punto e virgola.Because an external indexer declaration provides no actual implementation, each of its _accessor_declarations* consists of a semicolon.

Nell'esempio seguente viene dichiarata una BitArray classe che implementa un indicizzatore per accedere ai singoli bit nella matrice di bit.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);
            }
        }
    }
}

Un'istanza della BitArray classe utilizza sostanzialmente meno memoria rispetto a un oggetto corrispondente bool[] (poiché ogni valore del primo occupa un solo bit anziché un solo byte di quest'ultimo), ma consente le stesse operazioni di un oggetto bool[] .An instance of the BitArray class consumes substantially less memory than a corresponding bool[] (since each value of the former occupies only one bit instead of the latter's one byte), but it permits the same operations as a bool[].

La CountPrimes classe seguente usa un BitArray e l'algoritmo classico "sieve" per calcolare il numero di numeri primi tra 1 e un valore massimo specificato: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);
    }
}

Si noti che la sintassi per l'accesso agli elementi di BitArray è esattamente identica a quella di un oggetto bool[] .Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].

Nell'esempio seguente viene illustrata una classe della griglia di 26 * 10 con un indicizzatore con due parametri.The following example shows a 26 * 10 grid class that has an indexer with two parameters. Il primo parametro deve essere una lettera maiuscola o minuscola nell'intervallo A-Z e il secondo deve essere un numero intero compreso nell'intervallo 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;
        }
    }
}

Overload dell'indicizzatoreIndexer overloading

Le regole di risoluzione dell'overload dell'indicizzatore sono descritte in inferenza del tipo.The indexer overload resolution rules are described in Type inference.

OperatoriOperators

*Operator _ è un membro che definisce il significato di un operatore Expression che può essere applicato alle istanze della classe.An *operator _ is a member that defines the meaning of an expression operator that can be applied to instances of the class. Gli operatori vengono dichiarati utilizzando _operator_declaration * s:Operators are declared using _operator_declaration*s:

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 ';'
    | ';'
    ;

Sono disponibili tre categorie di operatori che eseguono l'overload: gli operatori unari (operatori unari), gli operatori binari (operatori binari) e gli operatori di conversione (operatori di conversione).There are three categories of overloadable operators: Unary operators (Unary operators), binary operators (Binary operators), and conversion operators (Conversion operators).

Il operator_body è un punto e virgola, un'istruzione * Body _ o un corpo dell' espressione.The operator_body is either a semicolon, a statement body _ or an _expression body*_. Il corpo di un'istruzione è costituito da un _block *, che specifica le istruzioni da eseguire quando viene richiamato l'operatore.A statement body consists of a _block*, which specifies the statements to execute when the operator is invoked. Il blocco deve essere conforme alle regole per i metodi che restituiscono valori descritti nel corpo del metodo.The block must conform to the rules for value-returning methods described in Method body. Il corpo di un'espressione è costituito => da seguito da un'espressione e da un punto e virgola e indica una singola espressione da eseguire quando viene richiamato l'operatore.An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked.

Per extern gli operatori, il operator_body è costituito semplicemente da un punto e virgola.For extern operators, the operator_body consists simply of a semicolon. Per tutti gli altri operatori, il operator_body è un corpo del blocco o un corpo dell'espressione.For all other operators, the operator_body is either a block body or an expression body.

Le regole seguenti si applicano a tutte le dichiarazioni di operatore:The following rules apply to all operator declarations:

  • Una dichiarazione di operatore deve includere sia un public static modificatore che un modificatore.An operator declaration must include both a public and a static modifier.
  • Il parametro o i parametri di un operatore devono essere parametri valore (parametri valore).The parameter(s) of an operator must be value parameters (Value parameters). Si tratta di un errore in fase di compilazione per una dichiarazione di operatore per specificare i ref out parametri o.It is a compile-time error for an operator declaration to specify ref or out parameters.
  • La firma di un operatore (operatori unari, operatori binari, operatori di conversione) deve differire dalle firme di tutti gli altri operatori dichiarati nella stessa 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.
  • Tutti i tipi a cui viene fatto riferimento in una dichiarazione di operatore devono essere accessibili almeno quanto l'operatore stesso (vincoli di accessibilità).All types referenced in an operator declaration must be at least as accessible as the operator itself (Accessibility constraints).
  • È un errore che lo stesso modificatore venga visualizzato più volte in una dichiarazione di operatore.It is an error for the same modifier to appear multiple times in an operator declaration.

Ogni categoria di operatori impone restrizioni aggiuntive, come descritto nelle sezioni seguenti.Each operator category imposes additional restrictions, as described in the following sections.

Come gli altri membri, gli operatori dichiarati in una classe di base vengono ereditati dalle classi derivate.Like other members, operators declared in a base class are inherited by derived classes. Poiché le dichiarazioni di operatore richiedono sempre la classe o lo struct in cui l'operatore viene dichiarato per partecipare alla firma dell'operatore, non è possibile che un operatore dichiarato in una classe derivata nasconda un operatore dichiarato in una classe di 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. Pertanto, il new modificatore non è mai necessario e quindi mai consentito in una dichiarazione di operatore.Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

Ulteriori informazioni sugli operatori unari e binari sono reperibili negli operatori.Additional information on unary and binary operators can be found in Operators.

Altre informazioni sugli operatori di conversione sono disponibili nelle conversioni definite dall'utente.Additional information on conversion operators can be found in User-defined conversions.

Operatori unariUnary operators

Le regole seguenti si applicano alle dichiarazioni dell'operatore unario, dove T indica il tipo di istanza della classe o dello struct che contiene la dichiarazione dell'operatore:The following rules apply to unary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Un operatore unario + ,, - ! o ~ deve prendere un solo parametro di tipo T o T? e può restituire qualsiasi tipo.A unary +, -, !, or ~ operator must take a single parameter of type T or T? and can return any type.
  • Un operatore unario ++ o -- deve prendere un solo parametro di tipo T o T? e deve restituire lo stesso tipo o un tipo derivato da esso.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.
  • Un operatore unario true o false deve prendere un solo parametro di tipo T o T? e deve restituire il tipo bool .A unary true or false operator must take a single parameter of type T or T? and must return type bool.

La firma di un operatore unario è costituita dal token dell'operatore ( + ,, - ! , ~ , ++ , -- , true o false ) e dal tipo del singolo parametro formale.The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. Il tipo restituito non fa parte della firma di un operatore unario, né è il nome del parametro formale.The return type is not part of a unary operator's signature, nor is the name of the formal parameter.

Gli true false operatori unari e richiedono la dichiarazione di coppie.The true and false unary operators require pair-wise declaration. Si verifica un errore in fase di compilazione se una classe dichiara uno di questi operatori senza dichiarare anche l'altro.A compile-time error occurs if a class declares one of these operators without also declaring the other. Gli true false operatori e sono descritti ulteriormente negli operatori logici condizionali definiti dall'utente e nelle espressioni booleane.The true and false operators are described further in User-defined conditional logical operators and Boolean expressions.

Nell'esempio seguente viene illustrata un'implementazione e l'utilizzo successivo di operator ++ per una classe Vector di tipo Integer: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
    }
}

Si noti il modo in cui il metodo operator restituisce il valore prodotto aggiungendo 1 all'operando, esattamente come gli operatori di incremento e decremento suffisso (operatori di incremento e decremento suffisso) e gli operatori di incremento e decremento del prefisso (operatori di incremento e decrementodel prefisso).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). Diversamente da C++, questo metodo non deve modificare direttamente il valore del relativo operando.Unlike in C++, this method need not modify the value of its operand directly. Infatti, la modifica del valore dell'operando violerebbe la semantica standard dell'operatore di incremento suffisso.In fact, modifying the operand value would violate the standard semantics of the postfix increment operator.

Operatori binariBinary operators

Le regole seguenti si applicano alle dichiarazioni di operatori binari, dove T indica il tipo di istanza della classe o dello struct che contiene la dichiarazione dell'operatore:The following rules apply to binary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:

  • Un operatore non di spostamento binario deve prendere due parametri, almeno uno dei quali deve avere il tipo T o T? e può restituire qualsiasi tipo.A binary non-shift operator must take two parameters, at least one of which must have type T or T?, and can return any type.
  • Un << operatore o binario >> deve assumere due parametri, il primo dei quali deve avere T il tipo o T? e il secondo di cui deve avere il tipo int o e int? può restituire qualsiasi tipo.A binary << or >> operator must take two parameters, the first of which must have type T or T? and the second of which must have type int or int?, and can return any type.

La firma di un operatore binario è costituita dal token dell'operatore ( + ,, - * , / , % , & , | , ^ , << , >> , == , != , > , < , >= o <= ) e dai tipi dei due parametri formali.The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. Il tipo restituito e i nomi dei parametri formali non fanno parte della firma di un operatore binario.The return type and the names of the formal parameters are not part of a binary operator's signature.

Per alcuni operatori binari è richiesta la dichiarazione di coppie.Certain binary operators require pair-wise declaration. Per ogni dichiarazione di uno degli operatori di una coppia, è necessario che sia presente una dichiarazione corrispondente dell'altro operatore della coppia.For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. Due dichiarazioni di operatore corrispondono quando hanno lo stesso tipo restituito e lo stesso tipo per ogni parametro.Two operator declarations match when they have the same return type and the same type for each parameter. Per gli operatori seguenti è necessaria la dichiarazione Pair-Wise: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 <=

Operatori di conversioneConversion operators

Una dichiarazione dell'operatore di conversione introduce una conversione definita dall'utente (conversioni definite dall'utente) che aumenta le conversioni implicite ed esplicite predefinite.A conversion operator declaration introduces a user-defined conversion (User-defined conversions) which augments the pre-defined implicit and explicit conversions.

Una dichiarazione dell'operatore di conversione che include la implicit parola chiave introduce una conversione implicita definita dall'utente.A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Le conversioni implicite possono verificarsi in varie situazioni, ad esempio chiamate di membri di funzioni, espressioni cast e assegnazioni.Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. Questa operazione viene descritta ulteriormente nelle conversioni implicite.This is described further in Implicit conversions.

Una dichiarazione dell'operatore di conversione che include la explicit parola chiave introduce una conversione esplicita definita dall'utente.A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Le conversioni esplicite possono verificarsi nelle espressioni cast e sono descritte ulteriormente nelle conversioni esplicite.Explicit conversions can occur in cast expressions, and are described further in Explicit conversions.

Un operatore di conversione converte da un tipo di origine, indicato dal tipo di parametro dell'operatore di conversione, a un tipo di destinazione, indicato dal tipo restituito dell'operatore di conversione.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.

Per un tipo di origine S e un tipo di destinazione specificati T , se S o T sono tipi nullable, consentire S0 e T0 fare riferimento ai relativi tipi sottostanti; in caso contrario, S0 e T0 sono uguali S rispettivamente a e T .For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. Una classe o uno struct è autorizzato a dichiarare una conversione da un tipo S di origine a un tipo di destinazione T solo se si verificano tutte le condizioni seguenti:A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

  • S0 e T0 sono tipi diversi.S0 and T0 are different types.
  • S0O T0 è il tipo di classe o struct in cui si verifica la dichiarazione di operatore.Either S0 or T0 is the class or struct type in which the operator declaration takes place.
  • S0Né né T0 è un INTERFACE_TYPE.Neither S0 nor T0 is an interface_type.
  • Escludendo le conversioni definite dall'utente, non esiste una conversione da S a T o da T a S .Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

Ai fini di queste regole, qualsiasi parametro di tipo associato a S o T viene considerato tipi univoci che non hanno una relazione di ereditarietà con altri tipi e tutti i vincoli sui parametri di tipo vengono ignorati.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.

Nell'esempioIn 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
}

le prime due dichiarazioni di operatore sono consentite perché, a scopo di indicizzatori,3, T e int e string rispettivamente sono considerati tipi univoci senza relazione.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. Tuttavia, il terzo operatore è un errore perché C<T> è la classe di base di D<T> .However, the third operator is an error because C<T> is the base class of D<T>.

Dalla seconda regola segue che un operatore di conversione deve eseguire la conversione da o verso il tipo di classe o struct in cui viene dichiarato l'operatore.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. È possibile, ad esempio, che una classe o un tipo struct C definisca una conversione da a C int e da int a C , ma non da int a bool .For example, it is possible for a class or struct type C to define a conversion from C to int and from int to C, but not from int to bool.

Non è possibile ridefinire direttamente una conversione predefinita.It is not possible to directly redefine a pre-defined conversion. Pertanto, gli operatori di conversione non sono autorizzati a eseguire la conversione da o a object perché le conversioni implicite ed esplicite esistono già tra object e tutti gli altri tipi.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. Analogamente, né l'origine né i tipi di destinazione di una conversione possono essere un tipo di base dell'altro, dal momento che una conversione sarebbe già esistente.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.

Tuttavia, è possibile dichiarare gli operatori sui tipi generici che, per determinati argomenti di tipo, specificano le conversioni già esistenti come conversioni predefinite.However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions. Nell'esempioIn the example

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

Quando object il tipo viene specificato come argomento di tipo per T , il secondo operatore dichiara una conversione già esistente (la conversione implicita e, di conseguenza, una conversione esplicita esiste da qualsiasi tipo al 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).

Nei casi in cui esiste una conversione predefinita tra due tipi, le conversioni definite dall'utente tra tali tipi vengono ignorate.In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. In particolare:Specifically:

  • Se esiste una conversione implicita predefinita (conversioni implicite) dal tipo S al tipo T , tutte le conversioni definite dall'utente (implicite o esplicite) da S a T vengono ignorate.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 una conversione esplicita predefinita (conversioni esplicite) esiste dal tipo S al tipo T , le conversioni esplicite definite dall'utente da S a T vengono ignorate.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. Tenere presente inoltre cheFurthermore:

Se T è un tipo di interfaccia, le conversioni implicite definite dall'utente da S a T vengono ignorate.If T is an interface type, user-defined implicit conversions from S to T are ignored.

In caso contrario, le conversioni implicite definite dall'utente da S a T vengono ancora prese in considerazione.Otherwise, user-defined implicit conversions from S to T are still considered.

Per tutti i tipi object , ma gli operatori dichiarati dal Convertible<T> tipo precedente non sono in conflitto con le conversioni predefinite.For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions. Ad esempio: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
}

Per il tipo, tuttavia, object le conversioni predefinite nascondono le conversioni definite dall'utente in tutti i casi, ma uno:However, for type object, pre-defined conversions hide the user-defined conversions in all cases but one:

void F(object o, Convertible<object> n) {
    o = n;                         // Pre-defined boxing conversion
    o = (object)n;                 // Pre-defined boxing conversion
    n = o;                         // User-defined implicit conversion
    n = (Convertible<object>)o;    // Pre-defined unboxing conversion
}

Nelle conversioni definite dall'utente non è consentito eseguire la conversione da o a INTERFACE_TYPE s.User-defined conversions are not allowed to convert from or to interface_type s. In particolare, questa restrizione garantisce che non si verifichino trasformazioni definite dall'utente durante la conversione in un INTERFACE_TYPE e che una conversione in una INTERFACE_TYPE abbia esito positivo solo se l'oggetto da convertire implementa effettivamente il INTERFACE_TYPE specificato.In particular, this restriction ensures that no user-defined transformations occur when converting to an interface_type, and that a conversion to an interface_type succeeds only if the object being converted actually implements the specified interface_type.

La firma di un operatore di conversione è costituita dal tipo di origine e dal tipo di destinazione.The signature of a conversion operator consists of the source type and the target type. Si noti che questa è l'unica forma di membro per cui il tipo restituito fa parte della firma. La implicit explicit classificazione o di un operatore di conversione non fa parte della firma dell'operatore.(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. Pertanto, una classe o uno struct non può dichiarare sia un implicit explicit operatore di conversione che con gli stessi tipi di origine e di destinazione.Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

In generale, le conversioni implicite definite dall'utente devono essere progettate in modo da non generare mai eccezioni e non perdere mai le informazioni.In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. Se una conversione definita dall'utente può generare eccezioni (ad esempio, poiché l'argomento di origine non è compreso nell'intervallo) o la perdita di informazioni, ad esempio la rimozione di bit più significativi, tale conversione deve essere definita come conversione esplicita.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.

Nell'esempioIn the example

using System;

public struct Digit
{
    byte value;

    public Digit(byte value) {
        if (value < 0 || value > 9) throw new ArgumentException();
        this.value = value;
    }

    public static implicit operator byte(Digit d) {
        return d.value;
    }

    public static explicit operator Digit(byte b) {
        return new Digit(b);
    }
}

la conversione da Digit a byte è implicita perché non genera mai eccezioni o perde informazioni, ma la conversione da byte a Digit è esplicita poiché Digit può rappresentare solo un subset dei valori possibili di un oggetto 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.

Costruttori di istanzeInstance constructors

Un *costruttore di istanza _ è un membro che implementa le azioni necessarie per inizializzare un'istanza di una classe.An *instance constructor _ is a member that implements the actions required to initialize an instance of a class. I costruttori di istanza vengono dichiarati utilizzando _constructor_declaration * s:Instance constructors are declared using _constructor_declaration*s:

constructor_declaration
    : attributes? constructor_modifier* constructor_declarator constructor_body
    ;

constructor_modifier
    : 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'extern'
    | constructor_modifier_unsafe
    ;

constructor_declarator
    : identifier '(' formal_parameter_list? ')' constructor_initializer?
    ;

constructor_initializer
    : ':' 'base' '(' argument_list? ')'
    | ':' 'this' '(' argument_list? ')'
    ;

constructor_body
    : block
    | ';'
    ;

Un constructor_declaration può includere un set di attributi (attributi), una combinazione valida dei quattro modificatori di accesso (modificatori di accesso) e un extern modificatore (metodi esterni).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. Una dichiarazione del costruttore non può includere lo stesso modificatore più volte.A constructor declaration is not permitted to include the same modifier multiple times.

L' identificatore di un constructor_declarator deve denominare la classe in cui è dichiarato il costruttore di istanza.The identifier of a constructor_declarator must name the class in which the instance constructor is declared. Se viene specificato un altro nome, si verificherà un errore in fase di compilazione.If any other name is specified, a compile-time error occurs.

Il formal_parameter_list facoltativo di un costruttore di istanza è soggetto alle stesse regole della formal_parameter_list di un metodo (Metodi).The optional formal_parameter_list of an instance constructor is subject to the same rules as the formal_parameter_list of a method (Methods). L'elenco di parametri formali definisce la firma (firme e overload) di un costruttore di istanza e regola il processo in base al quale la risoluzione dell'overload (inferenza del tipo) seleziona un particolare costruttore di istanza in una chiamata.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.

Ognuno dei tipi a cui viene fatto riferimento nel formal_parameter_list di un costruttore di istanza deve essere accessibile almeno quanto il costruttore stesso (vincoli di accessibilità).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).

Il constructor_initializer facoltativo specifica un altro costruttore di istanza da richiamare prima di eseguire le istruzioni fornite nell' constructor_body di questo costruttore di istanza.The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. Questa operazione viene descritta ulteriormente negli inizializzatori del costruttore.This is described further in Constructor initializers.

Quando una dichiarazione di costruttore include un extern modificatore, il costruttore viene definito come *Costruttore esterno _.When a constructor declaration includes an extern modifier, the constructor is said to be an *external constructor _. Poiché una dichiarazione del costruttore esterno non fornisce alcuna implementazione effettiva, il relativo _constructor_body * è costituito da un punto e virgola.Because an external constructor declaration provides no actual implementation, its _constructor_body* consists of a semicolon. Per tutti gli altri costruttori, il constructor_body è costituito da un blocco che specifica le istruzioni per inizializzare una nuova istanza della classe.For all other constructors, the constructor_body consists of a block which specifies the statements to initialize a new instance of the class. Corrisponde esattamente al blocco di un metodo di istanza con un void tipo restituito (corpo del metodo).This corresponds exactly to the block of an instance method with a void return type (Method body).

I costruttori di istanza non vengono ereditati.Instance constructors are not inherited. Pertanto, una classe non dispone di costruttori di istanza diversi da quelli effettivamente dichiarati nella classe.Thus, a class has no instance constructors other than those actually declared in the class. Se una classe non contiene dichiarazioni di costruttori di istanza, viene automaticamente fornito un costruttore di istanza predefinito (costruttori predefiniti).If a class contains no instance constructor declarations, a default instance constructor is automatically provided (Default constructors).

I costruttori di istanza vengono richiamati da object_creation_expression s (espressioni di creazione di oggetti) e tramite constructor_initializer s.Instance constructors are invoked by object_creation_expression s (Object creation expressions) and through constructor_initializer s.

Inizializzatori del costruttoreConstructor initializers

Tutti i costruttori di istanza (ad eccezione di quelli per la classe object ) includono implicitamente una chiamata a un altro costruttore di istanza immediatamente prima del constructor_body.All instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. Il costruttore da richiamare in modo implicito è determinato dall' constructor_initializer:The constructor to implicitly invoke is determined by the constructor_initializer:

  • Un inizializzatore del costruttore di istanza del form base(argument_list) o base() causa la richiamata di un costruttore di istanza dalla classe base diretta.An instance constructor initializer of the form base(argument_list) or base() causes an instance constructor from the direct base class to be invoked. Questo costruttore viene selezionato utilizzando argument_list se presente e le regole di risoluzione dell'overload della risoluzione dell'overload.That constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. Il set di costruttori di istanza candidati è costituito da tutti i costruttori di istanza accessibili contenuti nella classe di base diretta o dal costruttore predefinito (costruttori predefiniti), se non sono stati dichiarati costruttori di istanza nella classe di base diretta.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 il set è vuoto o se non è possibile identificare un singolo costruttore di istanze migliore, si verificherà un errore in fase di compilazione.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs.
  • Un inizializzatore del costruttore di istanza del form this(argument-list) o this() causa la richiamata di un costruttore di istanza dalla classe stessa.An instance constructor initializer of the form this(argument-list) or this() causes an instance constructor from the class itself to be invoked. Il costruttore viene selezionato utilizzando argument_list se presente e le regole di risoluzione dell'overload della risoluzione dell'overload.The constructor is selected using argument_list if present and the overload resolution rules of Overload resolution. Il set di costruttori di istanza candidati è costituito da tutti i costruttori di istanza accessibili dichiarati nella classe stessa.The set of candidate instance constructors consists of all accessible instance constructors declared in the class itself. Se il set è vuoto o se non è possibile identificare un singolo costruttore di istanze migliore, si verificherà un errore in fase di compilazione.If this set is empty, or if a single best instance constructor cannot be identified, a compile-time error occurs. Se una dichiarazione di un costruttore di istanza include un inizializzatore del costruttore che richiama il costruttore stesso, si verifica un errore in fase di compilazione.If an instance constructor declaration includes a constructor initializer that invokes the constructor itself, a compile-time error occurs.

Se un costruttore di istanza non dispone di un inizializzatore di costruttore, base() viene fornito implicitamente un inizializzatore del costruttore del modulo.If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. Quindi, una dichiarazione del costruttore di istanza del formThus, an instance constructor declaration of the form

C(...) {...}

è esattamente equivalente ais exactly equivalent to

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

L'ambito dei parametri forniti dall' formal_parameter_list di una dichiarazione del costruttore di istanza include l'inizializzatore del costruttore della dichiarazione.The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. Un inizializzatore di costruttore è quindi autorizzato ad accedere ai parametri del costruttore.Thus, a constructor initializer is permitted to access the parameters of the constructor. Ad esempio:For example:

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

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

Un inizializzatore di costruttore di istanza non può accedere all'istanza da creare.An instance constructor initializer cannot access the instance being created. Di conseguenza, si tratta di un errore in fase di compilazione a cui fare riferimento this in un'espressione di argomento dell'inizializzatore del costruttore, in quanto si tratta di un errore in fase di compilazione per un'espressione di argomento che fa riferimento a qualsiasi membro di istanza tramite un simple_name.Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple_name.

Inizializzatori di variabile di istanzaInstance variable initializers

Quando un costruttore di istanza non dispone di un inizializzatore di costruttore o un inizializzatore del costruttore del form base(...) , il costruttore esegue in modo implicito le inizializzazioni specificate dall' variable_initializer s dei campi di istanza dichiarati nella relativa 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_initializer s of the instance fields declared in its class. Corrisponde a una sequenza di assegnazioni che vengono eseguite immediatamente alla voce al costruttore e prima della chiamata implicita del costruttore della classe base diretta.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. Gli inizializzatori di variabile vengono eseguiti nell'ordine testuale in cui vengono visualizzati nella dichiarazione della classe.The variable initializers are executed in the textual order in which they appear in the class declaration.

Esecuzione del costruttoreConstructor execution

Gli inizializzatori di variabile vengono trasformati in istruzioni di assegnazione e queste istruzioni di assegnazione vengono eseguite prima della chiamata del costruttore di istanze della classe di base.Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. Questo ordinamento garantisce che tutti i campi di istanza vengano inizializzati dai rispettivi inizializzatori di variabile prima che vengano eseguite istruzioni con accesso a tale istanza.This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.

Dato l'esempioGiven 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() viene utilizzato per creare un'istanza di B , viene prodotto l'output seguente:when new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

Il valore di x è 1 perché l'inizializzatore di variabile viene eseguito prima che venga richiamato il costruttore di istanza della classe base.The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. Tuttavia, il valore di y è 0 (il valore predefinito di un int ) perché l'assegnazione a y non viene eseguita finché il costruttore della classe base non restituisce.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.

È utile considerare gli inizializzatori di variabile di istanza e gli inizializzatori di costruttore come istruzioni inserite automaticamente prima del constructor_body.It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor_body. L'esempio:The example

using System;
using System.Collections;

class A
{
    int x = 1, y = -1, count;

    public A() {
        count = 0;
    }

    public A(int n) {
        count = n;
    }
}

class B: A
{
    double sqrt2 = Math.Sqrt(2.0);
    ArrayList items = new ArrayList(100);
    int max;

    public B(): this(100) {
        items.Add("default");
    }

    public B(int n): base(n - 1) {
        max = n;
    }
}

contiene diversi inizializzatori di variabili. contiene anche inizializzatori di costruttori di entrambi i form ( base e this ).contains several variable initializers; it also contains constructor initializers of both forms (base and this). L'esempio corrisponde al codice riportato di seguito, in cui ogni commento indica un'istruzione inserita automaticamente (la sintassi utilizzata per le chiamate al costruttore inserite automaticamente non è valida, ma serve semplicemente per illustrare il meccanismo).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;
    }
}

Costruttori predefinitiDefault constructors

Se una classe non contiene dichiarazioni di costruttori di istanza, viene automaticamente fornito un costruttore di istanza predefinito.If a class contains no instance constructor declarations, a default instance constructor is automatically provided. Il costruttore predefinito richiama semplicemente il costruttore senza parametri della classe base diretta.That default constructor simply invokes the parameterless constructor of the direct base class. Se la classe è astratta, l'accessibilità dichiarata per il costruttore predefinito è protetta.If the class is abstract then the declared accessibility for the default constructor is protected. In caso contrario, l'accessibilità dichiarata per il costruttore predefinito è public.Otherwise, the declared accessibility for the default constructor is public. Pertanto, il costruttore predefinito è sempre nel formatoThus, the default constructor is always of the form

protected C(): base() {}

oppureor

public C(): base() {}

dove C è il nome della classe.where C is the name of the class. Se la risoluzione dell'overload non è in grado di determinare un candidato univoco per l'inizializzatore del costruttore della classe base, si verificherà un errore in fase di compilazione.If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs.

Nell'esempioIn the example

class Message
{
    object sender;
    string text;
}

viene fornito un costruttore predefinito perché la classe non contiene dichiarazioni di costruttori di istanza.a default constructor is provided because the class contains no instance constructor declarations. Di conseguenza, l'esempio è esattamente equivalente aThus, the example is precisely equivalent to

class Message
{
    object sender;
    string text;

    public Message(): base() {}
}

Costruttori privatiPrivate constructors

Quando una classe T dichiara solo costruttori di istanza privati, non è possibile che le classi esterne al testo di programma di T derivano da T o per creare direttamente istanze di 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. Se pertanto una classe contiene solo membri statici e non è destinata alla creazione di un'istanza, l'aggiunta di un costruttore di istanza privata vuota impedirà la creazione di istanze.Thus, if a class contains only static members and isn't intended to be instantiated, adding an empty private instance constructor will prevent instantiation. Ad esempio:For example:

public class Trig
{
    private Trig() {}        // Prevent instantiation

    public const double PI = 3.14159265358979323846;

    public static double Sin(double x) {...}
    public static double Cos(double x) {...}
    public static double Tan(double x) {...}
}

La Trig classe raggruppa i metodi e le costanti correlati, ma non deve essere creata un'istanza.The Trig class groups related methods and constants, but is not intended to be instantiated. Dichiara pertanto un singolo costruttore di istanza privata vuota.Therefore it declares a single empty private instance constructor. Per evitare la generazione automatica di un costruttore predefinito, è necessario dichiarare almeno un costruttore di istanze.At least one instance constructor must be declared to suppress the automatic generation of a default constructor.

Parametri facoltativi del costruttore di istanzaOptional instance constructor parameters

Il this(...) form dell'inizializzatore del costruttore viene comunemente usato in combinazione con l'overload per implementare parametri del costruttore di istanza facoltativi.The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. Nell'esempioIn 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
    }
}

i primi due costruttori di istanza forniscono semplicemente i valori predefiniti per gli argomenti mancanti.the first two instance constructors merely provide the default values for the missing arguments. Entrambi usano un this(...) inizializzatore di costruttore per richiamare il terzo costruttore di istanza, che in realtà esegue l'inizializzazione della nuova istanza.Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. L'effetto è che i parametri facoltativi del costruttore: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");

Costruttori staticiStatic constructors

Un costruttore statico* è un membro che implementa le azioni necessarie per inizializzare un tipo di classe closed.A *static constructor _ is a member that implements the actions required to initialize a closed class type. I costruttori statici vengono dichiarati usando _static_constructor_declaration * s:Static constructors are declared using _static_constructor_declaration*s:

static_constructor_declaration
    : attributes? static_constructor_modifiers identifier '(' ')' static_constructor_body
    ;

static_constructor_modifiers
    : 'extern'? 'static'
    | 'static' 'extern'?
    | static_constructor_modifiers_unsafe
    ;

static_constructor_body
    : block
    | ';'
    ;

Un static_constructor_declaration può includere un set di attributi (attributi) e un extern modificatore (metodi esterni).A static_constructor_declaration may include a set of attributes (Attributes) and an extern modifier (External methods).

L' identificatore di un static_constructor_declaration deve denominare la classe in cui è dichiarato il costruttore statico.The identifier of a static_constructor_declaration must name the class in which the static constructor is declared. Se viene specificato un altro nome, si verificherà un errore in fase di compilazione.If any other name is specified, a compile-time error occurs.

Quando una dichiarazione di costruttore statico include un extern modificatore, il costruttore statico viene definito un *costruttore statico esterno _.When a static constructor declaration includes an extern modifier, the static constructor is said to be an *external static constructor _. Poiché la dichiarazione di un costruttore statico esterno non fornisce alcuna implementazione effettiva, il relativo _static_constructor_body * è costituito da un punto e virgola.Because an external static constructor declaration provides no actual implementation, its _static_constructor_body* consists of a semicolon. Per tutte le altre dichiarazioni di costruttori statici, il static_constructor_body è costituito da un blocco che specifica le istruzioni da eseguire per inizializzare la 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. Corrisponde esattamente al method_body di un metodo statico con un void tipo restituito (corpo del Metodo).This corresponds exactly to the method_body of a static method with a void return type (Method body).

I costruttori statici non vengono ereditati e non possono essere chiamati direttamente.Static constructors are not inherited, and cannot be called directly.

Il costruttore statico per un tipo di classe Closed viene eseguito al massimo una volta in un determinato dominio applicazione.The static constructor for a closed class type executes at most once in a given application domain. L'esecuzione di un costruttore statico viene attivata dal primo degli eventi seguenti che si verificano all'interno di un dominio dell'applicazione:The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • Viene creata un'istanza del tipo di classe.An instance of the class type is created.
  • Viene fatto riferimento a qualsiasi membro statico del tipo di classe.Any of the static members of the class type are referenced.

Se una classe contiene il Main Metodo (avvio dell'applicazione) in cui inizia l'esecuzione, il costruttore statico per tale classe viene eseguito prima che Main venga chiamato il metodo.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.

Per inizializzare un nuovo tipo di classe chiusa, viene creato innanzitutto un nuovo set di campi statici (campi statici e di istanza) per quel particolare tipo chiuso.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. Ogni campo statico viene inizializzato sul valore predefinito (valori predefiniti).Each of the static fields is initialized to its default value (Default values). Successivamente, vengono eseguiti gli inizializzatori di campo statici (inizializzazione del campo statico) per i campi statici.Next, the static field initializers (Static field initialization) are executed for those static fields. Infine, viene eseguito il costruttore statico.Finally, the static constructor is executed.

L'esempio:The 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 produrre l'output:must produce the output:

Init A
A.F
Init B
B.F

Poiché l'esecuzione del A costruttore statico di viene attivata dalla chiamata a A.F e l'esecuzione del B costruttore statico di viene attivata dalla chiamata a B.F .because the execution of A's static constructor is triggered by the call to A.F, and the execution of B's static constructor is triggered by the call to B.F.

È possibile creare dipendenze circolari che consentono di osservare i campi statici con inizializzatori di variabili nello stato del valore predefinito.It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

L'esempio:The 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);
    }
}

produce l'outputproduces the output

X = 1, Y = 2

Per eseguire il Main metodo, il sistema esegue prima l'inizializzatore per B.Y , prima del B costruttore statico della classe.To execute the Main method, the system first runs the initializer for B.Y, prior to class B's static constructor. Yl'inizializzatore di ' s causa A l'esecuzione del costruttore statico perché A.X viene fatto riferimento al valore di.Y's initializer causes A's static constructor to be run because the value of A.X is referenced. Il costruttore statico di  A a sua volta continua a calcolare il valore di  X e, in questo modo, recupera il valore predefinito  Y , che è 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 viene quindi inizializzato su 1.A.X is thus initialized to 1. Il processo di esecuzione degli A inizializzatori di campo statici e del costruttore statico viene quindi completato, restituendo al calcolo del valore iniziale di  Y , il risultato del quale diventa 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.

Poiché il costruttore statico viene eseguito una sola volta per ogni tipo di classe costruito chiuso, è consigliabile applicare controlli di runtime sul parametro di tipo che non possono essere controllati in fase di compilazione tramite vincoli (vincoli delparametro di 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). Ad esempio, il tipo seguente usa un costruttore statico per applicare che l'argomento di tipo è un'enumerazione: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");
        }
    }
}

DistruttoriDestructors

Un *distruttore _ è un membro che implementa le azioni necessarie per distruggere un'istanza di una classe.A *destructor _ is a member that implements the actions required to destruct an instance of a class. Un distruttore viene dichiarato utilizzando un _destructor_declaration *:A destructor is declared using a _destructor_declaration*:

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

destructor_body
    : block
    | ';'
    ;

Un destructor_declaration può includere un set di attributi (attributi).A destructor_declaration may include a set of attributes (Attributes).

L' identificatore di un destructor_declaration deve denominare la classe in cui è dichiarato il distruttore.The identifier of a destructor_declaration must name the class in which the destructor is declared. Se viene specificato un altro nome, si verificherà un errore in fase di compilazione.If any other name is specified, a compile-time error occurs.

Quando una dichiarazione di distruttore include un extern modificatore, il distruttore viene definito come un distruttore esterno.When a destructor declaration includes an extern modifier, the destructor is said to be an *external destructor _. Poiché una dichiarazione di distruttore esterna non fornisce alcuna implementazione effettiva, il relativo _destructor_body * è costituito da un punto e virgola.Because an external destructor declaration provides no actual implementation, its _destructor_body* consists of a semicolon. Per tutti gli altri distruttori, il destructor_body è costituito da un blocco che specifica le istruzioni da eseguire per distruggere un'istanza della 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. Un destructor_body corrisponde esattamente al method_body di un metodo di istanza con un void tipo restituito (corpo del metodo).A destructor_body corresponds exactly to the method_body of an instance method with a void return type (Method body).

I distruttori non vengono ereditati.Destructors are not inherited. Pertanto, una classe non dispone di distruttori diversi da quello che può essere dichiarato in tale classe.Thus, a class has no destructors other than the one which may be declared in that class.

Poiché è necessario che un distruttore non abbia parametri, non può essere sottoposto a overload, quindi una classe può avere al massimo un distruttore.Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.

I distruttori vengono richiamati automaticamente e non possono essere richiamati in modo esplicito.Destructors are invoked automatically, and cannot be invoked explicitly. Un'istanza diventa idonea per la distruzione quando non è più possibile utilizzare tale istanza per il codice.An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. L'esecuzione del distruttore per l'istanza può verificarsi in qualsiasi momento dopo che l'istanza diventa idonea per la distruzione.Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. Quando un'istanza viene distrutta, i distruttori nella catena di ereditarietà di tale istanza vengono chiamati, in ordine, dal più derivato al meno derivato.When an instance is destructed, the destructors in that instance's inheritance chain are called, in order, from most derived to least derived. Un distruttore può essere eseguito su qualsiasi thread.A destructor may be executed on any thread. Per ulteriori informazioni sulle regole che determinano quando e come viene eseguito un distruttore, vedere gestione automatica della memoria.For further discussion of the rules that govern when and how a destructor is executed, see Automatic memory management.

Output dell'esempioThe 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

Poiché i distruttori in una catena di ereditarietà vengono chiamati in ordine, dal più derivato al meno derivato.since destructors in an inheritance chain are called in order, from most derived to least derived.

I distruttori vengono implementati eseguendo l'override del metodo virtuale Finalize in System.Object .Destructors are implemented by overriding the virtual method Finalize on System.Object. I programmi C# non sono autorizzati a eseguire l'override di questo metodo o di chiamarlo direttamente o di eseguirne l'override.C# programs are not permitted to override this method or call it (or overrides of it) directly. Ad esempio, il programmaFor instance, the program

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

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

contiene due errori.contains two errors.

Il compilatore si comporta come se questo metodo e le sue sostituzioni non esistessero affatto.The compiler behaves as if this method, and overrides of it, do not exist at all. Quindi, questo programma:Thus, this program:

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

è valido e il metodo Mostra il metodo Hides System.Object Finalize .is valid, and the method shown hides System.Object's Finalize method.

Per una descrizione del comportamento quando viene generata un'eccezione da un distruttore, vedere come vengono gestite le eccezioni.For a discussion of the behavior when an exception is thrown from a destructor, see How exceptions are handled.

IteratorsIterators

Un membro di funzione (membri di funzione) implementato utilizzando un blocco iteratore (blocchi) viene definito iteratore.A function member (Function members) implemented using an iterator block (Blocks) is called an iterator.

Un blocco iteratore può essere usato come corpo di un membro di funzione purché il tipo restituito del membro di funzione corrispondente sia una delle interfacce di enumeratore (interfacce di enumeratore) o una delle interfacce enumerabili(interfacce enumerabili).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). Può verificarsi come method_body, operator_body o accessor_body, mentre gli eventi, i costruttori di istanza, i costruttori statici e i distruttori non possono essere implementati come iteratori.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 un membro di funzione viene implementato usando un blocco iteratore, si tratta di un errore in fase di compilazione per l'elenco di parametri formali del membro della funzione per specificare i ref out parametri o.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.

Interfacce enumeratoreEnumerator interfaces

Le interfacce dell'enumeratore sono l'interfaccia non generica System.Collections.IEnumerator e tutte le creazioni di istanze dell'interfaccia generica System.Collections.Generic.IEnumerator<T> .The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. Per motivi di brevità, in questo capitolo viene fatto riferimento a queste interfacce IEnumerator rispettivamente come e IEnumerator<T> .For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively.

Interfacce enumerabiliEnumerable interfaces

Le interfacce enumerabili sono l'interfaccia non generica System.Collections.IEnumerable e tutte le creazioni di istanze dell'interfaccia generica System.Collections.Generic.IEnumerable<T> .The enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. Per motivi di brevità, in questo capitolo viene fatto riferimento a queste interfacce IEnumerable rispettivamente come e IEnumerable<T> .For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively.

Tipo yieldYield type

Un iteratore produce una sequenza di valori, tutti dello stesso tipo.An iterator produces a sequence of values, all of the same type. Questo tipo è denominato tipo yield dell'iteratore.This type is called the yield type of the iterator.

  • Tipo di Yield di un iteratore che restituisce IEnumerator o IEnumerable è object .The yield type of an iterator that returns IEnumerator or IEnumerable is object.
  • Tipo di Yield di un iteratore che restituisce IEnumerator<T> o IEnumerable<T> è T .The yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T.

Oggetti enumeratoreEnumerator objects

Quando un membro di funzione che restituisce un tipo di interfaccia di enumerazione viene implementato utilizzando un blocco iteratore, la chiamata al membro della funzione non esegue immediatamente il codice nel blocco iteratore.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. Viene invece creato e restituito un oggetto enumeratore .Instead, an enumerator object is created and returned. Questo oggetto incapsula il codice specificato nel blocco iteratore e l'esecuzione del codice nel blocco iteratore si verifica quando viene richiamato il metodo dell'oggetto enumeratore MoveNext .This object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object's MoveNext method is invoked. Un oggetto enumeratore presenta le caratteristiche seguenti:An enumerator object has the following characteristics:

  • Implementa IEnumerator e IEnumerator<T> , dove T è il tipo di risultato dell'iteratore.It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator.
  • Implementa System.IDisposable.It implements System.IDisposable.
  • Viene inizializzata con una copia dei valori degli argomenti (se presenti) e del valore dell'istanza passati al membro della funzione.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
  • Ha quattro stati potenziali, before _, _Running_, _suspended_ e _after*, e inizialmente è nello stato _ *before**.It has four potential states, before _, _running*, suspended, and after, and is initially in the _ before* state.

Un oggetto enumeratore è in genere un'istanza di una classe di enumeratori generata dal compilatore che incapsula il codice nel blocco iteratore e implementa le interfacce dell'enumeratore, ma sono possibili altri metodi di implementazione.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 una classe di enumeratori viene generata dal compilatore, tale classe sarà annidata, direttamente o indirettamente, nella classe contenente il membro della funzione, avrà l'accessibilità privata e avrà un nome riservato per l'uso del compilatore (identificatori).If an enumerator class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (Identifiers).

Un oggetto enumeratore può implementare più interfacce rispetto a quelle specificate in precedenza.An enumerator object may implement more interfaces than those specified above.

Nelle sezioni seguenti viene descritto il comportamento esatto dei MoveNext Current membri, e Dispose delle IEnumerable IEnumerable<T> implementazioni dell'interfaccia e fornite da un oggetto enumeratore.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.

Si noti che gli oggetti enumeratore non supportano il IEnumerator.Reset metodo.Note that enumerator objects do not support the IEnumerator.Reset method. Quando si richiama questo metodo System.NotSupportedException , viene generata un'eccezione.Invoking this method causes a System.NotSupportedException to be thrown.

Metodo MoveNextThe MoveNext method

Il MoveNext metodo di un oggetto enumeratore incapsula il codice di un blocco iteratore.The MoveNext method of an enumerator object encapsulates the code of an iterator block. La chiamata del MoveNext metodo esegue il codice nel blocco iteratore e imposta la Current proprietà dell'oggetto enumeratore nel modo appropriato.Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. L'azione esatta eseguita da MoveNext dipende dallo stato dell'oggetto enumeratore quando MoveNext viene richiamato:The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:

  • Se lo stato dell'oggetto enumeratore è precedente, richiamare MoveNext :If the state of the enumerator object is before, invoking MoveNext:
    • Imposta lo stato su in esecuzione.Changes the state to running.
    • Inizializza i parametri (incluso this ) del blocco iteratore sui valori degli argomenti e sul valore dell'istanza salvati quando l'oggetto enumeratore è stato inizializzato.Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized.
    • Esegue il blocco iteratore dall'inizio fino a quando l'esecuzione non viene interrotta (come descritto di seguito).Executes the iterator block from the beginning until execution is interrupted (as described below).
  • Se lo stato dell'oggetto enumeratore è in esecuzione, il risultato della chiamata non MoveNext è specificato.If the state of the enumerator object is running, the result of invoking MoveNext is unspecified.
  • Se lo stato dell'oggetto enumeratore è sospeso, richiamando MoveNext :If the state of the enumerator object is suspended, invoking MoveNext:
    • Imposta lo stato su in esecuzione.Changes the state to running.
    • Ripristina i valori di tutte le variabili e i parametri locali (incluso) ai valori salvati durante l'ultima sospensione dell'esecuzione del blocco iteratore.Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. Si noti che il contenuto degli oggetti a cui fanno riferimento queste variabili potrebbe essere stato modificato rispetto alla precedente chiamata a MoveNext.Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext.
    • Riprende l'esecuzione del blocco iteratore immediatamente dopo l' yield return istruzione che ha causato la sospensione dell'esecuzione e continua fino a quando l'esecuzione non viene interrotta (come descritto di seguito).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 lo stato dell'oggetto enumeratore è dopo, la chiamata di MoveNext restituisce false .If the state of the enumerator object is after, invoking MoveNext returns false.

Quando MoveNext esegue il blocco iteratore, l'esecuzione può essere interrotta in quattro modi: da un' yield return istruzione, da un' yield break istruzione, con la fine del blocco iteratore e da un'eccezione generata e propagata all'esterno del blocco iteratore.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 yield return viene rilevata un'istruzione (istruzione yield):When a yield return statement is encountered (The yield statement):
    • L'espressione specificata nell'istruzione viene valutata, convertita in modo implicito nel tipo yield e assegnata alla Current proprietà dell'oggetto enumeratore.The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
    • L'esecuzione del corpo dell'iteratore è sospesa.Execution of the iterator body is suspended. I valori di tutte le variabili e i parametri locali (incluso this ) vengono salvati, così come il percorso di questa yield return istruzione.The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. Se l' yield return istruzione si trova all'interno di uno o più try blocchi, i finally blocchi associati non vengono eseguiti in questo momento.If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
    • Lo stato dell'oggetto enumeratore viene modificato in sospeso.The state of the enumerator object is changed to suspended.
    • Il MoveNext metodo restituisce true al chiamante, a indicare che l'iterazione è stata avanzata correttamente in base al valore successivo.The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value.
  • Quando yield break viene rilevata un'istruzione (istruzione yield):When a yield break statement is encountered (The yield statement):
    • Se l' yield break istruzione si trova all'interno di uno o più try blocchi, finally vengono eseguiti i blocchi associati.If the yield break statement is within one or more try blocks, the associated finally blocks are executed.
    • Lo stato dell'oggetto enumeratore viene modificato in dopo.The state of the enumerator object is changed to after.
    • Il MoveNext metodo restituisce false al chiamante, a indicare che l'iterazione è completa.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Quando viene rilevata la fine del corpo dell'iteratore:When the end of the iterator body is encountered:
    • Lo stato dell'oggetto enumeratore viene modificato in dopo.The state of the enumerator object is changed to after.
    • Il MoveNext metodo restituisce false al chiamante, a indicare che l'iterazione è completa.The MoveNext method returns false to its caller, indicating that the iteration is complete.
  • Quando viene generata un'eccezione e viene propagata all'esterno del blocco iteratore:When an exception is thrown and propagated out of the iterator block:
    • finallyI blocchi appropriati nel corpo dell'iteratore sono stati eseguiti dalla propagazione delle eccezioni.Appropriate finally blocks in the iterator body will have been executed by the exception propagation.
    • Lo stato dell'oggetto enumeratore viene modificato in dopo.The state of the enumerator object is changed to after.
    • La propagazione delle eccezioni continua al chiamante del MoveNext metodo.The exception propagation continues to the caller of the MoveNext method.

Proprietà corrente.The Current property

La proprietà di un oggetto enumeratore Current è interessata dalle yield return istruzioni nel blocco iteratore.An enumerator object's Current property is affected by yield return statements in the iterator block.

Quando un oggetto enumeratore si trova nello stato *suspended _, il valore di Current è il valore impostato dalla chiamata precedente a MoveNext .When an enumerator object is in the *suspended _ state, the value of Current is the value set by the previous call to MoveNext. Quando un oggetto enumeratore si trova negli Stati before, Running o _ *after**, il risultato dell'accesso a non Current è specificato.When an enumerator object is in the before, running, or _ after* states, the result of accessing Current is unspecified.

Per un iteratore con un tipo yield diverso da object , il risultato dell'accesso Current tramite l'implementazione dell'oggetto enumeratore IEnumerable corrisponde all'accesso Current tramite l'implementazione dell'oggetto enumeratore IEnumerator<T> ed eseguendo il cast del risultato a object .For an iterator with a yield type other than object, the result of accessing Current through the enumerator object's IEnumerable implementation corresponds to accessing Current through the enumerator object's IEnumerator<T> implementation and casting the result to object.

Metodo DisposeThe Dispose method

Il Dispose metodo viene usato per pulire l'iterazione portando l'oggetto enumeratore allo stato after .The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.

  • Se lo stato dell'oggetto enumeratore è *before _, quando si richiama lo Dispose stato viene modificato in _ dopo *.If the state of the enumerator object is before _, invoking Dispose changes the state to _after**.
  • Se lo stato dell'oggetto enumeratore è in esecuzione, il risultato della chiamata non Dispose è specificato.If the state of the enumerator object is running, the result of invoking Dispose is unspecified.
  • Se lo stato dell'oggetto enumeratore è sospeso, richiamando Dispose :If the state of the enumerator object is suspended, invoking Dispose:
    • Imposta lo stato su in esecuzione.Changes the state to running.
    • Esegue qualsiasi blocco finally come se l'ultima yield return istruzione eseguita fosse un' yield break istruzione.Executes any finally blocks as if the last executed yield return statement were a yield break statement. Se in questo modo viene generata un'eccezione e viene propagata all'esterno del corpo dell'iteratore, lo stato dell'oggetto enumeratore viene impostato su after e l'eccezione viene propagata al chiamante del Dispose metodo.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.
    • Imposta lo stato su after.Changes the state to after.
  • Se lo stato dell'oggetto enumeratore è after, la chiamata a Dispose non ha alcun effetto.If the state of the enumerator object is after, invoking Dispose has no affect.

Oggetti enumerabiliEnumerable objects

Quando un membro di funzione che restituisce un tipo di interfaccia enumerabile viene implementato utilizzando un blocco iteratore, la chiamata al membro della funzione non esegue immediatamente il codice nel blocco iteratore.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. Viene invece creato e restituito un oggetto enumerabile .Instead, an enumerable object is created and returned. Il metodo dell'oggetto enumerabile GetEnumerator restituisce un oggetto enumeratore che incapsula il codice specificato nel blocco iteratore e l'esecuzione del codice nel blocco iteratore si verifica quando viene richiamato il metodo dell'oggetto enumeratore MoveNext .The enumerable object's GetEnumerator method returns an enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object's MoveNext method is invoked. Un oggetto enumerabile presenta le caratteristiche seguenti:An enumerable object has the following characteristics:

  • Implementa IEnumerable e IEnumerable<T> , dove T è il tipo di risultato dell'iteratore.It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator.
  • Viene inizializzata con una copia dei valori degli argomenti (se presenti) e del valore dell'istanza passati al membro della funzione.It is initialized with a copy of the argument values (if any) and instance value passed to the function member.

Un oggetto enumerabile è in genere un'istanza di una classe enumerabile generata dal compilatore che incapsula il codice nel blocco iteratore e implementa le interfacce enumerabili, ma sono possibili altri metodi di implementazione.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 una classe enumerabile viene generata dal compilatore, tale classe sarà nidificata, direttamente o indirettamente, nella classe contenente il membro della funzione, avrà accessibilità privata e avrà un nome riservato per l'uso del compilatore (identificatori).If an enumerable class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (Identifiers).

Un oggetto enumerabile può implementare più interfacce rispetto a quelle specificate in precedenza.An enumerable object may implement more interfaces than those specified above. In particolare, un oggetto enumerabile può implementare anche IEnumerator e IEnumerator<T> , consentendo di fungere da enumeratore e enumeratore.In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. In tale tipo di implementazione, la prima volta che viene richiamato il metodo di un oggetto enumerabile GetEnumerator , viene restituito l'oggetto enumerabile stesso.In that type of implementation, the first time an enumerable object's GetEnumerator method is invoked, the enumerable object itself is returned. Le chiamate successive dell'oggetto enumerabile GetEnumerator , se presente, restituiscono una copia dell'oggetto enumerabile.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Pertanto, ogni enumeratore restituito ha il proprio stato e le modifiche apportate in un enumeratore non avranno effetto su un'altra.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another.

Metodo GetEnumeratorThe GetEnumerator method

Un oggetto enumerabile fornisce un'implementazione dei GetEnumerator metodi delle IEnumerable interfacce e IEnumerable<T> .An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. I due GetEnumerator Metodi condividono un'implementazione comune che acquisisce e restituisce un oggetto enumeratore disponibile.The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. L'oggetto enumeratore viene inizializzato con i valori dell'argomento e il valore dell'istanza salvati quando l'oggetto enumerabile è stato inizializzato. in caso contrario, l'oggetto enumeratore funziona come descritto in oggetti enumeratore.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.

Esempio di implementazioneImplementation example

In questa sezione viene descritta una possibile implementazione di iteratori in termini di costrutti C# standard.This section describes a possible implementation of iterators in terms of standard C# constructs. L'implementazione descritta di seguito si basa sugli stessi principi usati dal compilatore Microsoft C#, ma non è un'implementazione obbligatoria o l'unico possibile.The implementation described here is based on the same principles used by the Microsoft C# compiler, but it is by no means a mandated implementation or the only one possible.

La Stack<T> classe seguente implementa il GetEnumerator Metodo usando un iteratore.The following Stack<T> class implements its GetEnumerator method using an iterator. L'iteratore enumera gli elementi dello stack nell'ordine dall'alto verso il basso.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];
    }
}

Il GetEnumerator metodo può essere convertito in un'istanza di una classe di enumeratori generata dal compilatore che incapsula il codice nel blocco iteratore, come illustrato di seguito.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();
        }
    }
}

Nella conversione precedente, il codice nel blocco iteratore viene trasformato in una macchina a Stati e inserito nel MoveNext metodo della classe Enumerator.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. Inoltre, la variabile locale i viene trasformata in un campo nell'oggetto enumeratore, in modo che possa continuare a esistere tra le chiamate di MoveNext .Furthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext.

Nell'esempio seguente viene stampata una semplice tabella di moltiplicazione dei numeri interi da 1 a 10.The following example prints a simple multiplication table of the integers 1 through 10. Il FromTo metodo nell'esempio restituisce un oggetto enumerabile ed è implementato utilizzando un iteratore.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();
        }
    }
}

Il FromTo metodo può essere convertito in un'istanza di una classe Enumerable generata dal compilatore che incapsula il codice nel blocco iteratore, come illustrato di seguito.The FromTo method can be translated into an instantiation of a compiler-generated enumerable class that encapsulates the code in the iterator block, as shown in the following.

using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;

class Test
{
    ...

    static IEnumerable<int> FromTo(int from, int to) {
        return new __Enumerable1(from, to);
    }

    class __Enumerable1:
        IEnumerable<int>, IEnumerable,
        IEnumerator<int>, IEnumerator
    {
        int __state;
        int __current;
        int __from;
        int from;
        int to;
        int i;

        public __Enumerable1(int __from, int to) {
            this.__from = __from;
            this.to = to;
        }

        public IEnumerator<int> GetEnumerator() {
            __Enumerable1 result = this;
            if (Interlocked.CompareExchange(ref __state, 1, 0) != 0) {
                result = new __Enumerable1(__from, to);
                result.__state = 1;
            }
            result.from = result.__from;
            return result;
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return (IEnumerator)GetEnumerator();
        }

        public int Current {
            get { return __current; }
        }

        object IEnumerator.Current {
            get { return __current; }
        }

        public bool MoveNext() {
            switch (__state) {
            case 1:
                if (from > to) goto case 2;
                __current = from++;
                __state = 1;
                return true;
            case 2:
                __state = 2;
                return false;
            default:
                throw new InvalidOperationException();
            }
        }

        public void Dispose() {
            __state = 2;
        }

        void IEnumerator.Reset() {
            throw new NotSupportedException();
        }
    }
}

La classe Enumerable implementa sia le interfacce enumerabili che le interfacce dell'enumeratore, consentendo di fungere da enumeratore e enumeratore.The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. La prima volta che il GetEnumerator metodo viene richiamato, viene restituito l'oggetto enumerabile stesso.The first time the GetEnumerator method is invoked, the enumerable object itself is returned. Le chiamate successive dell'oggetto enumerabile GetEnumerator , se presente, restituiscono una copia dell'oggetto enumerabile.Subsequent invocations of the enumerable object's GetEnumerator, if any, return a copy of the enumerable object. Pertanto, ogni enumeratore restituito ha il proprio stato e le modifiche apportate in un enumeratore non avranno effetto su un'altra.Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. Il Interlocked.CompareExchange metodo viene usato per garantire l'operazione thread-safe.The Interlocked.CompareExchange method is used to ensure thread-safe operation.

I from to parametri e vengono convertiti in campi nella classe Enumerable.The from and to parameters are turned into fields in the enumerable class. Poiché from viene modificato nel blocco iteratore, __from viene introdotto un campo aggiuntivo per conservare il valore iniziale assegnato a from in ogni enumeratore.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.

Il MoveNext metodo genera un'eccezione InvalidOperationException se viene chiamato quando __state è 0 .The MoveNext method throws an InvalidOperationException if it is called when __state is 0. In questo modo si impedisce l'utilizzo dell'oggetto enumerabile come oggetto enumeratore senza prima chiamare GetEnumerator .This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator.

Nell'esempio seguente viene illustrata una semplice classe Tree.The following example shows a simple tree class. La Tree<T> classe implementa il GetEnumerator Metodo usando un iteratore.The Tree<T> class implements its GetEnumerator method using an iterator. L'iteratore enumera gli elementi della struttura ad albero in ordine infisso.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();
    }
}

Il GetEnumerator metodo può essere convertito in un'istanza di una classe di enumeratori generata dal compilatore che incapsula il codice nel blocco iteratore, come illustrato di seguito.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();
        }
    }
}

I temporaries generati dal compilatore usati nelle foreach istruzioni vengono rimossi nei __left __right campi e dell'oggetto enumeratore.The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. Il __state campo dell'oggetto enumeratore viene aggiornato con attenzione, in modo che il metodo corretto venga Dispose() chiamato correttamente se viene generata un'eccezione.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. Si noti che non è possibile scrivere il codice tradotto con foreach istruzioni semplici.Note that it is not possible to write the translated code with simple foreach statements.

Funzioni asincroneAsync functions

Un metodo (Methods) o una funzione anonima (espressioni di funzione anonima) con il async modificatore viene chiamato *funzione asincrona _.A method (Methods) or anonymous function (Anonymous function expressions) with the async modifier is called an *async function _. In generale, il termine _ Async* viene usato per descrivere qualsiasi tipo di funzione con il async modificatore.In general, the term _ async* is used to describe any kind of function that has the async modifier.

Si tratta di un errore in fase di compilazione per l'elenco di parametri formali di una funzione asincrona per specificare i ref out parametri o.It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters.

Il return_type di un metodo asincrono deve essere void o un tipo di attività.The return_type of an async method must be either void or a task type. I tipi di attività sono System.Threading.Tasks.Task e i tipi costruiti da System.Threading.Tasks.Task<T> .The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. Per motivi di brevità, in questo capitolo viene fatto riferimento a questi tipi Task rispettivamente come e Task<T> .For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. Un metodo asincrono che restituisce un tipo di attività viene detto restituzione di attività.An async method returning a task type is said to be task-returning.

La definizione esatta dei tipi di attività è definita dall'implementazione, ma dal punto di vista del linguaggio un tipo di attività è in uno degli Stati incompleti, Succeeded o Faulted.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. Un'attività con errori registra un'eccezione pertinente.A faulted task records a pertinent exception. Un oggetto è riuscito a Task<T> registrare un risultato di tipo T .A succeeded Task<T> records a result of type T. I tipi di attività sono awaitable e possono quindi essere gli operandi di espressioni await (espressioni await).Task types are awaitable, and can therefore be the operands of await expressions (Await expressions).

Una chiamata di funzione asincrona ha la possibilità di sospendere la valutazione per mezzo delle espressioni await (await) nel corpo.An async function invocation has the ability to suspend evaluation by means of await expressions (Await expressions) in its body. La valutazione può essere ripresa in un secondo momento nel punto in cui viene sospesa l'espressione await per mezzo di un delegato di ripresa* _.Evaluation may later be resumed at the point of the suspending await expression by means of a *resumption delegate _. Il delegato di ripresa è di tipo System.Action e, quando viene richiamato, la valutazione della chiamata della funzione asincrona riprenderà dall'espressione await da dove era stata interrotta.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. Il chiamante corrente* di una chiamata di funzione asincrona è il chiamante originale se la chiamata di funzione non è mai stata sospesa o il chiamante più recente del delegato di ripresa in caso contrario.The _ current caller* of an async function invocation is the original caller if the function invocation has never been suspended, or the most recent caller of the resumption delegate otherwise.

Valutazione di una funzione asincrona che restituisce un'attivitàEvaluation of a task-returning async function

La chiamata di una funzione asincrona che restituisce un'attività causa la generazione di un'istanza del tipo di attività restituito.Invocation of a task-returning async function causes an instance of the returned task type to be generated. Questa operazione viene definita attività return della funzione asincrona.This is called the return task of the async function. L'attività inizialmente si trova in uno stato incompleto.The task is initially in an incomplete state.

Il corpo della funzione asincrona viene quindi valutato fino a quando non viene sospeso (raggiungendo un'espressione await) o termina, in cui il controllo del punto viene restituito al chiamante, insieme all'attività return.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 il corpo della funzione asincrona termina, l'attività return viene spostata allo stato incompleto:When the body of the async function terminates, the return task is moved out of the incomplete state:

  • Se il corpo della funzione termina in seguito al raggiungimento di un'istruzione return o alla fine del corpo, qualsiasi valore di risultato viene registrato nell'attività return, che viene inserita in uno stato succeeded.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 il corpo della funzione termina come risultato di un'eccezione non rilevata (istruzione throw), l'eccezione viene registrata nell'attività return, che viene inserita in uno stato di errore.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.

Valutazione di una funzione asincrona che restituisce voidEvaluation of a void-returning async function

Se il tipo restituito della funzione asincrona è void , la valutazione differisce dal precedente nel modo seguente: poiché non viene restituita alcuna attività, la funzione comunica invece il completamento e le eccezioni al contesto di sincronizzazione del thread corrente.If the return type of the async function is void, evaluation differs from the above in the following way: Because no task is returned, the function instead communicates completion and exceptions to the current thread's synchronization context. La definizione esatta del contesto di sincronizzazione è dipendente dall'implementazione, ma è una rappresentazione "Where" del thread corrente in esecuzione.The exact definition of synchronization context is implementation-dependent, but is a representation of "where" the current thread is running. Il contesto di sincronizzazione riceve una notifica quando inizia la valutazione di una funzione asincrona che restituisce void, viene completata correttamente o causa la generazione di un'eccezione non rilevata.The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.

Ciò consente al contesto di tenere traccia del numero di funzioni asincrone che restituiscono void e di decidere come propagare le eccezioni in uscita.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.