(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();
}
}
}
学习过程中参考了以下内容,诚挚感谢知识的分享者!