11Net基础加强十一天-委托 事件 反射

复习委托

1.委托是一种数据类型,像类一样的数据类型,一般都是直接在命名空间中声明。
2.定义委托时,要指明返回值类型、委托名、参数列表,这样就能确定这个类型的委托能够存储什么样的方法。
3.委托是一个引用类型,当声明一个委托变量之后,如果不赋值,则这个变量的值为null,所以在使用委托类型的变量前最好做一次非空校验。另外一个委托类型的变量只能赋值一个委托类型的对象。

练习

        private void Form1_Load(object sender, EventArgs e)
        {
            string str = "这里有一个邮箱1911499587@qq.com你拿去用吧";
            //将替换的内容用一个委托来实现(就是传一个方法,用这个方法的返回值来当做替换的内容)
            str = Regex.Replace(str, @"(\w+)(@\w+([.]\w+)+)", GetReplace, RegexOptions.ECMAScript);
            MessageBox.Show(str);
        }
        //定义一个方法,根据匹配到的结果返回一个字符串
        public string GetReplace(Match match)
        {
            string group1 = match.Groups[1].Value;//邮箱用户名
            string group2 = match.Groups[2].Value;//邮箱域名
            StringBuilder sb = new StringBuilder();
            //将邮箱的用户名部分全部替换成*
            for (int i = 0; i < group1.Length; i++)
            {
                sb.Append("*");
            }
            return sb.ToString() + group2;
        }

lambda表达式
lambda表达式实际上就是一个匿名函数,表达式的参数列表中不能出现数据类型
lambda表达式语法:MyDelegate md=(参数列表)=>{具体的方法实现;};

//声明一个含有一个参数的委托
    public delegate string GetUpperDelegate(string str);
    //声明一个含有多个参数的委托
    public delegate int GetSumDelegate(int n1, int n2, int n3);
    //声明一个参数为数组的委托
    public delegate int GetArraySumDelegate(params int[] arr);
 //lambda表达式,在lambda表达式中不能出现数据类型
            GetUpperDelegate gud = str =>//当参数列表中只有一个参数的时候,可以省略括号
            {
                return str.ToUpper();
            };
            MessageBox.Show(gud("hello"));
            GetSumDelegate gsd = (n1, n2, n3) => {
                return n1 + n2 + n3;
            };
            MessageBox.Show(gsd(1, 2, 3).ToString());
            GetArraySumDelegate gasd = arr => {
                int sum = 0;
                for (int i = 0; i < arr.Length; i++)
                {
                    sum += arr[i];
                }
                return sum;
            };
            MessageBox.Show(gasd(new int[] { 1, 2, 3, 4, 5, 6 }).ToString());

系统为我们定义好的三种委托类型Action、Action<>、Func<>

其实在开发过程中我们不需要自己手动去写委托,因为系统内已经帮忙我们写好了能够通用的委托

        //系统内定义好的一个无返回值,无参数的委托Action
        Action ac = () => { MessageBox.Show("这是系统定义的的无参数无返回值的一个委托类型"); };
        ac();
        //系统内定义的好的一个无返回值,但是参数可以重载的泛型委托
        Action<int, int, int> ac2 = (n1, n2, n3) => { MessageBox.Show((n1 + n2 + n3).ToString()); };
        ac2(1, 2, 3);
        //系统内定义好的一个有返回值,并且参数可以重载的泛型委托Func<T1,T2,T3.....TResult>
        //Func<T1,T2,T3.....TResult>参数列表中最后一个参数是返回值,所以在匿名函数中定义形参或给参数赋值时,最后一个参数不需要赋值
        Func<int, int, int, int> func = (n1, n2, n3) => { return n1 + n2 + n3; };
        int sum = func(1, 2, 3);
        MessageBox.Show(sum.ToString());

List集合中的where方法,使用lambda表达式来实现

        //筛选出数字集合中的大于4的数字
        List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 };
        //此时where方法中需要传入一个Func<int,bool>类型的泛型委托,第一个参数是int类型,第二个参数是返回值类型bool
        IEnumerable<int> ie = list.Where(x => { return x > 4; });
        foreach (int item in ie)
        {
            MessageBox.Show(item.ToString());
        }
        //筛选出字符串集合中的长度大于4的集合
        List<string> list2 = new List<string>() { "1", "22", "33333", "33434343" };
        //此时where方法中需要传入一个Func<string,bool>类型的泛型委托,第一个参数是string类型,第二个参数是返回值类型bool
        IEnumerable<string> ie2 = list2.Where(str => { return str.Length > 4; });
        foreach (string  item in ie2)
        {
            MessageBox.Show(item);
        }

