DelegatiDelegates

I delegati consentono scenari in cui altri linguaggi, ad esempio C++, Pascal e modula, sono stati risolti con i puntatori a funzione.Delegates enable scenarios that other languages—such as C++, Pascal, and Modula -- have addressed with function pointers. A differenza dei puntatori a funzione C++, tuttavia, i delegati sono completamente orientati agli oggetti e, a differenza dei puntatori C++ alle funzioni membro, i delegati incapsulano sia un'istanza dell'oggetto che un metodo.Unlike C++ function pointers, however, delegates are fully object oriented, and unlike C++ pointers to member functions, delegates encapsulate both an object instance and a method.

Una dichiarazione di delegato definisce una classe derivata dalla classe System.Delegate .A delegate declaration defines a class that is derived from the class System.Delegate. Un'istanza del delegato incapsula un elenco chiamate, ovvero un elenco di uno o più metodi, ognuno dei quali viene definito entità richiamabile.A delegate instance encapsulates an invocation list, which is a list of one or more methods, each of which is referred to as a callable entity. Per i metodi di istanza, un'entità chiamabile è costituita da un'istanza e da un metodo su tale istanza.For instance methods, a callable entity consists of an instance and a method on that instance. Per i metodi statici, un'entità chiamabile è costituita da un solo metodo.For static methods, a callable entity consists of just a method. La chiamata di un'istanza del delegato con un set di argomenti appropriato comporta la chiamata di ogni entità chiamabile del delegato con il set di argomenti specificato.Invoking a delegate instance with an appropriate set of arguments causes each of the delegate's callable entities to be invoked with the given set of arguments.

Una proprietà interessante e utile di un'istanza del delegato è che non è in grado di conoscere o preoccuparsi delle classi dei metodi incapsulati. ciò che conta è che questi metodi siano compatibili (dichiarazioni delegate) con il tipo del delegato.An interesting and useful property of a delegate instance is that it does not know or care about the classes of the methods it encapsulates; all that matters is that those methods be compatible (Delegate declarations) with the delegate's type. Questa operazione rende i delegati perfettamente adatti alla chiamata "anonima".This makes delegates perfectly suited for "anonymous" invocation.

Dichiarazioni DelegateDelegate declarations

Un delegate_declaration è un type_declaration (dichiarazioni di tipo) che dichiara un nuovo tipo delegato.A delegate_declaration is a type_declaration (Type declarations) that declares a new delegate type.

delegate_declaration
    : attributes? delegate_modifier* 'delegate' return_type
      identifier variant_type_parameter_list?
      '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';'
    ;

delegate_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | delegate_modifier_unsafe
    ;

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

Il new modificatore è consentito solo su delegati dichiarati all'interno di un altro tipo, nel qual caso specifica che tale delegato nasconde un membro ereditato con lo stesso nome, come descritto nel nuovo modificatore.The new modifier is only permitted on delegates declared within another type, in which case it specifies that such a delegate hides an inherited member by the same name, as described in The new modifier.

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

Il nome del tipo del delegato è Identifier.The delegate's type name is identifier.

Il formal_parameter_list facoltativo specifica i parametri del delegato e return_type indica il tipo restituito del delegato.The optional formal_parameter_list specifies the parameters of the delegate, and return_type indicates the return type of the delegate.

Il variant_type_parameter_list facoltativo (elenchi di parametri di tipo Variant) specifica i parametri di tipo per il delegato stesso.The optional variant_type_parameter_list (Variant type parameter lists) specifies the type parameters to the delegate itself.

Il tipo restituito di un tipo delegato deve essere void o indipendente dall'output (sicurezza dallavarianza).The return type of a delegate type must be either void, or output-safe (Variance safety).

Tutti i tipi di parametro formali di un tipo delegato devono essere indipendenti dall'input.All the formal parameter types of a delegate type must be input-safe. Inoltre, i out ref tipi di parametro o devono anche essere indipendenti dall'output.Additionally, any out or ref parameter types must also be output-safe. Si noti che out è necessario che anche i parametri siano indipendenti dall'input, a causa di una limitazione della piattaforma di esecuzione sottostante.Note that even out parameters are required to be input-safe, due to a limitation of the underlying execution platform.

