C#进阶04——委托和事件
1.委托
1.委托是什么
委托是 函数(方法)的容器
可以理解为表示函数(方法)的变量类型
用来 存储、传递函数(方法)
委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
不同的 函数(方法)必须对应和各自"格式"一致的委托
2.基本语法
关键字 : delegate
语法:访问修饰符 delegate 返回值 委托名(参数列表);写在哪里?
可以申明在namespace和class语句块中
更多的写在namespace中简单记忆委托语法 就是 函数申明语法前面加一个delegate关键字
3.定义自定义委托
访问修饰符默认不写 为public 在别的命名空间中也能使用
private 其他命名空间就不能用了
一般使用public
申明了一个可以用来存储无参无返回值的容器
这里只是定义了规则 并没有使用
delegate void MyFun();
委托规则的申明 是不能重名(同一语句块中)
表示用来装载或传递 返回值为 int 有一个 int参数 的函数的 委托 容器
delegate int MyFun2(int a);
class Program
{
static void Main(string[] args)
{
Console.WriteLine("委托");
//专门用来装载 函数的 容器
MyFun f = new MyFun(Fun);
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
Console.WriteLine("5");
f.Invoke(); //1 2 3 4 5 123456
MyFun f2 = Fun;
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
Console.WriteLine("5");
f2(); //1 2 3 4 5 123456
MyFun2 f3 = Fun2;
f3(1);
Console.WriteLine(f3(1)); //1
MyFun2 f4 = new MyFun2(Fun2);
Console.WriteLine(f4.Invoke(3)); //3
}
static void Fun()
{
Console.WriteLine("123456");
}
static int Fun2(int value)
{
return value;
}
}
委托是支持 泛型的 可以让返回值和参数 可变 更方便我们的使用
delegate T MyFun3<T, K>(T t, K k);
4.使用定义好的委托
委托变量是函数的容器
委托常用在:
- 作为类的成员
- 作为函数的参数
class Test
{
public MyFun fun;
public MyFun2 fun2;
public void TestFun(MyFun fun, MyFun2 fun2)
{
//先处理一些别的逻辑 当这些逻辑处理完了 再执行传入的函数
int i = 1;
i *= 2;
i += 2;
//fun();
//fun2(i);
//this.fun = fun;
//this.fun2 = fun2;
}
#region 增
public void AddFun(MyFun fun, MyFun2 fun2)
{
this.fun += fun;
this.fun2 += fun2;
}
#endregion
#region 删
public void Remove(MyFun fun, MyFun2 fun2)
{
//this.fun = this.fun - fun;
this.fun -= fun;
this.fun2 -= fun2;
}
}
5.委托变量可以存储多个函数(多播委托)
//如何用委托存储多个函数
MyFun ff = null;
//ff = ff + Fun;
ff += Fun;
ff += Fun3;
ff();
//从容器中移除指定的函数
ff -= Fun;
//多减 不会报错 无非就是不处理而已
ff -= Fun;
ff();
//清空容器
ff = null;
if( ff != null )
{
ff();
}
6.系统定义好的委托
使用系统自带委托 需要引用 using System;
//无参无返回值
Action action = Fun;
action += Fun3;
action();
//可以指定返回值类型的 泛型委托
Func<string> funString = Fun4;
Func<int> funInt = Fun5;
//可以传n个参数的 系统提供了 1到16个参数的委托
Action<int, string> action1 = Fun6;
//可以传n个参数的 并且有返回值的 系统也提供了 16个委托
Func<int, int> func2 = Fun2;
//***************************************************//
static string Fun4()
{
return " ";
}
static int Fun5()
{
return 1;
}
static void Fun6(int i, string s)
{
}
7.总结
简单理解 委托 就是装载、传递函数的容器而已
可以用委托变量 来存储函数或者传递函数的
系统其实已经提供了很多委托给我们用
Action :没有返回值,参数 提供了 0到16个委托给我们用
Func :有返回值,参数 提供了0到16个委托给我们用
2.事件
1.事件是什么
事件是基于委托的存在
事件是委托的安全包裹
让委托的使用更具有安全性
事件 是一种特殊的变量类型
2.事件的使用
申明语法:
访问修饰符 event 委托类型 事件名;
事件的使用:
1.事件是作为 成员变量存在于类中
2.委托怎么用 事件就怎么用
事件相对于委托的区别:
1.不能在类外部 赋值
2.不能再类外部 调用
注意:它只能作为成员存在于类和接口以及结构体中
class Test
{
//委托成员变量 用于存储 函数的
public Action myFun;
//事件成员变量 用于存储 函数的
public event Action myEvent;
public Test()
{
//事件的使用和委托 一模一样 只是有些 细微的区别
myFun = TestFun;
myFun += TestFun;
myFun -= TestFun;
myFun();
myFun.Invoke();
//myFun = null;
myEvent = myFun;
myEvent += myFun;
myEvent -= myFun;
myEvent();
myEvent.Invoke();
//myEvent = null;
}
public void TestFun()
{
Console.WriteLine("123");
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("事件");
Test t = new Test();
//委托可以在外部赋值
t.myFun = null;
//事件不能在外部赋值
//t.myEvent = null;
//虽然不能直接赋值 但是可以 加减 去添加移除记录的函数
t.myEvent += TestFun;
//委托是可以在外部调用的
t.myFun();
t.myFun.Invoke();
//事件不能在外部调用
//t.myEvent();
//t.myEvent.invoke();
//只能在类的内部去封装 调用
Action a = TestFun;
//event Action e = TestFun;
//事件 是不能作为临时变量在函数中使用的
}
static void TestFun()
{
Console.WriteLine("123");
}
}
3.为什么有事件
- 防止外部随意置空委托
- 防止外部随意调用委托
- 事件相当于对委托进行了一次封装 让其更加安全
4.总结
事件和委托的区别
事件和委托的使用基本是一模一样的
事件就是特殊的委托
主要区别:
- 事件不能再外部使用赋值=符号,只能使用+ - 委托 哪里都能用
- 事件 不能再外部执行 委托哪里都能执行
- 事件 不能作为 函数中的临时变量的 委托可以
3.匿名函数
1.什么是匿名函数
顾名思义,就是没有名字的函数
匿名函数的使用主要是配合委托和事件进行使用
脱离委托和事件 是不会使用匿名函数的
2.基本语法
delegate (参数列表)
{
//函数逻辑
};
何时使用?
1.函数中传递委托参数时
2.委托或事件赋值时
3.使用
1.无参无返回
- 这样申明匿名函数 只是在申明函数而已 还没有调用
- 真正调用它的时候 是这个委托容器啥时候调用 就申明时候调用这个匿名函数
Action a= delegate ()
{
Console.WriteLine("匿名函数逻辑");
};
a();
2.有参
Action<int,string>b= delegate (int a, string b)
{
Console.WriteLine(a);
Console.WriteLine(b);
};
b(100,"123");
3.有返回
Func<string> c= delegate ()
{
return "123";
};
Console.WriteLine(c());
4.一般情况会作为函数参数传递 或者 作为函数返回值
Test t = new Test();
Action ac = delegate ()
{
Console.WriteLine("随参数传入的匿名函数逻辑");
};
t.Dosomething(50, ac);
// 参数传递
t.Dosomething(100, delegate ()
{
Console.WriteLine("随参数传入的匿名函数逻辑");
});
// 返回值
Action ac2 = t.GetFun();
ac2();
//一步到位 直接调用返回的 委托函数
t.GetFun()();
4.匿名函数的缺点
添加到委托或事件中后 不记录 无法单独移除
Action ac3 = delegate ()
{
Console.WriteLine("匿名函数一");
};
ac3+= delegate ()
{
Console.WriteLine("匿名函数二");
};
ac3();
//因为匿名函数没有名字 所以没有办法指定移除某一个匿名函数
//此匿名函数非彼匿名函数 不能通过看逻辑是否一样 就证明是一个
//ac3 -= delegate ()
//{
// Console.WriteLine("匿名函数一");
//};
ac3 = null;
//ac3();
5.总结
匿名函数 就是没有名字的函数
固定写法:
delegate (参数列表){}
主要是在 委托传递和存储时 为了方便可以直接使用倪敏该函数
缺点是 没有办法指定移除
4.lambad表达式
1.什么是lambad表达式
可以将lambad表达式 理解为匿名函数的简写
它除了写法不同外
使用上和匿名函数一模一样
都是和委托或者事件 配合使用的
2.lambad表达式语法
匿名函数
delegate (参数列表)
{};
lambad表达式
(参数列表) =>
{
//函数体
};
3.使用
1.无参无返回
Action a = () =>
{
Console.WriteLine("无参无返回值的lambad表达式");
};
a();
2.有参
Action<int>a2= (int value) =>
{
Console.WriteLine("有参数的lambda表达式{0}",value);
};
a2(100);
3.甚至参数类型都可以省略 参数类型和委托或事件容器一致
Action<int> a3 = (value) =>
{
Console.WriteLine("省略参数类型的写法{0}", value);
};
a3(200);
4.有返回值
Func<string, int> a4 = (value) =>
{
Console.WriteLine("有返回值有参数的lambda表达式{0}", value);
return 1;
};
//其他传承使用的和匿名函数一样
//确定也是和匿名函数一样的
4.闭包
内层的函数可以引用包含在它外层的函数的变量
即使外层函数的执行已经终止
注意:该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值
class Test
{
event Action action;
public Test()
{
int value = 10;
//这里就形成了闭包
//因为 当构造函数执行完毕时 其中什么的临时变量value的生命周期被改变了
action = () =>
{
Console.WriteLine(value);
};
for (int i = 0; i < 10; i++)
{
//此index 非彼index
int index = i;
action += () =>
{
Console.WriteLine(index);
};
}
}
public void DoSomething()
{
action();
}
}
5.总结
匿名函数的特殊写法 就是 lambad表达式
固定写法 就是(参数列表) =>{}
参数列表 可以直接省略参数类型
主要在 委托传递和存储时 为了方便可以直接使用匿名函数或者lambad表达式
缺点:无法指定移除