《CL汉兰达 Via C# 第2版》笔记之(二十四) – 委托

信托是.net中落到实处回调机制的一种首要技术,越发在编写服务端程序的时候,更是数次使用委托。

重庆大学内容:

  • 寄托本质
  • 委托链
  • 动态委托

 

1. 寄托本质

寄托本质实际上正是2个类,基本上在能够定义类的地方都能够定义委托,C#中央委员托的写法其实只是C#的语法糖。

如下概念个委托:

using System;

namespace delegate_test
{
    internal delegate int Sum(int a, int b);
}

用ILSpy能够窥见对应的IL代码如下:

.class private auto ansi sealed delegate_test.Sum
    extends [mscorlib]System.MulticastDelegate
{
    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor (
            object 'object',
            native int 'method'
        ) runtime managed 
    {
    } // end of method Sum::.ctor

    .method public hidebysig newslot virtual 
        instance int32 Invoke (
            int32 a,
            int32 b
        ) runtime managed 
    {
    } // end of method Sum::Invoke

    .method public hidebysig newslot virtual 
        instance class [mscorlib]System.IAsyncResult BeginInvoke (
            int32 a,
            int32 b,
            class [mscorlib]System.AsyncCallback callback,
            object 'object'
        ) runtime managed 
    {
    } // end of method Sum::BeginInvoke

    .method public hidebysig newslot virtual 
        instance int32 EndInvoke (
            class [mscorlib]System.IAsyncResult result
        ) runtime managed 
    {
    } // end of method Sum::EndInvoke

} // end of class delegate_test.Sum

从中大家得以看到

  1. 委托是一个类,继承于System.MulticastDelegate。
  2. 委托类中有三个点子和二个构造函数
  3. 寄托构造函数中需传入一个参数

 

为了更好的垂询委托的类,上面写了个简易的例子先来领会一下寄托的构造函数:

using System;

namespace delegate_test
{
    internal delegate int Sum(int a, int b);

    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // 实例化委托,这里和委托的IL代码中构造函数不一致
            // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型)
            /*
            .method public hidebysig specialname rtspecialname 
                instance void .ctor (
                        object 'object',
                        native int 'method'
                    ) runtime managed 
                {
                } // end of method Sum::.ctor
            */
            Sum s = new Sum(Add);

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }

        public static int Add(int a, int b)
        {
            return a+b;
        }
    }
}

在意代码的表明,大家看来C#中实例化委托的写法与IL中见到的嘱托的构造函数不相同等,

事实上那只是C#的语法糖,为了便于代码编写,将方面包车型客车代码编写翻译后,用ILSpy查看IL代码:

    IL_0000: nop
    IL_0001: ldstr "Hello World!"
    IL_0006: call void [mscorlib]System.Console::WriteLine(string)
    IL_000b: nop
    IL_000c: ldnull
    IL_000d: ldftn int32 delegate_test.Program::Add(int32, int32)
    IL_0013: newobj instance void delegate_test.Sum::.ctor(object, native int)

大家发现,实际运转时,依然组织了一个参数字传送给了寄托的构造函数,2个null,还有三个是函数Add的地方。

那里的首先个参数object为null的原故是,Add函数是static,不用实例化二个object来调用它。

上边构造1个非static的函数Add2来看看object的值是否还为null。

using System;

namespace delegate_test
{
    internal delegate int Sum(int a, int b);

    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // 实例化委托,这里和委托的IL代码中构造函数不一致
            // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型)
            /*
            .method public hidebysig specialname rtspecialname 
                instance void .ctor (
                        object 'object',
                        native int 'method'
                    ) runtime managed 
                {
                } // end of method Sum::.ctor
            */
            Sum s = new Sum(Add);
            // 非static函数 Add2
            Sum s2 = new Sum((new Program()).Add2);

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }

        public static int Add(int a, int b)
        {
            return a+b;
        }

        public int Add2(int a, int b)
        {
            return a+b;
        }
    }
}

Sum s2 = new Sum((new Program()).Add2); 那句对应的IL如下:

    IL_0019: newobj instance void delegate_test.Program::.ctor()
    IL_001e: ldftn instance int32 delegate_test.Program::Add2(int32, int32)
    IL_0024: newobj instance void delegate_test.Sum::.ctor(object, native int)

 

委托的Invoke函数正是我们实在运作委托时调用的函数,至于BeginInvoke和EndInvoke则是用以异步的动静。

using System;