I tipi di delegati in C# sono equivalenti al nome e non sono strutturalmente equivalenti.Delegate types in C# are name equivalent, not structurally equivalent. In particolare, due tipi delegati diversi che hanno gli stessi elenchi di parametri e il tipo restituito sono considerati tipi delegati diversi.Specifically, two different delegate types that have the same parameter lists and return type are considered different delegate types. Tuttavia, le istanze di due tipi delegati distinti ma strutturalmente equivalenti possono essere confrontate come uguali (operatori di uguaglianza dei delegati).However, instances of two distinct but structurally equivalent delegate types may compare as equal (Delegate equality operators).

Ad esempio:For example:

delegate int D1(int i, double d);

class A
{
    public static int M1(int a, double b) {...}
}

class B
{
    delegate int D2(int c, double d);
    public static int M1(int f, double g) {...}
    public static void M2(int k, double l) {...}
    public static int M3(int g) {...}
    public static void M4(int g) {...}
}

I metodi A.M1 e B.M1 sono compatibili con i tipi delegati D1 e D2 , poiché hanno lo stesso tipo restituito e l'elenco di parametri. Tuttavia, questi tipi delegati sono due tipi diversi, quindi non sono intercambiabili.The methods A.M1 and B.M1 are compatible with both the delegate types D1 and D2 , since they have the same return type and parameter list; however, these delegate types are two different types, so they are not interchangeable. I metodi B.M2 , B.M3 e B.M4 sono incompatibili con i tipi delegati D1 e D2 , poiché presentano tipi restituiti o elenchi di parametri diversi.The methods B.M2, B.M3, and B.M4 are incompatible with the delegate types D1 and D2, since they have different return types or parameter lists.

Analogamente ad altre dichiarazioni di tipo generico, è necessario assegnare argomenti di tipo per creare un tipo delegato costruito.Like other generic type declarations, type arguments must be given to create a constructed delegate type. I tipi di parametro e il tipo restituito di un tipo delegato costruito vengono creati sostituendo, per ogni parametro di tipo nella dichiarazione del delegato, l'argomento di tipo corrispondente del tipo delegato costruito.The parameter types and return type of a constructed delegate type are created by substituting, for each type parameter in the delegate declaration, the corresponding type argument of the constructed delegate type. Il tipo restituito e i tipi di parametro risultanti vengono utilizzati per determinare quali metodi sono compatibili con un tipo delegato costruito.The resulting return type and parameter types are used in determining what methods are compatible with a constructed delegate type. Ad esempio:For example:

delegate bool Predicate<T>(T value);

class X
{
    static bool F(int i) {...}
    static bool G(string s) {...}
}

Il metodo X.F è compatibile con il tipo delegato Predicate<int> e il metodo X.G è compatibile con il tipo delegato Predicate<string> .The method X.F is compatible with the delegate type Predicate<int> and the method X.G is compatible with the delegate type Predicate<string> .

L'unico modo per dichiarare un tipo delegato è tramite un delegate_declaration.The only way to declare a delegate type is via a delegate_declaration. Un tipo delegato è un tipo di classe derivato da System.Delegate .A delegate type is a class type that is derived from System.Delegate. I tipi delegati sono implicitamente sealed , pertanto non è consentito derivare alcun tipo da un tipo delegato.Delegate types are implicitly sealed, so it is not permissible to derive any type from a delegate type. Non è inoltre consentito derivare un tipo di classe non delegata da System.Delegate .It is also not permissible to derive a non-delegate class type from System.Delegate. Si noti che System.Delegate non è a sua volta un tipo delegato. si tratta di un tipo di classe da cui derivano tutti i tipi delegati.Note that System.Delegate is not itself a delegate type; it is a class type from which all delegate types are derived.

