DelegadosDelegates
Los delegados habilitan escenarios en los que otros lenguajes, como C++, Pascal y modula, se han direccionado con punteros de función.Delegates enable scenarios that other languages—such as C++, Pascal, and Modula -- have addressed with function pointers. A diferencia de los punteros de función de C++, sin embargo, los delegados están totalmente orientados a objetos y, a diferencia de los punteros de C++ a las funciones miembro, los delegados encapsulan una instancia de objeto y un método.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 declaración de delegado define una clase que se deriva de la clase System.Delegate
.A delegate declaration defines a class that is derived from the class System.Delegate
. Una instancia de delegado encapsula una lista de invocación, que es una lista de uno o varios métodos, a la que se hace referencia como una entidad a la que se puede llamar.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. En el caso de los métodos de instancia, una entidad a la que se puede llamar está formada por una instancia y un método en esa instancia.For instance methods, a callable entity consists of an instance and a method on that instance. En el caso de los métodos estáticos, una entidad a la que se puede llamar consta simplemente de un método.For static methods, a callable entity consists of just a method. Al invocar una instancia de delegado con un conjunto de argumentos adecuado, se invoca a cada una de las entidades a las que se puede llamar del delegado con el conjunto de argumentos especificado.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 propiedad interesante y útil de una instancia de delegado es que no sabe ni le interesan las clases de los métodos que encapsula; lo único que importa es que esos métodos sean compatibles (declaraciones de delegado) con el tipo de delegado.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. Esto hace que los delegados sean perfectamente adecuados para la invocación "anónima".This makes delegates perfectly suited for "anonymous" invocation.
Declaraciones de delegadoDelegate declarations
Un delegate_declaration es un type_declaration (declaraciones de tipos) que declara un nuevo tipo de delegado.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
;
Es un error en tiempo de compilación que el mismo modificador aparezca varias veces en una declaración de delegado.It is a compile-time error for the same modifier to appear multiple times in a delegate declaration.
El new
modificador solo se permite en los delegados declarados dentro de otro tipo, en cuyo caso especifica que dicho delegado oculte un miembro heredado con el mismo nombre, tal y como se describe en el modificador New.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.
Los public
protected
internal
modificadores,, y private
controlan la accesibilidad del tipo de delegado.The public
, protected
, internal
, and private
modifiers control the accessibility of the delegate type. En función del contexto en el que se produce la declaración de delegado, es posible que algunos de estos modificadores no estén permitidos (sedeclare accesibilidad).Depending on the context in which the delegate declaration occurs, some of these modifiers may not be permitted (Declared accessibility).
El nombre de tipo del delegado es Identifier.The delegate's type name is identifier.
El formal_parameter_list opcional especifica los parámetros del delegado y return_type indica el tipo de valor devuelto del delegado.The optional formal_parameter_list specifies the parameters of the delegate, and return_type indicates the return type of the delegate.
El variant_type_parameter_list opcional (listas de parámetros de tipo variante) especifica los parámetros de tipo para el delegado.The optional variant_type_parameter_list (Variant type parameter lists) specifies the type parameters to the delegate itself.
El tipo de valor devuelto de un tipo de delegado debe ser o tener seguridad void
de salida (seguridad de lavarianza).The return type of a delegate type must be either void
, or output-safe (Variance safety).
Todos los tipos de parámetros formales de un tipo de delegado deben ser seguros para la entrada.All the formal parameter types of a delegate type must be input-safe. Además, los out
ref
tipos de parámetro o también deben ser de seguridad de salida.Additionally, any out
or ref
parameter types must also be output-safe. Tenga en cuenta que incluso out
se requiere que los parámetros sean seguros para la entrada, debido a una limitación de la plataforma de ejecución subyacente.Note that even out
parameters are required to be input-safe, due to a limitation of the underlying execution platform.
Los tipos de delegado en C# son equivalentes de nombre, no estructuralmente equivalentes.Delegate types in C# are name equivalent, not structurally equivalent. En concreto, dos tipos de delegado diferentes que tienen las mismas listas de parámetros y tipo de valor devuelto se consideran tipos de delegado distintos.Specifically, two different delegate types that have the same parameter lists and return type are considered different delegate types. Sin embargo, las instancias de dos tipos de delegado distintos, pero estructuralmente equivalentes, pueden compararse como iguales (operadores de igualdad de delegado).However, instances of two distinct but structurally equivalent delegate types may compare as equal (Delegate equality operators).
Por ejemplo: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) {...}
}
Los métodos A.M1
y B.M1
son compatibles con los tipos de delegado D1
y D2
, ya que tienen el mismo tipo de valor devuelto y la misma lista de parámetros; sin embargo, estos tipos de delegado son dos tipos diferentes, por lo que no son intercambiables.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. Los métodos B.M2
, B.M3
y B.M4
son incompatibles con los tipos de delegado D1
y D2
, ya que tienen tipos de valor devuelto o listas de parámetros diferentes.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.
Al igual que otras declaraciones de tipos genéricos, se deben proporcionar argumentos de tipo para crear un tipo de delegado construido.Like other generic type declarations, type arguments must be given to create a constructed delegate type. Los tipos de parámetro y el tipo de valor devuelto de un tipo de delegado construido se crean sustituyendo, por cada parámetro de tipo de la declaración de delegado, el argumento de tipo correspondiente del tipo de delegado construido.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. El tipo de valor devuelto y los tipos de parámetro resultantes se usan para determinar qué métodos son compatibles con un tipo de delegado construido.The resulting return type and parameter types are used in determining what methods are compatible with a constructed delegate type. Por ejemplo:For example:
delegate bool Predicate<T>(T value);
class X
{
static bool F(int i) {...}
static bool G(string s) {...}
}
El método X.F
es compatible con el tipo de delegado Predicate<int>
y el método X.G
es compatible con el tipo de delegado 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>
.
La única manera de declarar un tipo de delegado es a través de un delegate_declaration.The only way to declare a delegate type is via a delegate_declaration. Un tipo de delegado es un tipo de clase que se deriva de System.Delegate
.A delegate type is a class type that is derived from System.Delegate
. Los tipos de delegado son implícitamente sealed
, por lo que no se permite derivar ningún tipo de un tipo de delegado.Delegate types are implicitly sealed
, so it is not permissible to derive any type from a delegate type. Tampoco se permite derivar un tipo de clase no delegado de System.Delegate
.It is also not permissible to derive a non-delegate class type from System.Delegate
. Tenga en cuenta que System.Delegate
no es un tipo de delegado, sino que es un tipo de clase del que se derivan todos los tipos de delegado.Note that System.Delegate
is not itself a delegate type; it is a class type from which all delegate types are derived.
C# proporciona una sintaxis especial para la invocación y creación de instancias de delegado.C# provides special syntax for delegate instantiation and invocation. A excepción de la creación de instancias, cualquier operación que se puede aplicar a una instancia de clase o clase también se puede aplicar a una clase o instancia de delegado, respectivamente.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. En concreto, es posible tener acceso a los miembros del System.Delegate
tipo a través de la sintaxis de acceso a miembros habitual.In particular, it is possible to access members of the System.Delegate
type via the usual member access syntax.
El conjunto de métodos encapsulado por una instancia de delegado se denomina lista de invocación.The set of methods encapsulated by a delegate instance is called an invocation list. Cuando se crea una instancia de delegado (compatibilidad con delegación) desde un único método, encapsula ese método y su lista de invocación contiene solo una entrada.When a delegate instance is created (Delegate compatibility) from a single method, it encapsulates that method, and its invocation list contains only one entry. Sin embargo, cuando se combinan dos instancias de delegado que no son NULL, sus listas de invocación se concatenan, en el operando izquierdo de orden y operando derecho, para formar una nueva lista de invocación, que contiene dos o más entradas.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.
Los delegados se combinan mediante el operador binario +
(operador de suma) y los +=
operadores (asignación compuesta).Delegates are combined using the binary +
(Addition operator) and +=
operators (Compound assignment). Un delegado se puede quitar de una combinación de delegados, mediante -
eloperadorbinario (operador de resta) y los -=
operadores (asignación compuesta).A delegate can be removed from a combination of delegates, using the binary -
(Subtraction operator) and -=
operators (Compound assignment). Los delegados se pueden comparar para comprobar si son iguales (operadores de igualdad de delegado).Delegates can be compared for equality (Delegate equality operators).
En el ejemplo siguiente se muestra la creación de instancias de varios delegados y sus listas de invocación correspondientes: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
}
}
Cuando cd1
cd2
se crean instancias de y, cada una de ellas encapsula un método.When cd1
and cd2
are instantiated, they each encapsulate one method. Cuando cd3
se crea una instancia de, tiene una lista de invocaciones de dos métodos, M1
y M2
, en ese orden.When cd3
is instantiated, it has an invocation list of two methods, M1
and M2
, in that order. cd4
la lista de invocación de contiene M1
, M2
y M1
, en ese orden.cd4
's invocation list contains M1
, M2
, and M1
, in that order. Por último, cd5
la lista de invocación de contiene M1
, M2
,, M1
M1
y M2
, en ese orden.Finally, cd5
's invocation list contains M1
, M2
, M1
, M1
, and M2
, in that order. Para obtener más ejemplos de cómo combinar (y quitar) delegados, vea invocación de delegado.For more examples of combining (as well as removing) delegates, see Delegate invocation.
Compatibilidad de delegadoDelegate compatibility
Un método o un delegado M
es compatible con un tipo D
de delegado si se cumplen todas las condiciones siguientes:A method or delegate M
is compatible with a delegate type D
if all of the following are true:
D
yM
tienen el mismo número de parámetros, y cada parámetro deD
tiene los mismosref
out
modificadores o que el parámetro correspondiente deM
.D
andM
have the same number of parameters, and each parameter inD
has the sameref
orout
modifiers as the corresponding parameter inM
.- Para cada parámetro de valor (un parámetro sin
ref
out
modificador o), existe una conversión de identidad (conversión de identidad) o una conversión de referencia implícita (conversiones de referencia implícita) del tipo de parámetro enD
el tipo de parámetro correspondiente enM
.For each value parameter (a parameter with noref
orout
modifier), an identity conversion (Identity conversion) or implicit reference conversion (Implicit reference conversions) exists from the parameter type inD
to the corresponding parameter type inM
. - Para cada
ref
out
parámetro o, el tipo de parámetro deD
es el mismo que el tipo de parámetro enM
.For eachref
orout
parameter, the parameter type inD
is the same as the parameter type inM
. - Existe una conversión de referencia implícita o de identidad del tipo de valor devuelto de
M
al tipo de valor devuelto deD
.An identity or implicit reference conversion exists from the return type ofM
to the return type ofD
.
Creación de instancias de delegadoDelegate instantiation
Una instancia de un delegado se crea mediante un delegate_creation_expression (expresiones de creación de delegados) o una conversión a un tipo de delegado.An instance of a delegate is created by a delegate_creation_expression (Delegate creation expressions) or a conversion to a delegate type. La instancia de delegado recién creada hace referencia a:The newly created delegate instance then refers to either:
- Método estático al que se hace referencia en el delegate_creation_expression, o bienThe static method referenced in the delegate_creation_expression, or
- El objeto de destino (que no puede ser
null
) y el método de instancia al que se hace referencia en el delegate_creation_expression, o bienThe target object (which cannot benull
) and instance method referenced in the delegate_creation_expression, or - Otro delegado.Another delegate.
Por ejemplo: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 vez creada la instancia, las instancias de delegado siempre hacen referencia al mismo objeto y método de destino.Once instantiated, delegate instances always refer to the same target object and method. Recuerde que, cuando se combinan dos delegados o uno se quita de otro, se produce un nuevo delegado que tiene su propia lista de invocación. las listas de invocaciones de los delegados combinados o quitados permanecen sin cambios.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.
Invocación de delegadoDelegate invocation
C# proporciona una sintaxis especial para invocar un delegado.C# provides special syntax for invoking a delegate. Cuando se invoca una instancia de delegado que no es null cuya lista de invocación contiene una entrada, invoca el método con los mismos argumentos en los que se ha dado y devuelve el mismo valor que el método al que se hace referencia.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. (Vea invocaciones de delegado para obtener información detallada sobre la invocación de delegado). Si se produce una excepción durante la invocación de este tipo de delegado y esa excepción no se detecta dentro del método que se invocó, la búsqueda de una cláusula catch de excepción continúa en el método que llamó al delegado, como si ese método hubiera llamado directamente al método al que el delegado hizo referencia.(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 invocación de una instancia de delegado cuya lista de invocaciones contiene varias entradas continúa invocando cada uno de los métodos de la lista de invocación, de forma sincrónica, en orden.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. Cada método al que se llama se pasa el mismo conjunto de argumentos que se asignó a la instancia del delegado.Each method so called is passed the same set of arguments as was given to the delegate instance. Si dicha invocación de delegado incluye parámetros de referencia (parámetros de referencia), cada invocación de método se producirá con una referencia a la misma variable. los cambios en esa variable por un método en la lista de invocación serán visibles para los métodos más abajo en la lista de invocación.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. Si la invocación del delegado incluye parámetros de salida o un valor devuelto, su valor final proviene de la invocación del último delegado en la lista.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.
Si se produce una excepción durante el procesamiento de la invocación de este delegado, y esa excepción no se detecta dentro del método que se invocó, la búsqueda de una cláusula catch de excepción continúa en el método que llamó al delegado, y no se invocan los métodos que se encuentran más abajo en la lista de invocación.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.
Al intentar invocar una instancia de delegado cuyo valor es null, se produce una excepción de tipo System.NullReferenceException
.Attempting to invoke a delegate instance whose value is null results in an exception of type System.NullReferenceException
.
En el ejemplo siguiente se muestra cómo crear instancias, combinar, quitar e invocar delegados: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
}
}
Como se muestra en la instrucción cd3 += cd1;
, un delegado puede estar presente varias veces en una lista de invocación.As shown in the statement cd3 += cd1;
, a delegate can be present in an invocation list multiple times. En este caso, simplemente se invoca una vez por cada repetición.In this case, it is simply invoked once per occurrence. En una lista de invocación como esta, cuando se quita ese delegado, la última aparición en la lista de invocación es la que realmente se quita.In an invocation list such as this, when that delegate is removed, the last occurrence in the invocation list is the one actually removed.
Inmediatamente antes de la ejecución de la instrucción final, cd3 -= cd1;
, el delegado cd3
hace referencia a una lista de invocación vacía.Immediately prior to the execution of the final statement, cd3 -= cd1;
, the delegate cd3
refers to an empty invocation list. El intento de quitar un delegado de una lista vacía (o de quitar un delegado que no existe de una lista no vacía) no es un error.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.
La salida generada es: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