C#基础之 - 初探委托

182 阅读1分钟
  • 本文已参与「新人创作礼」活动,一起开启掘金创作之路。

什么是委托?

委托在C#中应该是无处不在的,它是C#的精髓所在,我们可以将它理解为函数指针。

如何使用委托?

  • 定义委托
  • 创建委托实例,并指向一个方法
  • 调用
public class Program
{
    public delegate int CalcDelegate(int number1,int number2);
    static void Main(string[] args)
    {
        CalcDelegate catBehavior = Add;
        int calcResponse = catBehavior.Invoke(1,2);
        Console.WriteLine($"计算值为:{calcResponse}");
    }

    public static int Add(int number1, int number2)
    {
       return number1 + number2;
    }
}

使用委托有什么好处?

1. 代码复用

下面是实现一个类似计算器功能的类。

    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"计算值为:{Calc(1,2,Calcoperation.Add)}");
        }

        public static double Calc(double number1, double number2,Calcoperation calcoperation)
        {
            switch (calcoperation)
            {
                case Calcoperation.Add:
                    return Add(number1, number2);
                case Calcoperation.Subtract:
                    return Subtract(number1, number2);
                case Calcoperation.Multiply:
                    return Multiply(number1, number2);
                default:
                   return 0;
            }
        }

        public static double Add(double number1, double number2)
        {
           return number1 + number2;
        }
        public static double Subtract(double number1, double number2)
        {
            return number1 - number2;
        }
        public static double Multiply(double number1, double number2)
        {
            return number1 * number2;
        }

        public enum Calcoperation { Add,Subtract,Multiply,Divide }
    }

思考: 在这里如果添加一个算法,那么Switch需要加一个节点,然后需要加枚举,还要添加对应的方法?如果使用委托,直接讲算法当初参数传递会是什么样的呢?

 public class Program
    {
        public delegate double CalcDelegate(double number1, double number2);
        static void Main(string[] args)
        {
            Console.WriteLine($"计算值为:{Calc(1,2,Add)}");
        }

        public static double Calc(double number1, double number2, CalcDelegate calcDelegate)
        {
            return calcDelegate.Invoke(number1, number2);
        }

        public static double Add(double number1, double number2)
        {
           return number1 + number2;
        }
        public static double Subtract(double number1, double number2)
        {
            return number1 - number2;
        }
        public static double Multiply(double number1, double number2)
        {
            return number1 * number2;
        }
    }

2. 异步调用

委托的异步调用主要是靠BeginInvoke和EndInvoke实现的,下面请看代码。关于这两个方法的具体实现,大家可以去查阅源码,可能会让你的感受更加深刻

public class Program
{
    public delegate int CalcDelegate(int number1, int number2);
    static void Main(string[] args)
    {
        CalcDelegate catBehavior = Add;
        IAsyncResult result = catBehavior.BeginInvoke(1, 2, null, null);

        while (!result.IsCompleted) 
        {
            Console.WriteLine("Unfinished");
        }

        Console.WriteLine(catBehavior.EndInvoke(result));
    }

    public static int Add(int number1, int number2)
    {
        Thread.Sleep(2000);
        return number1 + number2;
    }
}

我这里的Add方法里面使线程休眠了2s,如果在同步方法里面,用户需要等待执行结果这样的体验是非常不好的。
在这里可以看出我们使用BeginInvoke执行了异步委托,使用EndInvoke获取异步的结果。
思考: 这里如果异步线程一直未完成,EndInvoke将一直阻塞调用线程,直到到异步调用完成?

3. 回调

上面说到如果异步线程未完成,我们应该怎么处理呢?大家可以看到我上面代码中使用的BeginInvoke方法我填入了两个NULL值。

image.png

通过上图可以看出来,BeginInvoke方法里面后面的两个参数是AsyncCallback,Object类,AsyncCallback主要是用于通知异步执行的回调,Object主要是用于向回调传递的参数。

    public class Program
    {
        public delegate int CalcDelegate(int number1, int number2);
        static void Main(string[] args)
        {
            CalcDelegate catBehavior = Add;
            string message = "传递消息!";
            IAsyncResult result = catBehavior.BeginInvoke(1, 2,new AsyncCallback(CallBack), message);

            for (int i = 0; i < 6; i++)
            {
                Thread.Sleep(1000);
            }
        }

        public static int Add(int number1, int number2)
        {
            Thread.Sleep(3000);
            return number1 + number2;
        }

        public static void CallBack(IAsyncResult async) 
        {
            AsyncResult asyncResult = (AsyncResult)async;

            CalcDelegate calc = (CalcDelegate)asyncResult.AsyncDelegate;

            int calcResult = calc.EndInvoke(asyncResult);
            string message = asyncResult.AsyncState.ToString();

            Console.WriteLine($"异步委托执行结果值为:{calcResult},异步委托最后携带的参数为:{message}");
        }
    }

给大家看下最后的执行结果

image.png

总结

关于线程问题,我们可以放到后续的文章中再来交流,本文只是我的一些个人观点,如果有写的不好的地方,还请大家多多指正。这里也只是委托的一小部分基础,关于委托在我们平时的代码里也是时常可见,声明委托也有许多种方式,如Action是无参无返回值的委托 Func是无参有返回值的委托等等... 委托还有非常多的用法,感兴趣的朋友可以多去了解了解。