C#委托和事件,匿名函数和lamabda表达式

(1)委托

委托是函数方法的容器,可以理解为函数方法的变量类型,用来存储传递函数方法

委托的本质是一个类,是用来定义函数方法的类型(返回值和参数的类型)

关键字delegate

using System;
namespace Test
{
	//委托
    //访问修饰符 delegate 返回值 委托名(参数列表);
    //更多的写到namespace中,也可以写到类里面
    delegate void MyFun();//无参无返回值的函数容器
    //当访问修饰符不写时为public
    public delegate int MyFun1(int a);//不能重名无法重载
	class Program
	{
		public static void Main(string[] args)
		{
			//无参
			//方式一
            MyFun f1 = new MyFun(Fun);//参数为符合条件的函数方法名 
            Console.WriteLine("-方法一-");
            f1.Invoke();//调用委托中的函数
            //方式二
            Console.WriteLine("-方法二-");
            MyFun f2 = Fun;//不加括号
            f2();
            //含参
            Console.WriteLine("-方法一-");
            MyFun1 f3 = new MyFun1(Fun2);
            Console.WriteLine(f3.Invoke(1));
            Console.WriteLine("-方法二-");
            MyFun1 f4 = Fun2;
            Console.WriteLine(f4(1));
            //多播
            Console.WriteLine("--多播委托--");
            MyFun ff = new MyFun(Fun);
            ff += Fun1;//委托变量存储多个函数 并按添加的顺序执行
            ff.Invoke();
            //移除指定函数
            ff -= Fun1;
            //清空函数
            ff = null;
            //使用系统自带的委托,使用时必须引用using System命名空间
            Console.WriteLine("--系统委托Action--");//Action无返回值的委托
            Action action = null;//Action委托无参数委托
            action += Fun1;
            action.Invoke();
            Action<string,int> action2 = null;//Action泛型n个参数委托(n<=16)
            action2 += Fun4;
            action2.Invoke("nihao",123);
            Console.WriteLine("--系统委托Func--");//Func泛型有返回值委托
            Func<string> func = null;//Func泛型无参数sting返回值委托
            func += Fun3;
            Console.WriteLine(func.Invoke());
            Func<int,int> func2 = null;//Func泛型前面参数,最后一个返回值
            func2 += Fun2;
            func2.Invoke(16);
		}
		static public void Fun()
		{
			Console.WriteLine("void Fun");
		}
		static public void Fun1()
		{
			Console.WriteLine("void Fun1");
		}
		static public int Fun2(int value)
		{
			Console.Write("int Fun2:{0}",value);
			return value;
		}
		static public string Fun3()
		{
			return "string Fun3";
		}
		static public void Fun4(string s,int i)
		{
			Console.WriteLine("void Fun4 string:{0},int:{1}",s,i);
		}
	}
	class Test //可以在类中委托
	{
		public MyFun fun1;
		public MyFun1 fun2;
		public void AddMyFun(MyFun fun1,MyFun1 fun2)
		{
			this.fun1 += fun1;
			this.fun2 += fun2;
		}
		public void RemoveMyFun(MyFun fun1,MyFun1 fun2)
		{
			this.fun1 -= fun1;
			this.fun2 -= fun2;                        	
		}                        
	}
}

(2)事件

事件是一种特殊的委托,事件是委托的安全包裹,让委托的使用更具有安全性

using System;
namespace Test
{
	//访问修饰符 event 委托类型 事件名;
	//只能作为成员变量存在类,接口和结构体中 和委托用法相同
	//相比于委托 事件不能在类的外部赋值与调用
	class Test
	{
		//委托成员变量 用于存储函数
		public Action myFun;
		//事件成员变量 用于存储函数
		public event Action myEvent;
		public Test()
		{
			myFun = TestFun;
			myFun += TestFun;
			myFun -= TestFun;
			myFun();
			myFun.Invoke();
			myFun = null;
			myEvent = TestFun;
			myEvent += TestFun;
			myEvent -= TestFun;
			myEvent();
			myEvent.Invoke();
			myEvent = null;
		}
		public void TestFun()
		{
			Console.WriteLine("NoProblem");
		}
	}
	class Program
	{
		public static void Main(string[] args)
		{
			Test test = new Test();
			//委托可以在外部赋值
			test.myFun = null;
			test.myFun = TestFun;
			//事件不能再外部赋值
			//test.myEvent =null;//报错
			//test.myEvent =TestFun;//报错
			//不能赋值但可以通过加减添加或者移除记录的函数
			test.myEvent += TestFun;
			//委托可以在外部调用
			test.myFun();
			test.myFun.Invoke();
			//事件不可以;
			//test.myEvent();//报错
			//test.myEvent.Invoke();//报错
		}
		static public void TestFun()
		{
			Console.WriteLine("MainFunNoProblem");
		}
	}
}
  • 为什么有事件

防止外部随意置空委托

防止外部随意调用委托

  • 区别

事件不能再外部使用赋值 = 符号 , 只能使用 + - 委托哪里都能用。

事件不能再外部执行,委托哪里都能执行。