C# fornisce una sintassi speciale per la creazione e la creazione di istanze di delegati.C# provides special syntax for delegate instantiation and invocation. Fatta eccezione per la creazione di istanze, qualsiasi operazione che può essere applicata a una classe o a un'istanza di classe può essere applicata rispettivamente a una classe o a un'istanza di delegato.Except for instantiation, any operation that can be applied to a class or class instance can also be applied to a delegate class or instance, respectively. In particolare, è possibile accedere ai membri del System.Delegate tipo tramite la consueta sintassi di accesso ai membri.In particular, it is possible to access members of the System.Delegate type via the usual member access syntax.

Il set di metodi incapsulati da un'istanza di delegato è denominato elenco chiamate.The set of methods encapsulated by a delegate instance is called an invocation list. Quando viene creata un'istanza del delegato (la compatibilità con idelegati) da un singolo metodo, il metodo viene incapsulato e il relativo elenco chiamate contiene una sola voce.When a delegate instance is created (Delegate compatibility) from a single method, it encapsulates that method, and its invocation list contains only one entry. Tuttavia, quando vengono combinate due istanze delegate non null, i rispettivi elenchi chiamate vengono concatenati nell'operando sinistro dell'ordine, quindi nell'operando destro, per formare un nuovo elenco chiamate, che contiene due o più voci.However, when two non-null delegate instances are combined, their invocation lists are concatenated -- in the order left operand then right operand -- to form a new invocation list, which contains two or more entries.

I delegati vengono combinati utilizzando l' + operatore di addizione(Binary) e += gli operatori (assegnazione composta).Delegates are combined using the binary + (Addition operator) and += operators (Compound assignment). Un delegato può essere rimosso da una combinazione di delegati, usando - l'operatore binario (sottrazione) e -= gli operatori (assegnazione composta).A delegate can be removed from a combination of delegates, using the binary - (Subtraction operator) and -= operators (Compound assignment). I delegati possono essere confrontati per verificarnel'uguaglianza.Delegates can be compared for equality (Delegate equality operators).

Nell'esempio seguente viene illustrata la creazione di un'istanza di un numero di delegati e gli elenchi chiamate corrispondenti:The following example shows the instantiation of a number of delegates, and their corresponding invocation lists:

delegate void D(int x);

class C
{
    public static void M1(int i) {...}
    public static void M2(int i) {...}

}

class Test
{
    static void Main() {
        D cd1 = new D(C.M1);      // M1
        D cd2 = new D(C.M2);      // M2
        D cd3 = cd1 + cd2;        // M1 + M2
        D cd4 = cd3 + cd1;        // M1 + M2 + M1
        D cd5 = cd4 + cd3;        // M1 + M2 + M1 + M1 + M2
    }

}

Quando cd1 cd2 viene creata un'istanza di e, ognuno incapsula un metodo.When cd1 and cd2 are instantiated, they each encapsulate one method. Quando cd3 viene creata un'istanza di, dispone di un elenco chiamate di due metodi, M1 e M2 , in questo ordine.When cd3 is instantiated, it has an invocation list of two methods, M1 and M2, in that order. cd4l'elenco chiamate contiene M1 , M2 e M1 , in questo ordine.cd4's invocation list contains M1, M2, and M1, in that order. Infine, cd5 l'elenco chiamate contiene M1 , M2 , M1 , M1 e M2 , in questo ordine.Finally, cd5's invocation list contains M1, M2, M1, M1, and M2, in that order. Per altri esempi di combinazione e rimozione di delegati, vedere chiamata al delegato.For more examples of combining (as well as removing) delegates, see Delegate invocation.

Compatibilità dei delegatiDelegate compatibility

Un metodo o un delegato M è compatibile con un tipo delegato D se si verificano tutte le condizioni seguenti:A method or delegate M is compatible with a delegate type D if all of the following are true:

  • D e M hanno lo stesso numero di parametri e ogni parametro in D ha gli stessi ref out modificatori o del parametro corrispondente in M .D and M have the same number of parameters, and each parameter in D has the same ref or out modifiers as the corresponding parameter in M.
  • Per ogni parametro di valore (un parametro senza ref out modificatore), esiste una conversione di identità (conversione di identità) o conversione di un riferimento implicito (conversioni di riferimenti impliciti) dal tipo di parametro in D al tipo di parametro corrispondente in M .For each value parameter (a parameter with no ref or out modifier), an identity conversion (Identity conversion) or implicit reference conversion (Implicit reference conversions) exists from the parameter type in D to the corresponding parameter type in M.
  • Per ogni ref out parametro o, il tipo di parametro in D corrisponde al tipo di parametro in M .For each ref or out parameter, the parameter type in D is the same as the parameter type in M.
  • Una conversione di identità o di riferimento implicita esiste dal tipo restituito di M al tipo restituito di D .An identity or implicit reference conversion exists from the return type of M to the return type of D.