多播委托

多播委托:多播委托就是将多个不同的方法同时指向同一个委托对象
委托实际上就是一个类,当我们自定义一个委托的时候,编译器就帮助我们将委托生成了一个密封类,所以所有的委托的父类都是Delegate这个类

        Action<string> action = M1;//第一次给委托赋值时只能用=,不能使用+=
        action += M2;//绑定M2方法
        action += M3;//绑定M3方法
        action += M4;//绑定M4方法
        action -= M1;//不仅可以绑定方法,还可以解绑方法
        action("gaga");

事件

1.“事件”实际上就是“委托”,只不过是委托使用起来不够安全,所以才有了事件
2.委托为什么不安全:因为如果委托变量被定义为public,那么在外部就能直接调用委托了,这样很不安全,但如果委托变量被定义为private,那么我们在外部给委托变量赋值的时候也无法赋值了。所以,才有了event事件这个东西来弥补委托的不足,而事件实际上是封装成了一个“私有的委托”+“add方法”+“remove方法”。所以,这就是为什么事件不能在外部调用只能在外部赋值,只能在定义它的类的内部调用的原因。
3.事件的定义也很简单,使用event关键字,就是在定义委托变量的基础上添加一个event关键字,例如:public event Action AfterStartPlay;,事件的使用方法和委托的使用方法一致,也是使用lambda表达式来进行赋值,唯一不同的是“事件”只能使用+=和-=来进行赋值。

使用事件模拟音乐播放

 //定义一个播放音乐的类
    public class PlayMusic
    {
        //定义一个音乐开始播放后触发的事件
        public event Action AfterStartPlay;
        //定义一个音乐结束播放之前触发的事件
        public event Action BeforeEndPlay;
        //定义一个开始播放音乐的方法
        public void StartPlay()
        {
            MessageBox.Show("开始播放音乐.....");
            //执行音乐开始播放后触发的事件处理程序
            if (AfterStartPlay != null)
            {
                AfterStartPlay();
            }
            Thread.Sleep(3000);//让当前线程等待一会
        }
        //定义一个结束播放音乐的方法
        public void EndPlay()
        {
            //执行结束音乐播放之前触发的事件处理程序
            if (BeforeEndPlay != null)
            {
                BeforeEndPlay();
            }
            MessageBox.Show("音乐播放已结束.....");
        }
    }
            //创建一个播放音乐的对象
            PlayMusic pm = new PlayMusic();
            //在赋值时事件和委托的区别是,事件只能使用+=和-=进行赋值
            pm.AfterStartPlay += () => {
                MessageBox.Show("开始加载歌词......");
            };
            pm.AfterStartPlay += () => {
                MessageBox.Show("开始加载动感背景......");
            };
            pm.BeforeEndPlay += () => {
                MessageBox.Show("开始删除歌词......");
            };
            pm.BeforeEndPlay += () => {
                MessageBox.Show("开始删除动感背景......");
            };
            pm.StartPlay();//开始播放
            pm.EndPlay();//播放完成

自定义用户登录控件,登录校验使用事件来实现
自定义登录控件

 public partial class UserControlLogin : UserControl
    {
        //声明一个用户登录账户和密码校验的事件
        public event Action<object, UserLoginChechoutEventArgs> UserLoginChechout;
        public UserControlLogin()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            if (UserLoginChechout != null)//对事件做非空校验
            {
                UserLoginChechoutEventArgs evtArgs = new UserLoginChechoutEventArgs();
                evtArgs.IsOk = false;//在未做校验之前为false
                evtArgs.UserName = txtName.Text.Trim();//得到用户名
                evtArgs.Password = txtPassword.Text.Trim();//得到用户密码
                UserLoginChechout(this, evtArgs);
                if (evtArgs.IsOk)
                {
                    MessageBox.Show("登陆成功!");
                }
                else
                {
                    MessageBox.Show("登录失败!");
                }
            }
        }
    }
    //声明一个用户登录检验事件的参数类
    public class UserLoginChechoutEventArgs
    {
        //声明一个用来存放用户名的属性
        public string UserName
        {
            get;
            set;
        }
        //声明一个用来存放密码的属性
        public string Password
        {
            get;
            set;
        }
        //声明一个用来标记是否校验成功的属性
        public bool IsOk
        {
            get;
            set;
        }
    }

