Bootstrap

C#编程学习24:委托、事件与反射机制

目录

1 委托

1.1  声明委托类型

1.2 向委托列表中添加方法列表

1.3 将方法作为传递参数

2 事件

2.1 基本原理分析

2.2  一个监听键盘空格键被按下的示例程序源码

3 标准事件委托

3.1 标准事件与委托

3.2 监听键盘按键的源码示例

4 反射机制

4.1 使用DotNetUitilities库的示例

4.2 var关键字


1 委托

类似于C语言中的函数指针,C#也可以将函数当作方法传给其他方法

1.1  声明委托类型

定义委托与定义其他方法类似,不过需要使用关键字delagate关键字

public int delegateDemo(int num1, int num2);//与该委托匹配的方法必须是返回值为int,参数列表为(int, int)

1.2 向委托列表中添加方法列表

委托类型的隐含公共基类:System.Delagate和System.MulticastDelegate(多播委托)

public int addDemo(int num1, int num2) {return num1 + num2;}
public int subDemo(int num1, int num2) {return num1 - num2;}
public int mulDemo(int num1, int num2) {return num1 * num2;}
public int divDemo(int num1, int num2) {return num1 / num2;}

委托的实例化:【其中的met2就是多播委托】

//new关键字实例化
method met1 = new method(addDemo);
met1(10,200);//会调用addDemo
//直接实例化
method met2 = subDemo;
//添加委托
met2 += mulDemo;
met2 += divDemo;
met2(3,4);//会调用subDemo、mulDemo和divDemo
//移除委托
met2 -= mulDemo;
met2(5,6);//会调用subDemo和divDemo

多播委托被调用,会调用与之关联的所有方法;

1.3 将方法作为传递参数

当将委托当作参数传递某个方法时,是值类型的传递;在方法内部对引用的委托实例进行变更时,并不会反应到原来的委托实例中

public delegate void myDe();
public void test1()
{
    Console.WriteLine("方法一");
}
public void test2()
{
    Console.WriteLine("方法二");
}
public void testDemo(myDe de)
{
    if(de != null) {de();}
    de = test2;    
}

//调用
myDe de = test1;
testDemo(de);
de();

可以总结出委托使用的三个关键点:定义委托,委托实例化,委托调用

2 事件

2.1 基本原理分析

事件是委托类型,要声明事件就需要定义事件封装类型的委托;然后在类中使用event关键字,同时为了让派生类可以重写该事件,常将其定义为protected类型。习惯上,事件的命名方式为On<事件名>

以学生听到铃声就知道是下课为例,说明事件与委托的关系:铃声响,代表事件发生;学生知道下课就是对事件的响应

事件使用的要点:

  1. 定义一个事件封装类型的委托【delegate关键字】
  2. 声明委托类型的事件【event关键字】
  3. 定义引发事件的方法On<事件名>
  4. 定义事件被调用的方法
  5. 将方法添加到事件处理方法列表中
  6. 事件被调用

2.2  一个监听键盘空格键被按下的示例程序源码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace delegateEvent
{
    class Program
    {             
        //事件响应函数
        private static void app_SpaceKeyDown()
        {
             Console.WriteLine(DateTime.Now.ToLocalTime().ToString() + "空格键被按下");
        }

        static void Main(string[] args)
        {
            myApp app = new myApp();
            app.SpaceKeyDown += app_SpaceKeyDown;
            app.StartRun();
        }

        
    }
    //定义封装委托类型的类
    class myApp
    {
        //定义一个委托类型
        public delegate void SpaceKeyPressedEventHander();

        //声明委托类型的事件
        public event SpaceKeyPressedEventHander SpaceKeyDown;

        //引发事件的方法
        protected void OnSpaceKeyDown()
        {
            if (this.SpaceKeyDown != null)
            {
                SpaceKeyDown();
            }
        }
        public void StartRun()
        {
            while (true)
            {
                ConsoleKeyInfo key = Console.ReadKey();
                if (key.Key == ConsoleKey.Spacebar)
                {
                    OnSpaceKeyDown();
                }
                if (key.Key == ConsoleKey.Escape)
                {                  
                    break;
                }
            }
        }        
    }
}

