• 【C#基础】委托那些事儿(一)


    一、简单的委托

    1.1  委托的声明:

      C#当中,委托(delegate)是一种方法封装,也即委托对象可以作为一种传递方法的变量来使用。

      委托也算是一种类,与类是平级的存在。在类中写delegate对象当然是允许的,毕竟C#也允许类中类。但是一般不这样做,委托的声明最好与类声明平级。

      声明:

    public  delegate    void         ActionName();
    
            关键字       返回类型      委托变量名(函数参数)

      方法执行,有两种方式,效果相同:

      TestHandler.Invoke(实参);

      TestHandler(实参);

      示例:

     public delegate void TestHandler(string str);
     
     class Program
     {
         static void Main(string[] args)
         {
             TestHandler t = delegate (string str) { Console.WriteLine($"There will show : {str}"); };
             // TestHandler t = str => Console.WriteLine($"There will show : {str}");  lambda表达式,上行的语法糖。
             t.Invoke("Hello world !");
         }
     }

      在说到具体如何实现委托之前,先简单说明一下Action、Func是什么。

      C#规则中,Action和Func都是方法对象(委托类型),两者的区别仅在于有无返回类型。

    public delegate void Action();
    
    public delegate TResult Func<out TResult>();

      形参是允许添加多个的:

    Action<int, string, double> action
    /*============== F12 ================*/
    public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3);
    Func<string, string, string> func
    /*============== F12 ================*/
    public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); //(其中,TResult参数总写在形参说明的最后)
    // 注:尖括号表示里面是泛型,在声明中,需要在<>内写入具体的类型。

      实例化这个对象,这样写:

    Action<T1, T2> action = delegate(T1 arg1, T2 arg2) { 具体实现 };
    
     类型       变量名称  关键字   (方法入参)          {..}

      例如可以这样声明:

      Action<int, string, double> action = delegate(int a, string b, double c) { };

      Func<string, string, string> func = delegate { return "test.."; };

      注:入参可以省略不写,但是注意可能会有一些小陷阱,见下例[1]:

    // 启动新线程,.NET2.0有4个线程构造函数:
    
    public Thread(ParameterizedThreadStart start)
    
    public Thread(ThreadStart start)
    
    public Thread(ParameterizedThreadStart start, int maxStackSize)
    
    public Thread(ThreadStart start, int maxStackSize)
    
    // 涉及的两个委托类型是:
    
    public delegate void ThreadStart()
    
    public delegate void ParameterizedThreadStart(object obj)
    
    // 创建一个新线程的尝试:
    
    new Thread(delegate ()          {  //..});
    
    new Thread(delegate (object o)  {  //..});
    
    new Thread(delegate             {  //..});

    // 编译器将不知道第三行delegate省却的形参到底是哪个委托,因为它既可能是ThreadStart,又可以是ParameterizedThreadStart。

    1.2  订阅的容器 event

      首先,在此需要认同一个观点:

      属性不是字段——很多时候,属性是字段的包装器,保护字段不被滥用。而包装器永远不可能是包装的东西。

      知道如何写封装的方法后,接下来便是——如何去打包这些方法。即:如何把这些方法添加(或者说是整合)到某个对象上。这种“添加”,一般需要一个“盒子”,它的关键字名称叫作事件(event)。

      把方法装进事件当中,C#规定了这两个符号,

      ◆ += 表示“订阅”,-= 表示“退订”;

      ◆ 实例化后,该实例的事件成员只能出现在 += 或 -= 符号的左边。

      ◆ 只要订阅了,事件一旦执行,所有订阅的方法都会执行。

      姑且可以把事件当成一个方法集合(+= 看做Add, -= 看做Remove)。

      声明格式见下(关键字  delegate类型  事件变量名称  =  delegate {..}):

    public event EventHandler click = delegate {};

        ( * 上行省却的方法参数,编译器将参考”方法规则” )

      以上声明方法是赋值式的,即会替换掉该事件当前订阅的所有内容,此处要注意。

      示例:

        class Program
        {
            static void Main(string[] args)
            {
                Test a = new Test();
                a.TestEvent += delegate() { Console.WriteLine("First Action."); };
                a.TestEvent += SecondAction;
                a.TestEvent += () => { Console.WriteLine("Third Action."); };
                a.TestAction();
    Console.WriteLine(
    "================"); a.TestEvent -= SecondAction; a.TestAction(); } public static void SecondAction() { Console.WriteLine("Second Action."); } } class Test { public event Action TestEvent; public void TestAction() { if (TestEvent != null) TestEvent(); } }

      这个例子是不是非常眼熟,和控件的事件订阅是一样的。

      比如CheckBox的勾选事件:

      _checkBox.Checked += _checkBox_Checked;
     // TIPS:输入+=后长按Tab键,IDE将自动生成方法;+=后按一次Tab,自动生成lambda表达式方法。
    private void _checkBox_Checked(object sender, RoutedEventArgs e) { }

      同理鼠标点击事件、双击等等,只不过像是监听鼠标操作(点下左键、松开左键等等)的这一项交由C#来处理了。

       注意:如果多次初始化界面,同一变量再次订阅,将订阅多次哦。

    ==================================================================================

      下节预告:

      属性不是字段——很多时候,属性是字段的包装器,保护字段不被滥用。包装器永远不可能是包装的东西。

    注释:

    [1] 自《深入理解C#》(第3版)Jon Skeet 著  姚琪琳 译

    [2] 自 刘猛铁的C#学习视频

  • 相关阅读:
    【组合数学】 03
    【组合数学】 02
    【微积分】 10
    马未都说收藏:陶瓷篇(18、19)五彩瓷、斗彩
    马未都说收藏:陶瓷篇(1)历史朝代、陶器
    maven pom.xml常用标签 Exclusions plugins是什么意思
    Java日志记录工具SLF4J介绍
    Spring Context及ApplicationContext
    RabbitMQ中Queue详细介绍
    收藏专家马未都简介
  • 原文地址:https://www.cnblogs.com/carmen-019/p/10483525.html
Copyright © 2020-2023  润新知