使用登录控件

        private void Form1_Load(object sender, EventArgs e)
        {
            userControlLogin1.UserLoginChechout += UserControlLogin1_UserLoginChechout;
        }
        //用户登录校验的事件处理程序
        private void UserControlLogin1_UserLoginChechout(object arg1, UserLoginChechoutEventArgs arg2)
        {
            if (arg2.UserName == "admin" && arg2.Password == "admin")
            {
                arg2.IsOk = true;
            }
        }

程序集

程序集是.Net中的特有的名词,在.net中生成的exe和dll文件都叫做程序集,在c语言中生成那不叫程序集,没办法通过reflector工具进行反编译。
程序集包含:类型元数据,程序集元数据,IL代码,资源文件

反射

反射就是动态加载程序集,获取里面的类型,并创建对象,调用他们的成员。
Type类就是类型元数据的类,使用这个类能得到所有类型的元数据

       //第一种获取元数据的方法,通过实例化的对象来获取元数据
        MyClass1 mc = new MyClass1();
        Type t1 = mc.GetType();//使用对象获取元数据

        //第二种获取元数据的方法,通过typeof()方法来获取
        Type t2 = typeof(MyClass1);
         //从元数据中获取该类型的所有字段(只能获取非私有字段,想要获取私有字段还得通过其他方法)
            FieldInfo[] fields = t2.GetFields();
            for (int i = 0; i < fields.Length; i++)
            {
                MessageBox.Show(fields[i].ToString());
            }
            //从元数据中获取该类型的所有属性
            PropertyInfo[] props = t2.GetProperties();
            foreach (PropertyInfo item in props)
            {
                MessageBox.Show(item.ToString());
            }
            //从元数据中获取该类型的所有方法
            MethodInfo[] methods = t2.GetMethods();
            foreach (MethodInfo item in methods)
            {
                MessageBox.Show(item.ToString());
            }

动态加载程序集及反射

            //动态加载程序集
            Assembly asm = Assembly.LoadFile(@"C:\Users\lenovo\Documents\visual studio 2015\Projects\11Net基础加强十一天-委托 事件 反射\_自定义程序集01\bin\Debug\_自定义程序集01.dll");
            //GetTypes()方法是从程序集实例中得到所有的类型(这种获取方式是将私有和非私有的类型都获取到)
            Type[] types = asm.GetTypes();
            foreach (Type item in types)
            {
                MessageBox.Show(item.FullName);
            }
            //GetExportedTypes()方法是从程序集实例中获取所有非私有的类型
            Type[] types2 = asm.GetExportedTypes();
            foreach (Type item in types2)
            {
                MessageBox.Show(item.Name);
            }
            //GetType()方法从程序集实例中获取指定的类型
            Type t1 = asm.GetType("_自定义程序集01.Person");
            MessageBox.Show(t1.Name);
             //GetMethods()方法通过类型实例获取这个类型的所有非私有方法
            MethodInfo[] methods = t1.GetMethods();
            foreach (MethodInfo item in methods)
            {
                MessageBox.Show(item.Name);
            }
            //获取指定的方法,第一个参数是方法名,第二个参数是想要获取的方法的参数列表
            MethodInfo method1 = t1.GetMethod("SayHi",new Type[] { typeof(string)});
            //创建当前类型的实例,使用Activator这个类的CreateInstance(类型实例)方法
            object obj = Activator.CreateInstance(t1);
            //调用获取到的方法(和委托调用差不多,基本上都是使用Invoke()方法进行调用),如果有返回值的话,直接接受Invoke()方法的返回值即可。
            method1.Invoke(obj,new object[] { "hello"});

            //通过获取到指定的构造函数来创建对象
            ConstructorInfo constructor = t1.GetConstructor(new Type[] { typeof(string), typeof(int), typeof(char) });
            object obj = constructor.Invoke(new object[] { "张三", 20, '男' });
            //通过反射获取对象的属性值
            PropertyInfo prop = t1.GetProperty("Name");//得到指定属性
            MessageBox.Show(prop.GetValue(obj).ToString());//得到属性值,参数中传一个实例对象

委托既可以做为方法的参数也可以当做返回值进行返回

    //将委托当做参数
    public int Sum(Func<int, int, int> func)
    {
        return func(1, 2);
    }
    //将委托当做返回值进行返回
    public Func<int, int, int> GetFunc()
    {
        return (a, b) => { return a + b; };
    }
            int result = Sum((a, b) => { return a + b; });
            MessageBox.Show(result.ToString());
            Func<int, int, int> func = GetFunc();
            MessageBox.Show(func(2, 3).ToString());