Creazione di un'istanza del delegatoDelegate instantiation

Un'istanza di un delegato viene creata da un delegate_creation_expression (espressioni di creazione di delegati) o una conversione in un tipo delegato.An instance of a delegate is created by a delegate_creation_expression (Delegate creation expressions) or a conversion to a delegate type. L'istanza del delegato appena creata fa quindi riferimento a uno dei seguenti valori:The newly created delegate instance then refers to either:

  • Metodo statico a cui si fa riferimento nell' delegate_creation_expression oThe static method referenced in the delegate_creation_expression, or
  • L'oggetto di destinazione, che non può essere, null e il metodo di istanza a cui si fa riferimento nel delegate_creation_expressionThe target object (which cannot be null) and instance method referenced in the delegate_creation_expression, or
  • Un altro delegato.Another delegate.

Ad esempio:For example:

delegate void D(int x);

class C
{
    public static void M1(int i) {...}
    public void M2(int i) {...}
}

class Test
{
    static void Main() { 
        D cd1 = new D(C.M1);        // static method
        C t = new C();
        D cd2 = new D(t.M2);        // instance method
        D cd3 = new D(cd2);        // another delegate
    }
}

Una volta creata un'istanza, le istanze delegate fanno sempre riferimento allo stesso oggetto e metodo di destinazione.Once instantiated, delegate instances always refer to the same target object and method. Tenere presente che quando due delegati vengono combinati o uno viene rimosso da un altro, un nuovo delegato restituisce il proprio elenco chiamate; gli elenchi chiamate dei delegati combinati o rimossi restano invariati.Remember, when two delegates are combined, or one is removed from another, a new delegate results with its own invocation list; the invocation lists of the delegates combined or removed remain unchanged.

Chiamata a un delegatoDelegate invocation

C# fornisce una sintassi speciale per richiamare un delegato.C# provides special syntax for invoking a delegate. Quando un'istanza di delegato non null il cui elenco chiamate contiene una voce viene richiamata, richiama il metodo con gli stessi argomenti che è stato fornito e restituisce lo stesso valore del metodo a cui si fa riferimento.When a non-null delegate instance whose invocation list contains one entry is invoked, it invokes the one method with the same arguments it was given, and returns the same value as the referred to method. Per informazioni dettagliate sulla chiamata al delegato, vedere chiamate di delegati . Se si verifica un'eccezione durante la chiamata di un delegato di questo tipo e tale eccezione non viene rilevata all'interno del metodo richiamato, la ricerca di una clausola catch di eccezione continua nel metodo che ha chiamato il delegato, come se il metodo avesse chiamato direttamente il metodo a cui fa riferimento il delegato.(See Delegate invocations for detailed information on delegate invocation.) If an exception occurs during the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, as if that method had directly called the method to which that delegate referred.

La chiamata di un'istanza del delegato il cui elenco chiamate contiene più voci continua richiamando ognuno dei metodi nell'elenco chiamate, in modo sincrono, nell'ordine.Invocation of a delegate instance whose invocation list contains multiple entries proceeds by invoking each of the methods in the invocation list, synchronously, in order. A ogni metodo chiamato viene passato lo stesso set di argomenti fornito all'istanza del delegato.Each method so called is passed the same set of arguments as was given to the delegate instance. Se tale chiamata al delegato include parametri di riferimento (parametri di riferimento), ogni chiamata al metodo viene eseguita con un riferimento alla stessa variabile. le modifiche apportate a tale variabile da un metodo nell'elenco chiamate saranno visibili ai metodi successivi nell'elenco chiamate.If such a delegate invocation includes reference parameters (Reference parameters), each method invocation will occur with a reference to the same variable; changes to that variable by one method in the invocation list will be visible to methods further down the invocation list. Se la chiamata al delegato include parametri di output o un valore restituito, il relativo valore finale proverrà dalla chiamata dell'ultimo delegato nell'elenco.If the delegate invocation includes output parameters or a return value, their final value will come from the invocation of the last delegate in the list.

