@作者: 风不停息丶 笔记
文章目录
C#委托
1、什么是委托
- 委托是函数(方法)的容器,可以理解为表示函数(方法)的变量类型
- 用来存储、传递函数(方法)
- 委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
- 不同的函数(方法)必须对应和各自"格式"一致的委托
2、基本语法
- 委托语法就是函数申明语法前面加一个delegate关键字
- 语法:访问修饰符 delegate 返回值 委托名(参数列表)
- 可以申明在namespace和class语句块中
public delegate int MyFun(int a);
3、自定义委托
- 访问修饰默认不写(public)在别的命名空间中也能使用
- private其它命名空间就不能用,一般使用public
//申明了一个可以用来存储无参无返回值函数的容器
//这里只是定义了规则并没有使用
delegate void MyFun();
//委托规则的申明是不能重名(同一语句块中)
//表示用来装载或传递返回值为int 有一个int参数的函数的委托容器规则
public delegate int MyFun02(int a);
4、自定义委托的应用
- 委托变量是函数的容器
- 作为类的成员
//委托基本使用方法
//专门用来装载函数的容器
//无参委托两种赋值调用方法
MyFun f = new MyFun(Fun);
f.Invoke();//调用
MyFun f2 = Fun;
f2();//调用
//有参委托两种赋值调用方法
MyFun02 f4 = new MyFun02(Fun02);
f4.Invoke(4);
MyFun02 f3 = Fun02;
Console.WriteLine(f3(1));
//****************************方法
//无参无返回值函数
static void Fun()
{
Console.WriteLine("123456");
}
//有参有返回值函数
static int Fun02(int index)
{
return index;
}
- 作为函数的参数
- 作为类的成员、作为函数的参数、添加 增 方法和 删 方法
class Test
{
//作为类的成员
public MyFun fun;
public MyFun02 fun2;
//作为函数的参数
public void TestFun(MyFun fun,MyFun02 fun02)
{
//先处理一些别的逻辑当这些逻辑处理完了再执行传入的函数
int i = 1;
i *= 2;
//直接执行
//fun();
//fun02(i);
//先存起来一会儿在执行
this.fun = fun;
this.fun2 = fun02;
}
//增
public void AddFun(MyFun fun,MyFun02 fun2)
{
this.fun += fun;
this.fun2 += fun2;
}
//删
public void RemoFun(MyFun fun,MyFun02 fun2)
{
this.fun -= fun;
this.fun2 -= fun2;
}
}
//***************************调用
Test test = new Test();//实例化
test.TestFun(Fun,Fun02);//调用委托方法
Console.WriteLine(test.fun2(2));//打印
5、委托变量可以存储多个函数
- 使用委托存储多个函数
//如何使用委托存储多个函数
MyFun ff = Fun;
//使用+=之前必须已经赋值过
ff += Fun3;
ff();//调用
//从容器中移除指定函数
//如果容器里没有被减参数,就不执行
ff -= Fun;
ff();
//清空容器,注意完全清空后继续调用会报错
ff = null;
if (ff != null)
{
ff();
}
//**************************方法
static void Fun3()
{
Console.WriteLine("bbbbbb");
}
- 调用在委托里写的增删方法存储多个函数
Test t = new Test();
t.AddFun(Fun,Fun02);//增
t.fun();//调用
t.fun02(13);
t.RemoFun(Fun,Fun02)//删
6、系统定义好的委托
- 使用系统自带委托需要引用using System;
//系统自带委托 无参无返回值的委托
Action action = new Action(Fun);
action += Fun3;
action();
//系统自带的泛型委托 有指定返回值
Func<string> funcString = Fun4;
string a = funcString();
Console.WriteLine(a);
Func<int> funcInt = Fun5;
//可以传多个参数的委托,系统提供了16个参数委托的重载
Action<int, string> action1 = Fun6;
//可以传多个参数的泛型委托,系统提供了16个参数委托的重载
Func<int, int> func2 = Fun02;
//************************************方法
static string Fun4()
{
return "abc";
}
static int Fun5()
{
return 2;
}
static void Fun6(int a,string b)
{
Console.WriteLine("is");
}
- 可以为自定义的委托添加泛型
- 委托是支持泛型的可以让返回值和参数可变更方便我们的使用
delegate T Myfun3<T, k>(T v, k k);
7、总结
- 简单理解委托就是装载、传递函数的容器而已
- 可以用委托变量来存储函数或者传递函数的
- 系统其实已经提供了很多委托给我们用
- Action:没有返回值,参数提供了0~16个委托给我们用
- Func:有返回值,参数提供了0~16个委托给我们用
C#事件
1、什么是事件
- 事件是基于委托的存在
- 事件是委托的安全包裹
- 让委托的使用更具有安全性
- 事件是—种特殊的变量类型
2、事件的使用
- 语法:访问修饰符 event 委托类型 事件名;
- 事件的使用:
- 1.事件是作为成员变量存在于类中
- 2.委托怎么用事件就怎么用
- 事件相对于委托的区别:
- 1.不能在类外部 赋值
- 2.不能再类外部 调用
/// <summary>
/// 事件
/// </summary>
class Test
{
//委托成员变量用于存储函数的
public Action myFun;
//事件成员变量用于存储函数
public event Action myEvent;
public Test()
{
//事件的使用和委托一模一样只是有些细微的区别
myFun = TestFun;
myFun += TestFun;
myFun -= TestFun;
myFun();
myFun.Invoke();
myFun = null;
Console.WriteLine("**************");
myEvent = TestFun;
myEvent += TestFun;
myEvent -= TestFun;
myEvent();
myEvent.Invoke();
myEvent = null;
}
//类中封装的事件方法,让事件在外部可以被调用
public void DoEvent()
{
if (myEvent != null)
{
myEvent();
}
}
public void TestFun()
{
Console.WriteLine("aaa");
}
}
internal class Program
{
static void Main()
{
Test t = new Test();
//委托可以在外部赋值
t.myFun = null;
t.myFun = TestFun;
t.myFun = t.myFun + TestFun;
//事件是不能再外部赋值的
//t.myEvent = null;//报错
//t.myEvent = TestFun;//报错
//虽然不能直接赋值但是可以加减去添加移除记录的函数
//t.myEvent = t.myEvent + TestFun;//报错
t.myEvent += TestFun;
t.myEvent -= TestFun;
//委托是可以在外部调用的
t.myFun();
t.myFun.Invoke();
//事件不能再外部调用
//t.myEvent();//报错
//如果事件想要在外部调用事件,必须在类中为其封装方法
t.DoEvent();
Action a = TestFun;
//事件是不能作为临时变量在函数中使用的
//event Action b = TestFun;//报错
}
static void TestFun()
{
}
}
- 注意:它只能作为成员存在于类和接口以及结构体中
3、事件的作用
- 1.防止外部随意置空委托
- 2.防止外营随意调用委托
- 3.事件相当于对委托进行了一次封装让其更加安全
4、总结
- 事件和委托的区别:
- 事件和委托的使用基本是一模一样的
- 事件就是特殊的委托
- 主要区别:
- 1.事件不能再外部使用赋值=符号,只能使用+–委托哪里都能用
- 2.事件不能再外部执行委托哪里都能执行
- 3.事件不能作为函数中的临时变量的委托可以
匿名函数
1、什么是匿名函数
- 顾名思义,就是没有名字的函数
- 匿名函数的使用主要是配合委托和事件进行使用
- 脱离委托和事件是不会使用匿名函数的
2、基本语法
delegate (参数列表)
{
//函数逻辑
}
//1.函数中传递委托参数时使用
//2.委托或事件赋值时使用
3、匿名函数的使用
//1、无参无返回值匿名函数
//声明匿名函数,还没调用
//这个委托容器什么时候调用,这个匿名函数就什么时候调用
Action test = delegate ()
{
Console.WriteLine("匿名函数逻辑");
};
test();
//2、有参匿名函数
Action<int,string> test2 = delegate (int a,string b)
{
Console.WriteLine(a);
Console.WriteLine(b);
};
test2(10, "aaa");
//3、有返回值的匿名函数
Func<string> test3 = delegate ()
{
return "123";
};
Console.WriteLine(test3());
- 4、一般情况会作为函数参数传递或者作为函数返回值
Test t = new Test();
//t.Dosmthing(100, Fun);//需要传参
//传递参数
t.Dosmthing(100, delegate ()//使用匿名函数
{
Console.WriteLine("随参数传入的匿名函数逻辑");
});
//Action ac = delegate ()
//{
// Console.WriteLine("随参数传入的匿名函数逻辑");
//};
//t.Dosmthing(10, ac);
//返回值
Action ac2 = t.GetFun();
ac2();
//一步到位直接调用返回的委托函数
t.GetFun()();
//添加到委托或事件容器中后不记录无法单独移除
Action ac3 = delegate ()
{
Console.WriteLine("匿名函数一");
};
ac3 += delegate ()
{
Console.WriteLine("匿名函数二");
};
ac3();
//因为匿名函数没有名字所以没有办法指定移除某一个匿名函数
//此匿名函数非彼匿名函数 不能通过看逻辑是否一样就证明是一个
ac3 -= delegate ()//无效
{
Console.WriteLine("匿名函数二");
};
//唯一清除方法
ac3 = null;
//******************************************方法
static void Fun()
{
Console.WriteLine("外部传入的函数逻辑");
}
//****************************************Test类
//委托
class Test
{
public Action action;
//作为参数传递时
public void Dosmthing(int a,Action fun)
{
Console.WriteLine(a);
fun();
}
//作为返回值
public Action GetFun()
{
//return Test01;
return delegate ()
{
Console.WriteLine("函数内部返回的一个匿名函数逻辑");
};
}
//无参无返回值
public void Test01()
{
Console.WriteLine("逻辑");
}
}
4、总结
1、主要是在委托传递和存储时为了方便可以直接使用匿名函数
2、缺点: 添加到委托或事件容器中后不记录无法单独移除
Lambad表达式
1、什么是Lambad表达式
- 可以将lambad表达式 理解为匿名函数的简写
- 它除了写法不同外,使用上和匿名函数一模一样
- 都是和委托或者事件 配合使用的
2、语法
//lambad表达式语法
(参数列表)=>
{
//函数体
}
3、使用
- 使用上也和匿名函数相似
//1、无参无返回值
Action test=() =>
{
Console.WriteLine("无参无返回值的lambad表达式");
};
test();
//2、有参
Action<int > test2 = (int index) =>
{
Console.WriteLine("有参lambad表达式{0}",index);
};
test2(2);
//3、甚至参数类型都可以省略参数类型和委托或事件容器一致
Action<int> test3 = (index) =>
{
Console.WriteLine("省略参数类型的写法{0}", index);
};
test3(3);
//4、有返回值
Func<string, int> test4 = (index) =>
{
Console.WriteLine("有返回值的lambad表达式{0}",index);
return 1;
};
Console.WriteLine(test4("123"));
。。。
4、闭包
- 内层的函数可以引用包含在它外层的函数的变量
- 即使外层函数的执行已经终止
- 注意:该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值
class Test
{
public event Action action;
public Test()
{
int value = 10;
//这里就形成了闭包
//因为当构造函数执行完毕时其中串明的临时变量value的声明周期被改变了
action = () =>
{
Console.WriteLine(value);
};
for (int i = 0; i < 10; i++)
{
//每次循环重新赋值
int a = i;
action += () =>
{
//打印的都是10,而不是按顺序打印1-10
//Console.WriteLine(i);
//如果想要打印1-10,需每次循环时重新赋值
Console.WriteLine(a);
};
}
}
//定义外部可调用事件方法
public void DoSomthing()
{
action();
}
}
//***********************************调用
Test test1 = new Test();
test1.DoSomthing();
5、总结
- 匿名函数的特殊写法就是lambad表达式
- 参数列表可以直接省略参数类型
- 主要在委托传递和存储时为了方便可以直接使用匿名函数或者lambad表达式