Type类的其他方法(IsAssignableFrom()、IsInstanceOfType()、IsSubclassOf()、IsAbstract属性)

        //动态加载程序集
        Assembly asm = Assembly.LoadFile(@"C:\Users\lenovo\Documents\visual studio 2015\Projects\11Net基础加强十一天-委托 事件 反射\_自定义程序集01\bin\Debug\_自定义程序集01.dll");
        //通过反射得到Person类型元数据、Student类型元数据、Teacher类型元数据
        Type personType = asm.GetType("_自定义程序集01.Person");
        Type studentType = asm.GetType("_自定义程序集01.Student");
        Type teacherType = asm.GetType("_自定义程序集01.Teacher");
        //IsAssignableFrom()方法是用来判断目标类型和此类型之间是否存在继承关系(目标类型的实例对象是否可以直接赋值给此类型变量)
        bool b1 = personType.IsAssignableFrom(studentType);
        MessageBox.Show(b1.ToString());//因为student类继承自person类,所以此处打印true
        bool b2 = personType.IsAssignableFrom(teacherType);
        MessageBox.Show(b2.ToString());//true
        bool b3 = teacherType.IsAssignableFrom(studentType);
        MessageBox.Show(b3.ToString());//teacher类和student类不存在继承关系,所以打印false

            //IsInstanceOfType()方法是用来判断某个实例对象是否属于某个类型(这里认为子类对象也可以是父类类型,返回true)
            object personObj = Activator.CreateInstance(personType);//实例化一个Person类型实例
            object studentObj = Activator.CreateInstance(studentType);//实例化一个Student类型实例
            bool b4= personType.IsInstanceOfType(personObj);
            MessageBox.Show(b4.ToString());//true
            bool b5 = teacherType.IsInstanceOfType(studentObj);
            MessageBox.Show(b5.ToString());//false

            //IsSubclassOf()方法用来判断该类型是否是目标类型的子类
            bool b7 = personType.IsSubclassOf(studentType);//person类型不是student类型的子类,所以返回false
            MessageBox.Show(b7.ToString());
            bool b8 = studentType.IsSubclassOf(personType);//true
            MessageBox.Show(b8.ToString());

            //IsAbstract属性判断某个类型是否为抽象的(静态类,抽象类,接口都属于抽象的,所以只要是不能进行实例化的就是抽象的)
            Type animalType = asm.GetType("_自定义程序集01.Animal");//获取静态类
            Type interfaceType = asm.GetType("_自定义程序集01.IFlyable");//获取接口
            Type animal2Type = asm.GetType("_自定义程序集01.Animal2");//获取抽象类
            MessageBox.Show(animalType.IsAbstract.ToString() + " " + interfaceType.IsAbstract.ToString() + " " + animal2Type.IsAbstract.ToString());//true true true

记事本插件

定义插件接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace IUpperable
{
    public interface IUpper
    {
         string Name//插件功能名称
        {
            get;
        }
        void Start(TextBox txtbox);//开始执行插件的方法
    }
}

定义插件类库:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IUpperable;
using System.Windows.Forms;

namespace Upper
{
    public class TextUpper : IUpper
    {
        public string Name
        {
            get
            {
                return "转大写";
            }
        }

        public void Start(System.Windows.Forms.TextBox txtbox)
        {
            txtbox.Text = txtbox.Text.ToUpper();//将字母转成大写
        }
    }
}

记事本主程序:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using IUpperable;
using System.Reflection;
using System.IO;

namespace 记事本
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //定义一个IUpper接口类型的变量
        public IUpper upperObj;
        private void Form1_Load(object sender, EventArgs e)
        {
            string dllsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dlls");//得到放置程序集文件夹的绝对路径
            string[] paths = Directory.GetFiles(dllsPath);
            foreach (string item in paths)//循环加载所有程序集
            {
                Assembly asm = Assembly.LoadFile(item);
                Type iupperType = typeof(IUpper);
                Type[] types = asm.GetTypes();//得到程序集中的所有类型元数据
                foreach (Type item2 in types)
                {
                    //要保证反射得到的类型不是抽象的,并且还是继承自IUpper接口的
                    if (!item2.IsAbstract && iupperType.IsAssignableFrom(item2))
                    {
                        upperObj = (IUpper)Activator.CreateInstance(item2);
                        ToolStripItem toolItem = 格式ToolStripMenuItem.DropDownItems.Add(upperObj.Name);
                        toolItem.Click += ToolItem_Click;//为toolItem添加单击事件
                    }
                }
            }
        }
        //toolItem单击事件处理程序
        private void ToolItem_Click(object sender, EventArgs e)
        {
            upperObj.Start(textBox1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值