复习委托
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);
}
}
}