zoukankan      html  css  js  c++  java
  • c# delegate知识

    一、引用方法

    委托是寻址方法的.NET版本。委托是类型安全的类,它定义了返回类型和参数的类型。委托是对方法的引用,也可以对多个方法进行引用,委托可以理解为指向方法地址的指针。

    如:delegate int ReturnIntHandler(int a,int b);//int是返回类型,a和b是引用类型,这是委托执行的方法必须满足如下格式:int method(int param1,int param2);

    二、委托

    当要把方法传递给其它方法时,需要使用委托。委托是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。

    1、声明委托

    委托使用关键字 delegate 进行定义。

    定义委托基本上就是定义一个新类,所以可以在定义类的任何相同地方定义委托。可以在委托定义上应用常见的访问修饰符:public、private、protected等。其访问作用域也雷同于类。

    2、使用委托

    为了减少输入量,只需要委托实例,就可以只传递地址的名称。这称为委托推断。

    delegate int CalculateMethodInvoker(int x, int y);
        class Program
        {
            static void Main(string[] args)
            {
                //创建委托对象
                CalculateMethodInvoker calculateMethodInvoker = CalculateHelper.Sum;
                //等同于CalculateMethodInvoker calculateMethodInvoker = new CalculateMethodInvoker(CalculateHelper.Sum);//委托的实例化,指向Sum方法,其实委托也是可以定义,实例化调用的,不只是lambda表达式的调用方式
                int x = 100, y = 200;
                Console.WriteLine("x,y相加:{0}", Calculate(calculateMethodInvoker, x, y));
                calculateMethodInvoker = CalculateHelper.Multiply;
                Console.WriteLine("x,y相乘:{0}", Calculate(calculateMethodInvoker, x, y));
                Console.ReadKey();
            }
            public static int Calculate(CalculateMethodInvoker calculateMethodInvoker, int x, int y)
            {
                //return calculateMethodInvoker(x, y);
                //return calculateMethodInvoker.Invoke(x, y);//是不是当前线程都可以
                IAsyncResult result = calculateMethodInvoker.BeginInvoke(x, y, null, calculateMethodInvoker);//异步,这里只是做展示,EndInvoke类似于async中的await,这里不能实现异步效果
                return calculateMethodInvoker.EndInvoke(result);
            }
    
        }
        public class CalculateHelper
        {
            public static int Sum(int x, int y)
            {
                return x + y;
            }
            public static int Multiply(int x, int y)
            {
                return x * y;
            }
        }

    3、Action<T>和Func<T>委托

    除了为每个参数和返回类型定义一个新的委托类型外,还可以使用Action<T>和Func<T>委托。

    泛型Action<T>委托表示引用一个void返回类型的方法,没有泛型参数的Action类可调用没有参数的方法,如Action 等价于 delegate void mydelegate;  Action<int,int>等价于delegate void mydelegate(int param1,int param2);

    泛型Func<T>委托表示引用一个有返回值的方法,泛型的最后一个参数时Func的返回值类型,如Func<int,int,bool>,等价于delegate bool mydelegate(int param1,int param2);

    4、多播委托

    委托也可以包含多个方法。这种委托成为多播委托。如果调用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果。多播委托识别运算符“-”、“+”、“-=”、“+=”以从委托中增加或删除方法调用。

    如:

    class Program
        {
            static void Main(string[] args)
            {
                Action<int, int> calFunc = CalculateHelper.Sum;
                calFunc += CalculateHelper.Multiply;//多播加
                int x = 100, y = 200;
                Calculate(calFunc, x, y);
                calFunc =calFunc- CalculateHelper.Multiply;//多播减
                Calculate(calFunc, x, y);
                Console.ReadKey();
            }
            public static void Calculate(Action<int, int> calculateMethodInvoker, int x, int y)
            {
                Console.WriteLine("运行结果:");
                //calculateMethodInvoker(x, y);
                foreach (Action<int, int> item in calculateMethodInvoker.GetInvocationList())//遍历,这里需要转为为当前类型委托
                {
                    try
                    {
                        item(x, y);//执行委托指向方法
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }

    如果通过委托调用的其中一个方法抛出异常,整个迭代就会停止。解决的方法是,使用Delegate类中定义的GetInvocationList()方法获取Delegate对象数组,再使用循环遍历执行,在过程中捕获异常,来继续下一次迭代。

    5、匿名方法

    匿名方法是用作委托的参数的一段代码。

    如:

    Action<int, int> calFunc = delegate (int i, int j)
    {
        Console.WriteLine("x,y相加:{0}", i + j);
    };

    在匿名方法中不可使用跳转语句(break、goto或continue),在匿名方法内部不能访问不安全代码,不能访问在匿名方法外部使用的ref和out参数。

    三、lambda表达式

    C#3.0后,可以使用lambda把实现代码赋予委托,只要有委托参数类型的地方,就可以使用lambda表达式。

    如:

    Action<int, int> calFunc = (i, j) =>
    {
        Console.WriteLine("x,y相加:{0}", i + j);
    };

    1、参数

    lambda表达式有几种定义参数的方式。如果只有一个参数,只写出参数名就足够了。如果除一个参数以外,需要圆括号把参数名括起来。

    例子:

    Action<int> one = i =>
    {
        //method body
    };
    Action<int, int> two = (i, j) =>
    {
        //method body
    };

    2、多行代码

    如果lambda表示只有一条语句,在方法块内就不需要花括号和return语句,因为编译器会隐式添加return。

    如:

    Func<int> lambdaOne = () => 0;

    如果实现代码超过一行,就需要使用return语句显式返回。

    如:

    {
        int i = 0;
        i++;
        ++i;
        return i;
    };

    3、闭包

    通过lambda表达式可以访问lambda表达式块外部的变量。这称为闭包。

    如:

    int param = 10;
    Action<int> lambdaSecond = (i) =>
    {
        Console.WriteLine(i + param);
    };
    lambdaSecond(3);
    Console.ReadKey();

    四、事件

    事件基于委托,为委托提供了一种发布/订阅机制。

    如:

    class Program
        {
            static void Main(string[] args)
            {
                AlarmClock alarmClock = new AlarmClock();
                Student zsStudent = new Student("张三");
                alarmClock.ItsGetUpClockEvent += zsStudent.ItsGetUpClock;
                alarmClock.ItsGetUpClock();
                Student lsStudent = new Student("李四");
                //WeakEventManager<AlarmClock, EventArgs>.AddHandler(alarmClock, "ItsGetUpClockEvent", lsStudent.ItsGetUpClock);
                ////弱事件,System.Windows,WPF中经常用到
                //alarmClock.ItsGetUpClock();
                Console.ReadKey();
            }
    
        }
        //事件发布类
        public class AlarmClock
        {
            public event EventHandler<EventArgs> ItsGetUpClockEvent;
            public void ItsGetUpClock()
            {
                Console.WriteLine("时间到,起床了!");
                ItsGetUpClockEvent?.Invoke(this, new EventArgs());//判断是否订阅事件
            }
        }
        //事件侦听类
        public class Student
        {
            public string Name { get; set; }
            public Student(string name)
            {
                this.Name = name;
            }
            public void ItsGetUpClock(object sender, EventArgs e)
            {
                Console.WriteLine("{0}关掉闹钟,起床了。", Name);
            }
        }

    事件最常用的地方是Winform和Wpf窗体中,而Invoke的经典使用场景如下(非当前线程更改窗体文本):

     private void ShowExecLog(string log)
            {
                if (this.richTextBox1.InvokeRequired)//判断是否是当前线程
                {
                    this.richTextBox1.Invoke(new ShowLogHandler(ShowLog), log);
                }
                else
                {
                    this.richTextBox1.Text += log;
                }
            }
    
            public void ShowLog(string log)
            {
                this.richTextBox1.Text += log;
            }
    
    
            public delegate void ShowLogHandler(string log);
  • 相关阅读:
    python库--pandas--DataFrame
    python库--pandas--Series
    python模块--collections(容器数据类型)
    Linux命令
    pycharm安装
    利用Anaconda进行python爬虫环境的配置-安装scrapy
    反射
    异常处理
    类的相关判断函数
    python random
  • 原文地址:https://www.cnblogs.com/lcawen/p/6645358.html
Copyright © 2011-2022 走看看