当前位置: 代码迷 >> C# >> C#学习之步步高(2)认识到熟悉委托
  详细解决方案

C#学习之步步高(2)认识到熟悉委托

热度:81   发布时间:2016-05-05 04:15:13.0
C#学习之步步高(二)认识到熟悉委托
大家好,这是本系列的第二篇文章,今天我给大家带来的是C#当中委托部分。

那么先讲讲委托吧,委托是一种定义方法签名的类型,当实例化委托时,您可以将其实例与任何具有兼容签名的方法相关联。 您可以通过委托实例调用方法。

使用委托的一个好处就是像操作变量一样操作一个方法。

下面是委托的一些特点:

  • 委托类似于 C++ 函数指针,但它们是类型安全的。
  • 委托允许将方法作为参数进行传递。
  • 委托可用于定义回调方法。
  • 委托可以链接在一起;例如,可以对一个事件调用多个方法。
  • 方法不必与委托签名完全匹配。

    下面 是定义个名为MyDelegate的委托,其参数类型为object,返回值为void

        public delegate void MyDelegate(object obj);

这行代码就是用来声明一个委托类型。再看如下代码:

    class Program    {        public delegate void MyDelegate(object obj);        static void Main(string[] args)        {            // 在这儿,把MyDelegate看做一个类(其实它就是一个类class)            // 下面这行代码和声明一个对象一模一样,因为mydelegate它就是一个对象            // 注意Method1的原型和MyDelegte一致才行(逆变和协变的内容在后面)            // 其实下面这行代码可以简写为:MyDelegate mydelegate = Method1;            MyDelegate mydelegate = new MyDelegate(Method1);            // 下面这行代码和mydelegate.Invoke(new object());没有任何区别!            // 这行代码的目的就是执行Method1方法            mydelegate(new object());        }        // 方法 i         public static void Method1(object o)        {            Console.WriteLine("Method1 Calling");        }    }


运行输入结果:Method1 Calling.

那么委托时如何像一个参数一样传给别的方法呢?下面这段代码用委托回调的方式开求1~100的和:

    class Program    {        public delegate void CallBack(int number);        static void Main(string[] args)        {            // 这个函数调用后,将输出1~100的和            Execute(100, Sum);        }        private static void Execute(int param, CallBack callBack)        {            if (callBack != null)            {                // 在这个地方调用callBack委托                callBack(param);            }        }        private static void Sum(int number)        {            int sum = 0;            for (int i = 0; i <= number; i++)            {                sum += i;            }            // 为了做演示,就暂且用这种最复杂的方法求和吧            Console.WriteLine("1~" + number + "的和为" + sum);        }    }

程序运行结果:1~100的和为5050。

那么就有人要说了,我像调用一次Execute方法,求出1~100的和之外还求出他们的所有质数之和怎么做呢,那么就可以使用委托将多个方法链接到一起了:

    class Program    {        public delegate void CallBack(int number);        static void Main(string[] args)        {            // 这个函数调用后,将输出1~100的和还有1~100之间质数的和            CallBack linkDelegate = Sum;            // 使用+=运算符,将2个方法链接一起            linkDelegate += SumOfPrimeNumber;            Execute(100, linkDelegate);        }        private static void Execute(int param, CallBack callBack)        {            if (callBack != null)            {                // 在这个地方调用callBack委托                callBack(param);            }        }        private static void Sum(int number)        {            int sum = 0;            for (int i = 0; i <= number; i++)            {                sum += i;            }            // 为了做演示,就暂且用这种最复杂的方法求和吧            Console.WriteLine("1~" + number + "的和为" + sum);        }        private static void SumOfPrimeNumber(int number)        {            int sum = 0;            for (int i = 0; i <= number; i++)            {                if (IsPrime(i))                {                    sum += i;                }            }            Console.WriteLine("1~" + number + "之间的所有质数的和为" + sum);        }        // 判断一个数字是否为质数        private static bool IsPrime(int n)        {            for (int i = 2; i * i <= n; i++)            {                if (n % i == 0)                {                    return false;                }            }            return true;        }    }

这段代码输出的结果为:1~100的和为5050。1~100之间的所有质数的和为1061。


下面就来讲讲委托的2个重点:逆变和协变。
  • 逆变:在使用委托中,可以将方法参数从他基类修改为他的派生类。
  • 协变:在使用委托中,可以将方法返回值从他派生类修改为他的基类。
下面是一个逆变的例子:
    class Program    {        public delegate void Contravariance(string str);        static void Main(string[] args)        {            Contravariance md = Method1;            md("Contravariance");        }        static void Method1(object obj)        {            Console.WriteLine(obj + " Method1 Calling");        }    }

尽管Method1的原型和Contravariance委托定义的不一致,但是对于参数而言,Method1中的object是Contravariance中的string的基类,所以上述代码还是能正常运行,这个就是逆变。

不过得注意下面这个例子:
    class Program    {        public delegate void Contravariance(int str);        static void Main(string[] args)        {            // 注意下面这行代码,虽然int是object的派生类型,但是下面这行代码还是会导致编译不过,因为逆变和协变对值类型不起作用(int为值类型)            Contravariance md = Method1;            md(4);        }        static void Method1(object obj)        {            Console.WriteLine(obj + " Method1 Calling");        }    }
上述代码是无法通过编译的,在使用逆变和协变中是都不支持值类型的。

同理,下面这个是逆变的例子:

    class Program    {        public delegate object Convariance();        static void Main(string[] args)        {            // 虽然Convariance委托定义的类型返回值和Method1不一致,但是这段代码是可以正常运行的            Convariance md = Method1;            md();        }        static string Method1()        {            return "Method1";        }    }

我们看一看如下代码:

    public delegate void TryCode(object userData);    public delegate void WaitCallback(object state);    public delegate void TimerCallback(object state);    public delegate void ParameterizedThreadStart(object obj);
发现这些委托定义的共同点了吗?是不是都一模一样?事实上在.NET Framework上可以使用委托泛型,上面所有的泛型其实都可以仅使用:

    public delegate void Action<in T>(T obj);
这一个委托定义来实现就可以了。在System命名空间下有这种多达17个Action委托(这儿使用in关键字表示委托类型支持逆变):

    public delegate void Action();    public delegate void Action<in T1>(T1 arg1);    public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);    ...

如果是需要有参数的委托的话,可以使用如下委托:

    public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
同样的System命名空间下也有17个Func的委托(使用out关键字表示委托泛型支持协变):

    public delegate TResult Func<out TResult>();    public delegate TResult Func<in T1, out TResult>(T1 arg1);    public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);    ...


最后是文本的关键点,为大家揭秘委托。

我们再看如下一行代码:

    internal delegate void Feedback(int value);
其实它被编译后,看起来像如下一个完整的类:

    internal class Feedback : System.MulticastDelegate    {        // 构造方法        public Feedback(object obj, IntPtr method);        // 这个方法的原型和委托定义的一模一样        public virtual void Invoke(int value);        // 一下方法实现了对回调方法的异步调用        public virtual IAsyncResult BeginInvoke(int value, AsyncCallback callback, Object obj);        public virtual void EndInvoke(IAsyncResult result);    }

也就是说其实委托它本身也是一个类,其继承关系为:自定义委托->MulticastDelegate->Delegate->Object。


本文内容就讲到这里了,谢谢各位。

  相关解决方案