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 thenew
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 esserenull
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 benull
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
. B
La 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. C
La 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 asealed
orabstract
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 typeobject
. - 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 withprotected
orprotected 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 formatoT.I
oThe namespace_or_type_name is theT
in a namespace_or_type_name of the formT.I
, or - Il namespace_or_type_name è
T
in una typeof_expression (elenchi di argomenti1) del modulotypeof(T)
.The namespace_or_type_name is theT
in a typeof_expression (Argument lists1) of the formtypeof(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
- Il primary_expression è
E
in una member_access (controllo in fase di compilazione della risoluzione dell'overload dinamico) del moduloE.I
.The primary_expression is theE
in a member_access (Compile-time checking of dynamic overload resolution) of the formE.I
.
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 {}
A
la 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
B
la 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 besealed
. - Il tipo non deve essere uno dei tipi seguenti:
System.Array
,System.Delegate
,System.Enum
oSystem.ValueType
.The type must not be one of the following types:System.Array
,System.Delegate
,System.Enum
, orSystem.ValueType
. - Il tipo non deve essere
object
.The type must not beobject
. Poiché tutti i tipi derivano daobject
, un vincolo di questo tipo non avrà alcun effetto se fosse consentito.Because all types derive fromobject
, 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 givenwhere
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 givenwhere
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 daT
.If a type parameterT
is used as a constraint for type parameterS
thenS
depends onT
. - Se un parametro di tipo
S
dipende da un parametro di tipoT
e dipende daT
un parametro di tipo,U
S
dipende daU
.If a type parameterS
depends on a type parameterT
andT
depends on a type parameterU
thenS
depends onU
.
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, quindiS
verrebbe forzato a essere lo stesso tipo diT
, eliminando la necessità di due parametri di tipo.Otherwise,T
is effectively sealed soS
would be forced to be the same type asT
, eliminating the need for two type parameters.- Se
S
ha il vincolo di tipo valoreT
, quindi non deve avere un vincolo class_type .IfS
has the value type constraint thenT
must not have a class_type constraint. - Se
S
ha un vincolo di class_typeA
eT
ha un vincolo di class_type , è necessario che sia presente una conversione diB
identità o una conversione di un riferimento implicito daA
aB
o da una conversione di riferimento implicita daB
aA
.IfS
has a class_type constraintA
andT
has a class_type constraintB
then there must be an identity conversion or implicit reference conversion fromA
toB
or an implicit reference conversion fromB
toA
. - Se
S
dipende anche dal parametro di tipoU
eU
ha un vincolo di class_typeA
eT
ha un vincolo di class_type , è necessario che sia presente una conversione diB
identità o un riferimento implicito da aA
o daB
a una conversione di riferimento implicita daB
aA
.IfS
also depends on type parameterU
andU
has a class_type constraintA
andT
has a class_type constraintB
then there must be an identity conversion or implicit reference conversion fromA
toB
or an implicit reference conversion fromB
toA
.
S
Il 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
.IfT
has no primary constraints or type parameter constraints, its effective base class isobject
. - Se
T
ha il vincolo di tipo valore, la relativa classe di base effettiva èSystem.ValueType
.IfT
has the value type constraint, its effective base class isSystem.ValueType
. - Se
T
ha un vincolo class_typeC
ma nessun vincolo type_parameter , la relativa classe di base effettiva èC
.IfT
has a class_type constraintC
but no type_parameter constraints, its effective base class isC
. - 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 .IfT
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 diT
e dalle classi di base valide dei vincoli di type_parameter .IfT
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 ofT
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
.IfT
has the reference type constraint but no class_type constraints, its effective base class isobject
.
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.IfT
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 .IfT
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 .IfT
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 .IfT
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 è inclusoThepartial
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:
- Si tratta di un errore in fase di compilazione per creare un delegato al metodo (espressioni di creazione di delegati).It is a compile-time error to create a delegate to method (Delegate creation expressions).
- Si tratta di un errore in fase di compilazione per fare riferimento all'
M
interno di una funzione anonima convertita in un tipo di albero delle espressioni (valutazione delle conversioni di funzioni anonime nei tipi di albero delle espressioni).It is a compile-time error to refer toM
inside an anonymous function that is converted to an expression tree type (Evaluation of anonymous function conversions to expression tree types). - Le espressioni che si verificano come parte di una chiamata di non
M
influiscono sullo stato di assegnazione definito (assegnazione definita), che può causare errori in fase di compilazione.Expressions occurring as part of an invocation ofM
do not affect the definite assignment state (Definite assignment), which can potentially lead to compile-time errors. M
non può essere il punto di ingresso di un'applicazione (avvio dell'applicazione).M
cannot be the entry point for an application (Application Startup).
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
eout
.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 byref
andout
. - 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
eout
.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 byref
andout
. - 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 daB
edB
è derivato daA
,C
eredita i membri dichiarati in, nonchéB
i membri dichiarati inA
.IfC
is derived fromB
, andB
is derived fromA
, thenC
inherits the members declared inB
as well as the members declared inA
. - 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 formE.M
,E
deve indicare un tipo che contieneM
.When a static memberM
is referenced in a member_access (Member access) of the formE.M
,E
must denote a type containingM
. Si tratta di un errore in fase di compilazione per laE
denotazione di un'istanza di.It is a compile-time error forE
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 tothis
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 formE.M
,E
deve indicare un'istanza di un tipo contenenteM
.When an instance memberM
is referenced in a member_access (Member access) of the formE.M
,E
must denote an instance of a type containingM
. Si tratta di un errore in fase di binding perE
per indicare un tipo.It is a binding-time error forE
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 asthis
(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");
}
}
}
B
la 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
oprivate
) 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
, orprivate
) and, like other class members, defaults toprivate
declared accessibility. - Un tipo annidato dichiarato in uno struct può avere tre forme di accessibilità dichiarata (
public
,internal
oprivate
) 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
, orprivate
) and, like other struct members, defaults toprivate
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 comeout
ref
parametro o.These are also the only contexts in which it is valid to pass areadonly
field as anout
orref
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
oSystem.UIntPtr
.The typebyte
,sbyte
,short
,ushort
,int
,uint
,char
,float
,bool
,System.IntPtr
, orSystem.UIntPtr
. - Un enum_type avere un tipo di base enum di
byte
,sbyte
,short
,ushort
,int
ouint
.An enum_type having an enum base type ofbyte
,sbyte
,short
,ushort
,int
, oruint
.
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
eoverride
.The declaration includes at most one of the following modifiers:static
,virtual
, andoverride
. - La dichiarazione include al massimo uno dei modificatori seguenti:
new
eoverride
.The declaration includes at most one of the following modifiers:new
andoverride
. - Se la dichiarazione include il
abstract
modificatore, la dichiarazione non include i modificatori seguenti:static
,virtual
sealed
oextern
.If the declaration includes theabstract
modifier, then the declaration does not include any of the following modifiers:static
,virtual
,sealed
orextern
. - Se la dichiarazione include il
private
modificatore, la dichiarazione non include i modificatori seguenti:virtual
,override
oabstract
.If the declaration includes theprivate
modifier, then the declaration does not include any of the following modifiers:virtual
,override
, orabstract
. - Se la dichiarazione include il
sealed
modificatore, la dichiarazione include anche iloverride
modificatore.If the declaration includes thesealed
modifier, then the declaration also includes theoverride
modifier. - Se la dichiarazione include il
partial
modificatore, non include i modificatori seguenti:new
,,public
protected
,internal
,private
,virtual
,sealed
,override
,abstract
oextern
.If the declaration includes thepartial
modifier, then it does not include any of the following modifiers:new
,public
,protected
,internal
,private
,virtual
,sealed
,override
,abstract
, orextern
.
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()
cuiS
è un tipo di valorean expression of the formnew S()
whereS
is a value type - espressione nel formato in
default(S)
cuiS
è un tipo di valorean expression of the formdefault(S)
whereS
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 theref
modifier. - Parametri di output, dichiarati con il
out
modificatore.Output parameters, which are declared with theout
modifier. - Matrici di parametri, dichiarate con il
params
modificatore.Parameter arrays, which are declared with theparams
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
eA
per selezionare un metodo specificoM
dal set di metodi dichiarato in e ereditato daC
.First, overload resolution is applied toC
,N
, andA
, to select a specific methodM
from the set of methods declared in and inherited byC
. Questa operazione è descritta in chiamate al metodo.This is described in Method invocations. - Quindi, se
M
è un metodo non virtuale,M
viene richiamato.Then, ifM
is a non-virtual method,M
is invoked. - In caso contrario,
M
è un metodo virtuale eM
R
viene richiamata l'implementazione più derivata di rispetto a.Otherwise,M
is a virtual method, and the most derived implementation ofM
with respect toR
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 lavirtual
dichiarazione di introduzione diM
, si tratta dell'implementazione più derivata diM
.IfR
contains the introducingvirtual
declaration ofM
, then this is the most derived implementation ofM
. - In caso contrario, se
R
contiene un valoreoverride
diM
, si tratta dell'implementazione più derivata diM
.Otherwise, ifR
contains anoverride
ofM
, then this is the most derived implementation ofM
. - In caso contrario, l'implementazione più derivata di rispetto
M
aR
corrisponde all'implementazione più derivata di rispettoM
alla classe di base diretta diR
.Otherwise, the most derived implementation ofM
with respect toR
is the same as the most derived implementation ofM
with respect to the direct base class ofR
.
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. B
l'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");
}
}
A
la 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 accessoget
eset
e quindi è consentito solo in una di queste funzioni di accesso.For a property or indexer that has nooverride
modifier, an accessor_modifier is permitted only if the property or indexer has both aget
andset
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 anoverride
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ò essereprotected internal
,,internal
protected
oprivate
.If the property or indexer has a declared accessibility ofpublic
, the accessor_modifier may be eitherprotected internal
,internal
,protected
, orprivate
. - Se la proprietà o l'indicizzatore ha un'accessibilità dichiarata di
protected internal
, il accessor_modifier può essereinternal
,protected
oprivate
.If the property or indexer has a declared accessibility ofprotected internal
, the accessor_modifier may be eitherinternal
,protected
, orprivate
. - Se la proprietà o l'indicizzatore ha un'accessibilità dichiarata di
internal
oprotected
, il accessor_modifier deve essereprivate
.If the property or indexer has a declared accessibility ofinternal
orprotected
, the accessor_modifier must beprivate
. - 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 ofprivate
, no accessor_modifier may be used.
- Se la proprietà o l'indicizzatore ha un'accessibilità dichiarata di
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 unaset
funzione di accesso è detta proprietà di lettura/scrittura .A property that includes both aget
accessor and aset
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 aget
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 aset
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. get
Le 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");
TextWriter
viene 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), theget
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), theset
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 diget
accesso e la funzione di accessoset
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 theget
accessors and theset
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.Aget
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à, unvoid
tipo restituito e gli stessi modificatori della proprietà che lo contiene.Aset
accessor corresponds to a method with a single value parameter of the property type, avoid
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 astatic
member, whereas an indexer is always an instance member. - Una
get
funzione di accesso di una proprietà corrisponde a un metodo senza parametri, mentre unaget
funzione di accesso di un indicizzatore corrisponde a un metodo con lo stesso elenco di parametri formali dell'indicizzatore.Aget
accessor of a property corresponds to a method with no parameters, whereas aget
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 denominatovalue
, mentre unaset
funzione di accesso di un indicizzatore corrisponde a un metodo con lo stesso elenco di parametri formali dell'indicizzatore, oltre a un parametro aggiuntivo denominatovalue
.Aset
accessor of a property corresponds to a method with a single parameter namedvalue
, whereas aset
accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter namedvalue
. - 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
, doveP
è il nome della proprietà.In an overriding property declaration, the inherited property is accessed using the syntaxbase.P
, whereP
is the property name. In una dichiarazione di indicizzatore che esegue l'override, viene eseguito l'accesso all'indicizzatore ereditato usando la sintassibase[E]
, doveE
è un elenco di espressioni separate da virgole.In an overriding indexer declaration, the inherited indexer is accessed using the syntaxbase[E]
, whereE
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 apublic
and astatic
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 specifyref
orout
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 tipoT
oT?
e può restituire qualsiasi tipo.A unary+
,-
,!
, or~
operator must take a single parameter of typeT
orT?
and can return any type. - Un operatore unario
++
o--
deve prendere un solo parametro di tipoT
oT?
e deve restituire lo stesso tipo o un tipo derivato da esso.A unary++
or--
operator must take a single parameter of typeT
orT?
and must return that same type or a type derived from it. - Un operatore unario
true
ofalse
deve prendere un solo parametro di tipoT
oT?
e deve restituire il tipobool
.A unarytrue
orfalse
operator must take a single parameter of typeT
orT?
and must return typebool
.
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
oT?
e può restituire qualsiasi tipo.A binary non-shift operator must take two parameters, at least one of which must have typeT
orT?
, and can return any type. - Un
<<
operatore o binario>>
deve assumere due parametri, il primo dei quali deve avereT
il tipo oT?
e il secondo di cui deve avere il tipoint
o eint?
può restituire qualsiasi tipo.A binary<<
or>>
operator must take two parameters, the first of which must have typeT
orT?
and the second of which must have typeint
orint?
, 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 ==
eoperator !=
operator ==
andoperator !=
operator >
eoperator <
operator >
andoperator <
operator >=
eoperator <=
operator >=
andoperator <=
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
eT0
sono tipi diversi.S0
andT0
are different types.S0
OT0
è il tipo di classe o struct in cui si verifica la dichiarazione di operatore.EitherS0
orT0
is the class or struct type in which the operator declaration takes place.S0
Né néT0
è un INTERFACE_TYPE.NeitherS0
norT0
is an interface_type.- Escludendo le conversioni definite dall'utente, non esiste una conversione da
S
aT
o daT
aS
.Excluding user-defined conversions, a conversion does not exist fromS
toT
or fromT
toS
.
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 tipoT
, tutte le conversioni definite dall'utente (implicite o esplicite) daS
aT
vengono ignorate.If a pre-defined implicit conversion (Implicit conversions) exists from typeS
to typeT
, all user-defined conversions (implicit or explicit) fromS
toT
are ignored. - Se una conversione esplicita predefinita (conversioni esplicite) esiste dal tipo
S
al tipoT
, le conversioni esplicite definite dall'utente daS
aT
vengono ignorate.If a pre-defined explicit conversion (Explicit conversions) exists from typeS
to typeT
, any user-defined explicit conversions fromS
toT
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)
obase()
causa la richiamata di un costruttore di istanza dalla classe base diretta.An instance constructor initializer of the formbase(argument_list)
orbase()
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)
othis()
causa la richiamata di un costruttore di istanza dalla classe stessa.An instance constructor initializer of the formthis(argument-list)
orthis()
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. Y
l'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
oIEnumerable
èobject
.The yield type of an iterator that returnsIEnumerator
orIEnumerable
isobject
. - Tipo di Yield di un iteratore che restituisce
IEnumerator<T>
oIEnumerable<T>
èT
.The yield type of an iterator that returnsIEnumerator<T>
orIEnumerable<T>
isT
.
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
eIEnumerator<T>
, doveT
è il tipo di risultato dell'iteratore.It implementsIEnumerator
andIEnumerator<T>
, whereT
is the yield type of the iterator. - Implementa
System.IDisposable
.It implementsSystem.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, invokingMoveNext
:- 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 (includingthis
) 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 invokingMoveNext
is unspecified. - Se lo stato dell'oggetto enumeratore è sospeso, richiamando
MoveNext
:If the state of the enumerator object is suspended, invokingMoveNext
:- 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 theyield 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
restituiscefalse
.If the state of the enumerator object is after, invokingMoveNext
returnsfalse
.
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 ayield 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 theCurrent
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 questayield return
istruzione.The values of all local variables and parameters (includingthis
) are saved, as is the location of thisyield return
statement. Se l'yield return
istruzione si trova all'interno di uno o piùtry
blocchi, ifinally
blocchi associati non vengono eseguiti in questo momento.If theyield return
statement is within one or moretry
blocks, the associatedfinally
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 restituiscetrue
al chiamante, a indicare che l'iterazione è stata avanzata correttamente in base al valore successivo.TheMoveNext
method returnstrue
to its caller, indicating that the iteration successfully advanced to the next value.
- L'espressione specificata nell'istruzione viene valutata, convertita in modo implicito nel tipo yield e assegnata alla
- Quando
yield break
viene rilevata un'istruzione (istruzione yield):When ayield 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 theyield break
statement is within one or moretry
blocks, the associatedfinally
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 restituiscefalse
al chiamante, a indicare che l'iterazione è completa.TheMoveNext
method returnsfalse
to its caller, indicating that the iteration is complete.
- Se l'
- 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 restituiscefalse
al chiamante, a indicare che l'iterazione è completa.TheMoveNext
method returnsfalse
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:
finally
I blocchi appropriati nel corpo dell'iteratore sono stati eseguiti dalla propagazione delle eccezioni.Appropriatefinally
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 theMoveNext
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 _, invokingDispose
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 invokingDispose
is unspecified. - Se lo stato dell'oggetto enumeratore è sospeso, richiamando
Dispose
:If the state of the enumerator object is suspended, invokingDispose
:- 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 executedyield return
statement were ayield 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 delDispose
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 theDispose
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, invokingDispose
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
eIEnumerable<T>
, doveT
è il tipo di risultato dell'iteratore.It implementsIEnumerable
andIEnumerable<T>
, whereT
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.