委托
什么是委托?
如果我们要把方法当做参数来传递的话,就要用到委托。简单来说委托是一个类型,这个类型可以赋值一个方法的引用。
使用委托:
- 定义委托
- 创建委托实例
声明委托
定义委托:
//定义委托
delegate void IntMethodInvoker(int x);
使用委托
普通使用
使用构造函数实例化
private delegate string GetAString() //定义委托类型
static void Main(){
int x = 40;
//使用委托类型 创建实例
GetAString myfun = new GetAString(x.ToString); //相当于实例化类型 参数为方法
Console.WriteLine(myfun())
}
在创建实例的时候,也可以直接赋值方法
private delegate string GetAString() //定义委托类型
static void Main(){
int x = 40;
//使用委托类型 创建实例
GetAString myfun = x.ToString; //相当于实例化类型 参数为方法
Console.WriteLine(myfun.Invoke()) //也可通过Invoke调用
}
委托类型作为参数使用
private delegate void PrintString();
static void PrintStr(PrintString print)
{
print.Invoke();
}
static void Main(){
PrintString method;
method = () => Console.WriteLine("666666"); //这里可以指向一个独立的方法
PrintStr(method);
Console.ReadKey();
}
委托的多播
private delegate void PrintString();
static void PrintStr(PrintString print)
{
print.Invoke();
}
static void Main(){
PrintString method;
//创建实例、匿名委托
method = () => Console.WriteLine("666666"); //这里可以指向一个独立的方法
method += () => Console.WriteLine("委托的多播");
PrintStr(method);
// 666666
// 委托的多播
Console.ReadKey();
}
系统内置委托
除了我们自己定义的委托之外,系统还给我们提供了内置的委托类型,Action&Func
Action
Action委托引用了一个==没有返回值(void)==的方法,T表示方法参数,可以传递0~16个参数类型
Acrion委托
-
Action
-
Action<int>
-
Action<int ,string,bool>
-
········
实例代码:
static void Main(string[] args)
{
// 创建实例
Action<int> method = delegate(int i) { Console.WriteLine($"这里是使用delegate的委托,参数值 是:{i}"); };
Action<int> methon1 = (int a) => { Console.WriteLine($"这里是使用匿名委托,参数值是:{a}"); };
// 执行
method(12);
methon1(52);
}
Func
Func引用了一个带有一个返回值的方法,可以传递0~16个参数类型,最后一个值为返回值
-
Func
-
Func<int>
-
Func<int ,string,bool>
-
········
实例代码:
static void Main(string[] args)
{
//匿名方法
Func<int,string> method2 = delegate(int i) { return i.ToString() + "转换";};
Console.WriteLine(method2(12));
}
匿名方法
本质还是一个方法,只是没有名字,任何使用委托变量的地方都可使用匿名方法。
只能赋值给委托,由委托调用,减少代码复杂度
比如在winForm中在当前窗口打开另外一个窗口,给数据添加一条新数据,然后通过委托进行对之前表中的内容进行刷新,如果这个方法仅用这一次,就可以使用匿名委托–回调方法
static void Main(string[] args)
{
Func<string,string> method3 = delegate(string str) {return "这就是"+str;};
Console.WriteLine(method3("匿名方法"));
}
Lambda表达式
- 是匿名方法的一种简写形式,也是定义了一个方法
- Lambda表达式的参数不需要指定参数类型
- 只有一个参数的时候不需要括号(类似Js箭头函数)
- 代码只有一句的时候,不需要花括号
- Lambda可以访问表达式块外部的变量(类似Js箭头函数)
static void Main(string[] args)
{
// 匿名方法
Func<string,string> method3 = delegate(string str) {return "这就是"+str;};
Console.WriteLine(method3("匿名方法"));
//lambda表达式替代匿名方法 无须再指定参数类型
Func<string,string> method4 = (arg) => "这就是"+str; //这时return也可以省略
Console.WriteLine(method4("匿名方法"));
// 访问外部变量
int someVal = 5;
Func<int,int> f = val => val+someVal;
Console.WriteLine(f(3)); //8
}
应用
通过泛型对冒泡排序进行扩展,可以给任何对象排序
冒泡排序:
bool swapped = true;
do
{
swapped = false;
for (int i = 0; i < arr.Length; i++)
{
if (arr[i]>arr[i+1])
{
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
swapped = true;
}
}
} while (swapped);
扩展:
//首先定义一个类
class Employee
{
public Employee(string name, decimal salary)
{
this.Name = name;
this.Salary = salary;
}
public string Name { get; private set; }
public decimal Salary { get; private set; }
//比较方法
public static bool CompareSalary(Employee e1, Employee e2)
{
return e1.Salary > e2.Salary;
}
}
class Program{
// 冒泡排序
public static void Srot<T>(List<T> sortArr, Func<T, T, bool> comparision)
{
bool swapped = true;
do
{
swapped = false;
for (int i = 0; i < sortArr.Count - 1; i++)
{
if (comparision(sortArr[i], sortArr[i + 1]))
{
T temp = sortArr[i];
sortArr[i] = sortArr[i + 1];
sortArr[i + 1] = temp;
swapped = true;
}
}
} while (swapped);
}
static void Main(string[] args)
{
Employee e1 = new Employee("111", 152);
Employee e2 = new Employee("111", 358);
Employee e3 = new Employee("111", 12);
List<Employee> list = new List<Employee> {e1, e2, e3};
// 传递比较方法给Sort
Srot(list, Employee.CompareSalary);
list.ForEach(r => { Console.WriteLine(r.Salary); });
Console.ReadKey();
}
}
事件
事件基于委托,为委托提供了一个发布/订阅机制,我们可以说事件是一种具有特殊签名的委托。
什么是事件?
事件(Event)是类或对象对其他类或对象通知发生的事情的一种特殊签名的委托。
事件的声明:
public event 委托类型 事件名;
事件使用event关键字来声明,它的返回值是一个委托类型。
通常事件的命名,以名字+Event作为他的名称
基本使用:
namespace _007_事件
{
public delegate void MyDelegate();
class Program
{
// 声明一个事件
public event MyDelegate mydelgate;
static void Main(string[] args)
{
Program p = new Program();
p.mydelgate = Test1;
p.mydelgate();
Console.ReadKey();
}
private static void Test1()
{
Console.WriteLine("Test");
}
}
}
实例:(观察者模式)
三只动物,猫(Tom),两只老鼠(Jerry,Jack),当猫叫(CatShout)的时候,两只老鼠开始逃跑(MouseRun)。
使用委托完成
实体类:
class Cat
{
private string name;
private string color;
// 声明一个委托
public Action catCome;
public Cat(string name,string color)
{
this.name = name;
this.color = color;
}
public void CatComing()
{
Console.WriteLine($"{color}的猫:{name}过来了---");
if (catCome!=null)
{
catCome();
}
}
}
class Mouse
{
private string name;
private string color;
public Mouse(string name,string color,Cat cat)
{
this.name = name;
this.color = color;
// 把自身的逃跑方法注册进猫里面
cat.catCome += RunAway;
}
public void RunAway()
{
Console.WriteLine($"{color}的老鼠:{name}说老猫来了。快跑!!!");
}
}
Program:
static void Main(string[] args)
{
Cat cat = new Cat("Tom","蓝色");
Mouse m1 = new Mouse("Jerry","黄色",cat);
Mouse m2 = new Mouse("Jack", "绿色",cat);
//猫的状态发生改变,
cat.CatComing();
Console.ReadKey();
}
但是:
委托是可以从外部直接调用到的
cat.catCome(); // 这个委托是可以直接从外部调用到的
和委托的区别
- 事件是一种特殊的委托,或者说是受限制的委托,是委托的一种特殊应用,只能施加+=,-=操作符。二者本质上是一个东西。
- event ActionHandler Tick; //编译成创建一 个私有的委托示例,和施加在其上的add, remove(+=,-=)方法。
- event只允许用add,remove方法来操作,这导致了它不允许在类的外部被直接触发,只能在类的内部适合的时机触发。委托可以在外部被触发,但是别这么用。
- 使用中,委托常用来表达回调;事件表达外发的接口。
所以直接将时间变成委托即可,使用方法不变:
// class Cat
public event Action catCome; //声明一个事件 发布消息
// class Mouse
cat.catCome += RunAway; //订阅时间