Se si verifica un'eccezione durante l'elaborazione della chiamata di un delegato di questo tipo e tale eccezione non viene rilevata all'interno del metodo che è stato richiamato, la ricerca di una clausola catch di eccezione continua nel metodo che ha chiamato il delegato e non vengono richiamati i metodi successivi nell'elenco chiamate.If an exception occurs during processing of the invocation of such a delegate, and that exception is not caught within the method that was invoked, the search for an exception catch clause continues in the method that called the delegate, and any methods further down the invocation list are not invoked.

Se si tenta di richiamare un'istanza del delegato il cui valore è null, viene generata un'eccezione di tipo System.NullReferenceException .Attempting to invoke a delegate instance whose value is null results in an exception of type System.NullReferenceException.

Nell'esempio seguente viene illustrato come creare un'istanza, combinare, rimuovere e richiamare delegati:The following example shows how to instantiate, combine, remove, and invoke delegates:

using System;

delegate void D(int x);

class C
{
    public static void M1(int i) {
        Console.WriteLine("C.M1: " + i);
    }

    public static void M2(int i) {
        Console.WriteLine("C.M2: " + i);
    }

    public void M3(int i) {
        Console.WriteLine("C.M3: " + i);
    }
}

class Test
{
    static void Main() { 
        D cd1 = new D(C.M1);
        cd1(-1);                // call M1

        D cd2 = new D(C.M2);
        cd2(-2);                // call M2

        D cd3 = cd1 + cd2;
        cd3(10);                // call M1 then M2

        cd3 += cd1;
        cd3(20);                // call M1, M2, then M1

        C c = new C();
        D cd4 = new D(c.M3);
        cd3 += cd4;
        cd3(30);                // call M1, M2, M1, then M3

        cd3 -= cd1;             // remove last M1
        cd3(40);                // call M1, M2, then M3

        cd3 -= cd4;
        cd3(50);                // call M1 then M2

        cd3 -= cd2;
        cd3(60);                // call M1

        cd3 -= cd2;             // impossible removal is benign
        cd3(60);                // call M1

        cd3 -= cd1;             // invocation list is empty so cd3 is null

        cd3(70);                // System.NullReferenceException thrown

        cd3 -= cd1;             // impossible removal is benign
    }
}

Come illustrato nell'istruzione cd3 += cd1; , un delegato può essere presente più volte in un elenco chiamate.As shown in the statement cd3 += cd1;, a delegate can be present in an invocation list multiple times. In questo caso, viene semplicemente richiamato una volta per ogni occorrenza.In this case, it is simply invoked once per occurrence. In un elenco chiamate come questo, quando il delegato viene rimosso, l'ultima occorrenza nell'elenco chiamate è quella effettivamente rimossa.In an invocation list such as this, when that delegate is removed, the last occurrence in the invocation list is the one actually removed.

Immediatamente prima dell'esecuzione dell'istruzione finale, cd3 -= cd1; , il delegato cd3 fa riferimento a un elenco chiamate vuoto.Immediately prior to the execution of the final statement, cd3 -= cd1;, the delegate cd3 refers to an empty invocation list. Il tentativo di rimuovere un delegato da un elenco vuoto (o di rimuovere un delegato non esistente da un elenco non vuoto) non è un errore.Attempting to remove a delegate from an empty list (or to remove a non-existent delegate from a non-empty list) is not an error.

L'output prodotto è:The output produced is:

C.M1: -1
C.M2: -2
C.M1: 10
C.M2: 10
C.M1: 20
C.M2: 20
C.M1: 20
C.M1: 30
C.M2: 30
C.M1: 30
C.M3: 30
C.M1: 40
C.M2: 40
C.M3: 40
C.M1: 50
C.M2: 50
C.M1: 60
C.M1: 60