事件不能作为函数中的临时变量 委托可以。

(3)匿名函数

没有名字的函数,主要是配合事件与委托进行使用

using System;
namespace Test
{
	class Program
	{
		public static void Main(string[] args)
		{
			//声明匿名函数 
			//无参无返回
			Action a1 = delegate()
			{
				Console.WriteLine("无参无返回匿名函数");
			};
			//只有函数容器调用时才会执行函数代码
			a1.Invoke();
			//有参无返回
			Action<int,string> a2 = delegate(int i,string s)
			{
				Console.WriteLine("有参有返回匿名函数{0}{1}",i,s);
			};
			a2(520,"你好");
			//有返回值无参
			Func<string> f1 = delegate()
			{
				return "有返回值无参匿名函数";
			};
			Console.WriteLine(f1.Invoke());
			//有返回值有参
			Func<string,string> f2 = delegate(string s)
			{
				return s;
			};
			Console.WriteLine("有返回值有参匿名函数"+f2.Invoke("我好"));
			//经常用在随参数传入时
			Test t = new Test();
			t.doSomething("你好",delegate(){Console.WriteLine("随参数传入的匿名函数");});
			t.doSomething("随参数传入的匿名函数是a1",a1);
			//也可以分部写匿名函数
			Action ac = t.GetFun();
			ac();
			
			t.GetFun()();//等于上面的代码 一步调用
			//匿名函数的缺点
			//不记录无法单独移除
			Action a3 = delegate()
			{
				Console.WriteLine("匿名函数1");
			};
			a3 += delegate()
			{
				Console.WriteLine("匿名函数2");
			};
			a3();
			//因为匿名函数没有名字,所以没办法指定移除某个匿名函数
			a3 -= delegate()//新函数,并非上方的1
			{
				Console.WriteLine("匿名函数1");
			};
			a3();
		 }
	}
	class Test
	{
		public void doSomething(string s, Action fun)
		{
			Console.WriteLine("执行了一段代码{0}",s);
			fun();
		}
		public Action GetFun()
		{
			return delegate()
			{
				Console.WriteLine("我只是想返回一个函数?");//返回匿名函数
			};
		}
	}
}

(4)lambda表达式

可以将lambda表达式理解为匿名函数的简写,它除了写法不同外,使用上和匿名函数一模一样,都是和委托或者事件配合使用的。

using System;
namespace Test
{
	class Program
	{
		public static void Main(string[] args)
		{
			//声明匿名函数 delegate(参数列表)
			//{
			//	
			//}
			Action a1 = delegate()
			{
				Console.WriteLine("无参无返回匿名函数");
			};
			a1();
			//lambda函数
			//(参数列表)=>
			//{
			//	
			//}
			//无参无返回值
			Action a2 = ()=>{Console.WriteLine("无参无返回匿名函数");};
			a2();
			//含参无返回值
			Action<int> a3 = (int value)=>{Console.WriteLine("含参无返回匿名函数{0}",value);};
			a3(520);
			//当含有参数时,lambda的参数类型可以省略
			Action<int> a4 = (value)=>{Console.WriteLine("含参无返回匿名函数{0}",value);};//没写int
			a4(100);
			//无参有返回值
			Func<string> f1 = ()=>{return "我爱你";};
			Console.WriteLine("无参有返回匿名函数{0}",f1());
			//含参有返回值
			Func<string,string> f2 = (string name)=>{return name;};
			Console.WriteLine("无参有返回匿名函数{0}",f2("张三"));
		 }
	}
}

(5)闭包

内层的函数可以引用包含在它外层的函数的变量
即使外层函数的执行已经终止

注意:该变量提供的值并非变量创建时的值,而是在父函数范围内最终值

using System;
namespace Test
{
	class Test
	{
		//闭包
		//内层的函数可以引用包含在它外层的函数的变量
		//即使外层函数的执行已经终止
		//注意:该变量提供的值并非变量创建时的值,而是在父函数范围内最终值
		public Action action;
		public Test()
		{
			int value = 10;//原来是构造函数执行完销毁
			action = ()=>{Console.WriteLine(value);};//相当于用action存了value的值,改变了原有的生命周期,行成了闭包
			for(int i=9;i>0;i--)
			{
				action += ()=>
				{
					//注意:该变量提供的值并非变量创建时的值,而是在父函数范围内最终值
					Console.WriteLine(i);//并非987654321 ,而是i的最终值0
				};
			}
			for(int i=9;i>0;i--)
			{
				int index = i;//每次创建的index都是一个新值
				action += ()=>
				{
					Console.WriteLine(index);//987654321
				};
			}
		}
	}
	class Program
	{
		public static void Main(string[] args)
		{
			Test t = new Test();
			t.action();
		 }
	}
}

学习过程中参考了以下内容,诚挚感谢知识的分享者!

微软 | Microft docs(C# 编程指南)

菜鸟教程 | C# 教程

唐老狮 | C#四部曲

Suzkfly | C#随机数

请叫我AXin |排序算法:堆排序【图解+代码】

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

睡不着乌托托

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值