namespace delegate_test
{
    internal delegate int Sum(int a, int b);

    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // 实例化委托,这里和委托的IL代码中构造函数不一致
            // IL代码中 Sum 的构造函数需要传入2个参数(一个object 类型,一个 native int类型)
            /*
            .method public hidebysig specialname rtspecialname 
                instance void .ctor (
                        object 'object',
                        native int 'method'
                    ) runtime managed 
                {
                } // end of method Sum::.ctor
            */
            Sum s = new Sum(Add);

            Sum s2 = new Sum((new Program()).Add2);

            // 调用委托的代码 s(5,7) 其实就是调用委托类的 Invoke方法
            Console.WriteLine("5+7="+ s(5,7));
            // 剥去语法外衣,也可以写成
            Console.WriteLine("5+7="+ s.Invoke(5,7));

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }

        public static int Add(int a, int b)
        {
            return a+b;
        }

        public int Add2(int a, int b)
        {
            return a+b;
        }
    }
}

s(5,7) 就是 s.Invoke(5,7) 的简化写法。

 

2.  委托链

委托既然是用来回调函数,那就应当能够三回回调多个函数,形成二个委托链。

以此委托链中应有还足以动态追加和削减回调函数。

实验代码如下:

using System;

namespace delegate_test
{
    internal delegate void Print_Sum(int a, int b);

    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Print_Sum s = null;            
            Print_Sum s1 = new Print_Sum(Add1);            
            Print_Sum s2 = new Print_Sum((new Program()).Add2);
            Print_Sum s3 = new Print_Sum((new Program()).Add3);

            s += s1;
            s += s2;
            s += s3;

            s(1,2);            
            // 委托链中删除Add2
            s -= s2;
            s(1,2);

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }

        public static void Add1(int a, int b)
        {
            Console.WriteLine("function Add1.... result is " + (a+b).ToString());
        }

        public void Add2(int a, int b)
        {
            Console.WriteLine("function Add2.... result is " + (2*(a+b)).ToString());
        }

        public void Add3(int a, int b)
        {
            Console.WriteLine("function Add3.... result is " + (3*(a+b)).ToString());
        }
    }
}

运行的结果也预期相符,其实个中的 s += s1;s -= s2;
也是C#的语法糖,实际调用的是System.DelegateCombine方法和Remove方法。

感兴趣的话,能够看编写翻译后的IL代码。

 

3. 动态委托

动态委托其实选取的并不多,它实际是和反光结合使用的。只有在您不能分明所调用的寄托所定义的参数个数和参数类型时,才须要用到动态委托。

选用动态委托主要就是行使 生成委托实例的 CreateDelegate 方法和
调用委托的 DynamicInvoke 方法。

using System;
using System.Reflection;

internal delegate void print_sum(int a, int b);
internal delegate void print_string(string a);

public class Dynamic_Delegate
{
    public static void Main(string[] args)
    {
        // 假设要调用的delegate名称,方法名称,方法的参数都是由 args传入的。
        // Main方法中并不知道要调用哪个委托
        // 调用委托print_sum时:
        // delegate_test.exe print_sum Sum 2 3
        // 调用委托print_string时:
        // delegate_test.exe print_string Str "hello delegate!"

        // 获取各个参数
        string delegate_name = args[0];
        string method_name = args[1];
        // 由于2种委托的参数不同,所以参数可能是一个,可能是两个
        object[] method_args = new object[args.Length - 2];
        for (int i = 0; i<args.Length-2; i++)
        {
            // print_sum的参数需要转换成int型
            if (delegate_name.Equals("print_sum"))
                method_args[i] = int.Parse(args[2+i]);
            else
                method_args[i] = args[2+i];
        }

        // 获取委托类型
        Type delegate_type = Type.GetType(delegate_name);
        // 获取方法信息
        MethodInfo mi = typeof(Dynamic_Delegate).GetMethod(method_name, BindingFlags.NonPublic | BindingFlags.Static);

        // 根据获取的委托类型和方法信息创建一个delegate            
        Delegate d = Delegate.CreateDelegate(delegate_type, mi);

        // 动态调用委托
        d.DynamicInvoke(method_args);

        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }

    private static void Sum(int a, int b)
    {
        Console.WriteLine("delegate print_sum....... result is : " + (a+b).ToString());
    }

    private static void Str(string a)
    {
        Console.WriteLine("delegate print_string.... result is : " + a);
    }
}

遵照编写翻译成的程序集(小编的次第集名称是delegate_test.exe),在命令行中分别输入如下命令来分别调用分歧的delegate:

delegate_test.exe print_sum Sum 2 3
delegate_test.exe print_string Str "hello delegate!"

相关文章