[C # Basic Knowledge Series] Tema 3: Cómo envolver múltiples métodos con la cadena de delegación-delegación

Introducción

 El tema anterior introdujo la forma en que el siguiente compilador traduce a los delegados. Al observar a los delegados desde la perspectiva de los idiomas intermedios, espero ayudarlo a comprender mejor a los delegados. Sin embargo, en la introducción anterior, los delegados solo encapsularon un método. ¿Qué pasa con los métodos múltiples? Como a menudo lo escucho en mi vida, hablo en nombre de las opiniones de todos, etc. Dado que el delegado también es un representante, si solo puede representar a una persona, su encanto no es excelente, por lo que delegaremos ¿No puede representar múltiples métodos? La respuesta es sí. De esto se trata este tema: la cadena de delegación. La cadena de delegación también es una delegación, simplemente porque une varias cadenas de delegación, así que la llamamos la cadena de delegación .

 

1. ¿Qué es exactamente la cadena de delegación?

Usualmente vinculamos un método cuando instanciamos el objeto delegado. El delegado presentado en el tema anterior también envuelve un método. Usando el ejemplo anterior, solo hay una persona que nombra a un abogado, es decir, solo hay una parte, pero la vida real Obviamente, este no es el caso en la ley. En una demanda, un abogado puede recibir múltiples casos al mismo tiempo, y también aceptar el nombramiento de varias personas en ese momento. De esta manera, el abogado está obligado a múltiples partes y necesita entender el caso de múltiples partes. De De hecho, esta es la cadena de confianza en la vida. En este momento, este abogado no es solo un abogado que representa a una persona, sino un abogado para múltiples partes. La cadena de delegación en la vida es muy similar a la cadena de delegación en C #. Ahora, ¿cuál es la cadena de delegación en C #?

En primer lugar, la cadena de delegación es una delegación , por lo que no debería ver lo que la cadena de delegación considera que es la nueva característica en C #. Sin embargo, para vincular varias cadenas de delegación, debe almacenar referencias a varias delegaciones. Luego, el objeto de la cadena de delegación está en ¿Dónde almacenar referencias para múltiples delegados? ¿Recuerdas en nuestro último tema, el tipo de comisión que presentamos tiene tres campos no públicos? Estos tres campos son _target, methodPtr y _invocationList. En cuanto a lo que estos tres campos representan específicamente, puede ver mi artículo sobre el tema anterior. Sin embargo, el campo _invocationList es donde se almacenan múltiples referencias de delegado .

Para explicar mejor cómo _invocationList almacena referencias de delegado, primero veamos un ejemplo de una cadena de delegado y los resultados de la operación, y luego analicemos los motivos:

Código de copia
using System; 

namespace DelegateTest 

{ 
    programa de clase pública  
    { // Declara un tipo de delegado, su instancia se refiere a un método
         // Este método devuelve un parámetro int y devuelve un tipo de vacío public delegate void DelegateTest ( int parm); public static void Main ( string [] args) 
        { // Usa métodos estáticos para instanciar delegados 
            DelegateTest dtstatic = new DelegateTest (Program.method1); // Usa métodos de instancia para instanciar delegados 
            DelegateTest dtinstance = new DelegateTest ( nuevo
        
          

          
            

            Programa (). Método2); 
            
            // llama implícitamente al delegado 
            dtstatic ( 1 ); 

            // llama explícitamente al método Invoke para llamar al delegado 
            dtinstance.Invoke ( 1 ); 

            // llama implícitamente al delegado 
            dtstatic ( 2 ); 

            // explícito Llame al método Invoke para llamar al delegado 
            dtinstance.Invoke ( 2 ); 
            Console.Read (); 
        } 
        private  static  void method1 ( int parm) 
        { 
            Console.WriteLine ( " Se llama al método estático, el valor del parámetro es: " + parm); 
        } 

        privado void method2 ( int parm) 
        { 
            Console.WriteLine ( " Se llama al método de instancia, el valor del parámetro es: " + parm); 
        } 
    } 

}
Código de copia

Resultado de la operación:

Analicemos por qué ocurre tal resultado:

Al principio instanciamos dos variables delegadas, el siguiente código:

 // Use un método estático para instanciar el delegado 
            DelegateTest dtstatic = new DelegateTest (Program.method1); 

            // Use un método de instancia para instanciar el delegado 
            DelegateTest dtinstance = new DelegateTest ( nuevo Programa (). Método2);

El estado inicial del objeto delegado al que hacen referencia las variables delegadas dtstatic y dtinstance es el siguiente:

Luego definimos una variable delegada de cadena de referencia delegatechain . Al principio, no tenía ningún objeto delegado, era una referencia nula. Cuando ejecutamos la siguiente línea de código,

delegatechain = (DelegateTest) Delegate.Combine (delegatechain, dtstatic);

El método Combine descubre que es nulo y dtstatic los que intentan fusionarse. Internamente, Combine devuelve directamente el objeto en dtstatic. En este momento, las variables delegatechain y dtstatic se refieren al mismo objeto delegado, como se muestra en la siguiente figura:

Para demostrar la cadena de delegación, agregamos otra delegación a través del código, y luego llamamos al método Combinar , el código es el siguiente:

 delegatechain = (DelegateTest) Delegate.Combine (delegatechain, dtinstance);

En este momento, Método combinación se ha encontrado delegatechain citado un objeto delegado de (ha sido citado en esta ocasión las referencias del objeto delegado destatic) , por lo que la cosechadora será construir un nuevo objeto delegado (que quiere String.Concat, simplemente uso Las dos cadenas están conectadas por el operador +. Para una discusión de cadenas, puede consultar este artículo en mi blog http://www.cnblogs.com/zhili/archive/2012/06/25/String_StringBuilder. html ), este nuevo objeto delegado inicializará sus campos privados _target y _methodPtr , y luego el campo _invocationList se inicializará para hacer referencia a una matriz de objetos delegados, el primer elemento de esta matriz (el subíndice es 0) se inicializa para referirse al delegado que envuelve el método método1, los dos elementos de la matriz se inicializan para referirse al delegado que envuelve el método método2 (es decir, el objeto delegado al que hace referencia dtinstance), y finalmente delegaechain se configura para referirse al objeto delegado recién creado , La siguiente es una imagen que puede ayudarlo a comprender la cadena de delegación (también denominada delegación de multidifusión):

De la misma manera, si agrega un tercer delegado a la cadena de delegados, el proceso es el mismo que el anterior, y se creará un nuevo objeto delegado en este momento, y el campo _invocationList se inicializará para hacer referencia a una matriz que contiene estos tres objetos delegados, sin embargo Algunas personas pueden preguntar después de llamar al método Combinar para la variable de tipo delegado que ha hecho referencia al objeto delegado, se creará un nuevo objeto delegado, y luego los tres campos del nuevo objeto delegado se reinicializarán, y finalmente el tipo delegado anterior La variable se refiere al objeto delegado recién creado (para ayudarlo a resumir el proceso de creación de la cadena de delegados), ¿qué pasa con el objeto delegado anterior? Creo que la mayoría de las personas tendrán esta pregunta. Esto es muy similar al método Concat de la cadena. El objeto delegado anterior y la matriz a la que hace referencia el campo -invocationList se recolectarán como basura. Es inmutable).

Nota : También podemos llamar al método Eliminar de delegado para eliminar al delegado de la cadena, como cuando se llama al siguiente código:

delegatechain = (DelegateTest) Delegate.Remove (delegatechain, nuevo DelegateTest (método1));

Cuando se llama al método Remove, escaneará la matriz delegada mantenida dentro del objeto delegado al que hace referencia delegateChain (primer parámetro) ( si la matriz Remove está vacía para la matriz delegada, no tendrá efecto, es decir, no se eliminará Cualquier referencia de delegado, aquí muestra principalmente que el escaneo se escanea desde la matriz de delegados ), si encuentra los campos _target y _methodPtr del objeto delegado al que hace referencia delegateChain

Para un delegado que coincide con el campo en el segundo parámetro (delegado recién creado), si solo queda un elemento de datos en la matriz después de la eliminación, se devuelve ese elemento de datos (en lugar de crear un nuevo objeto delegado e inicializarlo, esto Cuando _invocationList es nulo, en lugar de guardar una matriz a la que hace referencia un objeto delegado, puede eliminarla y depurarla más tarde.) Si quedan más elementos de datos en la matriz en este momento, cree un nuevo objeto delegado, que se crea E inicialice la matriz _invocationList (el objeto delegado al que hace referencia la matriz en este momento ya es uno menos, ya que se elimina con el método Remove), y cada llamada al método Remove solo puede eliminar un delegado de la cadena, y no eliminará una coincidencia Todos los delegados de los campos _target y _methodPtr (esto puede ser depurado por todos)

 

Segundo, cómo controlar la llamada de delegación en la cadena de delegación.

De lo anterior, creo que todos pueden entender cómo crear un objeto de cadena de delegado, pero también se puede ver en los resultados de ejecución que cada vez que se llama a la cadena de delegado, cada método envuelto por la cadena de delegado se ejecutará secuencialmente. El delegado lanza una excepción para que no se puedan invocar todos los objetos posteriores en la cadena, y si hay un tipo de retorno no nulo delante del delegado, solo se retendrá el último valor de retorno, y los valores de retorno de todos los demás métodos de devolución de llamada serán Descarte, ¿significa esto que el valor de retorno de todas las demás operaciones nunca se verá? Este no es el caso, podemos llamar explícitamente a cada delegado en la cadena llamando al método Delegate.GetInvocationList , y podemos agregar algunos de nuestros propios resultados de definición.

El método GetInvocationList devuelve una matriz de referencias de Delegado, cada una de las cuales apunta a un objeto delegado en la cadena. Internamente, GetInvocationList crea e inicializa una matriz, de modo que cada elemento de los datos se refiere a un delegado en la cadena y luego devuelve una referencia a la matriz. Si el campo _invocatinList es nulo, la matriz devuelta solo tiene un elemento, que es la instancia de delegado. Lo siguiente demuestra lo siguiente a través de un programa:

Código de copia
espacio de nombres DelegateChainDemo 
{ 
    clase Programa 
    { 
        // Declara un tipo de delegado, su instancia se refiere a un método
         // Este método devuelve un parámetro int y devuelve una 
        cadena de delegado público de  tipo vacío DelegateTest (); estático vacío Main ( string [] args) 
        { // Use un método estático para instanciar el delegado 
            DelegateTest dtstatic = new DelegateTest (Program.method1); // Use un método de instancia para instanciar el delegado 
            DelegateTest dtinstance = nuevo DelegateTest ( nuevo Programa (). 
            Método2 ); DelegateTest dtinstance2 = nuevo 

         
            

            DelegateTest ( nuevo Programa (). Método3);
             // Defina un objeto de cadena de delegado, inicialmente inicializado como nulo, no representa ningún método (soy yo, no represento a nadie) 
            DelegateTest delegatechain = null ; 
            delegatechain + = dtstatic; 
            delegatechain + = dtinstance; 
            delegatechain + = dtinstance2; 

            /// / delegatechain = (DelegateTest) Delegate.Remove (delegatechain, new DelegateTest (method1));
             /// / delegatechain = (DelegateTest) Delegate.Remove (delegatechain, new DelegateTest ( nuevo programa (). method2)); 
            Console.WriteLine (Test (delegatechain)); 
            Console.Read (); 
        } 

        privado  estática  Cadena del metodo1 () 
        { 
            retorno  " Este es un método estático 1. " ; 
        } 

        Privada  Cadena metodo2 () 
        { 
            de banda  nueva nueva Excepción ( " una excepción " ); 
        } 

        privada  Cadena del metodo3 () 
        { 
            retorno  " Este es un ejemplo de un método 3 " ; 
        } 
        // Probar el método de llamar al delegado 
        Prueba de cadena estática privada  (cadena DelegateTest) 
        { if (chain == 
            null ) 
            { 
                return  null ; 
            } 

            // Use esta variable para guardar la cadena de salida 
            StringBuilder Returnstring = new StringBuilder (); 

            // Obtenga una matriz de delegado, donde cada elemento se refiere al delegado en la cadena 
            Delegate [] delegatearray = chain. GetInvocationList (); 

            // Atraviesa cada delegado en la matriz 
            foreach (DelegateTest t en delegatearray) 
            { 
                try 
                { 
                    // Llama al delegado para obtener el valor de retorno 
                    returntring.Append (t () + Environment.NewLine); 
                } 
                catch (Excepción e)
                { 
                    returntring.AppendFormat ( " genera unaLa excepción se genera desde el método {0}, la información de excepción es: {1} {2} " , t.Method.Name, e.Message, Environment.NewLine); 
                } 
            } 

            // Devuelve el resultado a la 
            cadena de retorno de la persona que llama. ToString (); 
        } 
    } 
}
Código de copia

Captura de pantalla del resultado en ejecución:

A partir de los resultados de ejecución, podemos ver que en este momento podemos obtener el valor de retorno de cada método de devolución de llamada, y podemos agregar algunos valores de retorno personalizados (la cadena de nueva línea se agrega al programa), para que podamos El objeto delegado está controlado, incluso si uno de ellos arroja una excepción, aún podemos capturarlo en este momento, sin causar el problema de que no se puede llamar al objeto delegado posterior.

 

3. Resumen

Este tema presenta principalmente cómo crear una cadena de delegación y compartir en detalle el proceso de creación de una cadena de delegación. La segunda parte señala principalmente algunas limitaciones de la delegación, y luego devuelve una matriz de delegación llamando al método GetInvocationList, para que pueda Al atravesar cada delegado en la matriz de delegados para notificar el proceso de llamada del delegado, de modo que la llamada de la cadena de delegados pueda controlarse más. Hasta ahora, este tema se ha introducido. A través de la introducción de los tres temas a la comisión, creo que todos tendrán una comprensión más profunda de la comisión. Entonces, ¿por qué escribir tres temas para presentar la comisión en detalle? Principalmente, los eventos que se presentarán más adelante, las expresiones Lambda y Linq están relacionados con la delegación, por lo que una mejor comprensión de la delegación será la base de las siguientes características, espero que sean útiles para todos, voy a tratar el siguiente tema Introduce eventos.

28 artículos originales publicados · Me gusta 15 · Visitas 110,000+

Supongo que te gusta

Origin blog.csdn.net/z3h0a5n8g8x9i9a2o3/article/details/8603698
Recomendado
Clasificación