3 标准事件委托

3.1 标准事件与委托

标准事件委托签名

第一个参数为引发事件的对象本身,第二参数为与事件相关的数据

带有泛型的事件处理委托

TEventArgs应为System.EventArgs类型或其派生类型

使用要点:

  • 定义System.EventArgs派生类型的事件,指定事件的参数
  • 定义引发事件的方法【On<事件名>】
  • 定义调用事件的方法
  • 定义与事件处理方法(object sender, ***EventArgs e)
  • 向事件中添加关联方法
  • 调用事件

3.2 监听键盘按键的源码示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace delegateEvent
{
    class Program
    {             
        private static void meapp_SpaceKeyDown(object sender, keyPressedEventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToLocalTime().ToString() +  e.Key.ToString() + "被按下");
        }
        static void Main(string[] args)
        {
            myEventApp meapp = new myEventApp();
            meapp.SpaceKeyPressed += meapp_SpaceKeyDown;
            meapp.StartRun();
        }

        
    }


    class keyPressedEventArgs : EventArgs
    {
        public keyPressedEventArgs(ConsoleKey key)
        {
            Key = key;
        }
        public ConsoleKey Key
        {
            get;
            private set;
        }
    }

    class myEventApp
    {
        public event EventHandler<keyPressedEventArgs> SpaceKeyPressed;
        //引发事件的方法
        protected void OnSpaceKeyPressed(keyPressedEventArgs e)
        {
            if (this.SpaceKeyPressed != null)
            {
                this.SpaceKeyPressed(this, e);
            }
        }

        public void StartRun()
        {
            while (true)
            {
                ConsoleKeyInfo key = Console.ReadKey();            
                if (key.Key == ConsoleKey.Escape)
                {
                    break;
                }
                OnSpaceKeyPressed(new keyPressedEventArgs(key.Key));
            }
        }
    }
}

4 反射机制

.Net Framework 中提供了反射机制,可以再加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息。

在程序集中,包含模块(Module),模块包含类型,类型包含成员,提供反射,我们可以查看到一个程序集的路径,命名空间,类。我们还可以对其进行操作。可以对程序集的类进行实例化,掉用类中的方法等,就跟我们普通使用程序集一样。

可以使用程序集中的私有成员,引用程序集的方式实现不了这一点

 

4.1 使用DotNetUitilities库的示例

 

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace reflect
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string dllName = "Core.Common";//指定dll程序集的名字,放在exe的路径下
            string fullName = "Core.Common.PinYinUtil";//指定要使用的dll中定义的某个类
            Assembly assembly = Assembly.Load(dllName);//程序集加载
            Type type = assembly.GetType(fullName);//从程序集中获取对象类型
            var dll = Activator.CreateInstance(type);//创建构造函数

            //参数1:方法的名字;参数2:非构造函数、共有、静态;参数5:方法的参数列表
            string pinyin = type.InvokeMember("CHSToPinyin", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { textBox1.Text.Trim(), true }).ToString();
            MessageBox.Show(pinyin, "转换结果");
        }
        private void button2_Click(object sender, EventArgs e)
        {
            string dllName = "musicPlayer";//指定dll程序集的名字,放在exe的路径下
            string fullName = "musicPlayer.Form1";
            Assembly assembly = Assembly.Load(dllName);//程序集加载
            Form frm = assembly.CreateInstance(fullName) as Form;//创建构造函数
            frm.ShowDialog();
        }
    }
}

4.2 var关键字

隐式类型,在.Net 3.0之后才引入的,编译器可自动判断数据类型

参考资料:

C#超越菜鸟第十课:反射机制

 

 

 

 

;