委托
委托定义
委托是函数(方法)的容器,可以理解为表示函数(方法)的变量类型
用来存储、传递函数(方法)
委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
不同的 函数(方法) 必须对应和各自格式一致的委托
基本语法
关键字: delegate
语法: 访问修饰符 delegate 返回值 委托名(参数列表);
简单来说就是 函数申明语法前面加了delegate关键字
写在哪里
可以申明在namespace 和 class语句块中
更多的写在namespace中
定义自定义委托
//访问修饰符默认不写 为public 在别的命名空间中也能使用
//private 在别的命名空间不能使用
//申明一个用来存储无参无返回值函数的容器
//这里只定义规则,并没有使用
delegate void MyFun();
//委托规则的申明 不能重名(同一语句块中)
//不能重载!
//申明一个用来存储返回值为int 有一个int参数的函数的容器
delegate int MyFun2(int a);
使用定义好的委托
//委托变量是函数的容器
//委托常用在:1.作为类的成员 2.作为函数的参数
class Test
{
public MyFun fun;
public MyFun fun3;
public MyFun2 fun2;
public void TestFun(MyFun fun, MyFun2 fun2)
{
//先处理一些别的逻辑,当这些逻辑处理完了,再执行传入的函数
int i = 1;
i *= 2;
/*fun();
fun2(i);*/
this.fun = fun;
this.fun2 = fun2;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("委托");
//专门用来装载函数的容器
//基本用法
MyFun f = new MyFun(Fun);
Console.WriteLine("1");
f.Invoke();
MyFun f2 = Fun;
Console.WriteLine("1");
f2();
MyFun2 f3 = Fun2;
Console.WriteLine(f3(131));
MyFun2 f4 = new MyFun2(Fun2);
Console.WriteLine(f4.Invoke(2));
//作为类的成员
Test t = new Test();
t.TestFun(Fun,Fun2);
}
static void Fun()
{
Console.WriteLine("123");
}
static int Fun2(int val)
{
return val;
}
}
委托变量可以存储多个函数(多播委托)
//如何用委托存储多个函数
MyFun FF = Fun;
//但不能 MyFun FF += FUN;
//要初始化 可以 MyFun FF = null;
//在类中对象默认为null
FF += Fun;
FF();
//清空容器
FF = null;
static void Fun()
{
Console.WriteLine("123");
}
static void Fun3()
{
Console.WriteLine("321");
}
t.AddFun(Fun, Fun2);
t.fun();
Console.WriteLine(t.fun2(50));
public void AddFun(MyFun fun, MyFun2 fun2)
{
this.fun += fun;
this.fun2 += fun2;
}
//同理可减
系统定义好的委托
//使用系统自带委托 需要引用using System
//无参无返回值
Action act = Fun;
act += Fun3;
act();
//泛型委托
Func<string> funcString = Fun4;
Func<int> funcInt = Fun5;
static string Fun4()
{
return "";
}
static int Fun5()
{
return 4;
}
//委托是支持泛型的 可以让返回值可变 更方便我们的使用
delegate T MyFun3<T, K>(T v, K k);
//可以传n个参数的 最多支持16个
Action<int, string> action2 = Fun6;
static void Fun6(int val, string str)
{
return;
}
//可以传n个参数的 并且有返回值 系统也提供了 16个委托
//前输入 后输出
Func<int, int> func2 = Fun2;
补充
事件
事件定义
事件是基于委托的存在
事体是委托的安全包裹
让委托的使用更具有安全性
事件 是一种特殊的变量类型
事件的使用
申明语法:
访问修饰符 event 委托类型 事件名;
事件的使用:
1.事件是作为 成员变量存在于类中
2.委托怎么用 事件就怎么用
事件相对于委托的区别:
1.不能在类外部 赋值
2.不能在类外部 调用
注意:
它只能作为成员存在于类和接口以及结构体中
class Test
{
//委托成员变量 用于存储 函数的
public Action myFun;
//事件成员变量 用于存储 函数的
public event Action myEvent;
public Test()
{
//事件的使用和委托 一模一样,
//只是有些细微区别
myFun = TestFun;
myEvent = TestFun;
myEvent += TestFun;
myEvent -= TestFun;
myEvent();
myEvent.Invoke();
myEvent = null;
}
public void TestFun()
{
Console.WriteLine("123");
}
public void DoEvent()
{
if(myEvent != null)
{
myEvent();
}
}
}
class Promgram
{
static void Main(string[] args)
{
Test t = new Test();
//委托可以在外部赋值
t.myFun = null;
t.myFun = TestFun1;
//事件不能在外部赋值
//t.myEvent = null;
//t.myEvent = TestFun1;
//但可以+= -=
t.myEvent+= TestFun1;
//委托可以在外部调用
t.myFun();
t.myFun.Invoke();
//事件不能在外部调用
//t.myEvent();
//t.myEvent.Invoke();
//只能在类的内部去封装调用
t.DoEvent();
Action a = TestFun1;
//事件 不能作为临时变量在函数中使用
// event Aciton ae = TestFun1;
}
static void TestFun1()
{
}
}
为什么要事件
1.防止外部随意置空委托
2.防止外部随意调用委托
3.事件相当于对委托进行了一次封装 让其更加安全
总结
事件和委托的区别事件和委托的使用基本是一模一样的
事件就是特殊的委托
主要区别:
1.事件不能在外部使用赋值=符号,只能使用+-=(注意只能用复合运算符) 委托 哪里都能用
2.事件 不能在外部执行 委托哪里都能执行
3.事件 不能作为 函数中的临时变量的 委托可以
匿名函数
匿名函数定义
顾名思义,就是没有名字的函数
匿名函数的使用主要是配合委托和事件进行使用
脱离委托和事件 是不会使用匿名函数的
基本语法
delegate (参数列表)
{
//函数逻辑
};
何时使用:
1.函数中传递委托参数时
2.委托或事件赋值时
使用
class Program
{
static void Main(string[] args)
{
//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 "返回值";
};
Console.WriteLine(c());
//4.一般情况下 会作为函数参数传递 或者 作为函数返回值
Test t = new Test();
//参数传递
t.Dosomething(100, delegate(){
Console.WriteLine("随参数传入的匿名函数逻辑");
});
//返回值
Action ac2 = t.GetFun();
ac2();
//或者 一步到位 直接调用返回的委托函数
t.GetFun()();
}
class Test
{
public Action action;
//作为参数传递时
public void Dosomething(int a, Action fun)
{
Console.WriteLine(a);
fun();
}
//作为返回值
public Action GetFun()
{
return delegate ()
{
Console.WriteLine("函数内部返回的匿名函数逻辑");
};
}
}
}
缺点
添加到委托或事件容器中后 不记录 无法单独移除
//缺点
//添加到委托或事件容器中后 不记录 无法单独移除
Action ac3 = delegate ()
{
Console.WriteLine("匿名函数1");
};
ac3 += delegate ()
{
Console.WriteLine("匿名函数2");
};
ac3();
//因为匿名函数没有名字 所以没有办法指定移除某一个匿名函数
//以下方法没用 此匿名函数 非彼命名函数,虽然逻辑一样,但不是同一个
/*ac3 -= delegate ()
{
Console.WriteLine("匿名函数1");
};
ac3();*/
ac3 = null;
总结
匿名函数 就是没有名字的函数
固定写法delegate(参数列表){}
主要是在 委托传递和存储时 为了方便可以直接使用匿名函数
缺点是 没有办法指定移除
练习题
写一个函数,传入一个整数,返回一个函数,之后执行这个匿名函数时传入一个整数和之前那个函数传入的数相乘
返回结果
Func<int, int> fun = TestFun(2);
Console.WriteLine(fun(3));
static Func<int,int> TestFun(int i)
{
//这种写法改变了i的生命周期
return delegate (int v)
{
return i * v;
};
}
Lambda表达式
Lambda表达式定义
可以将lambda表达式 理解为匿名函数的简写
它除了写法不同外
使用上和匿名函数一模一样
都是和委托或者事件 配合使用的
语法
//匿名函数
delegate(参数列表)
{
};
//lambda表达式
(参数列表) =>
{
//函数体
};
使用
class Promgram
{
static void Main(string[] args)
{
//1.无参无返回
Action a = () =>
{
Console.WriteLine("无参无返回");
};
a();
//2.有参
Action<int> a2 = (int value) =>
{
Console.WriteLine("有参数:{0}", value);
};
a2(10);
//3.甚至参数类型可以省略 参数类型和委托或事件容器一致
Action<int> a3 = (value) =>
{
Console.WriteLine("省略参数类型:{0}",value);
};
a3(100);
//4.有返回值
Func<string, int> a4 = (value) =>
{
Console.WriteLine("有返回值,参数:{0}", value);
return 1;
};
Console.WriteLine(a4("阿喆不想学习"));
//其它传参使用等和匿名函数一样
//缺点也和匿名函数一样
}
}
闭包
内层的函数可以引用包含在它外层的函数的变量
即使外层函数的执行已经终止
注意:
该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。
Test t = new Test();
t.DoSomething();
class Test
{
public event Action action;
public Test()
{
int value = 10;
//这里就形成了闭包
//当构造函数执行完毕时,改变了value的生命周期
action = () =>
{
Console.WriteLine(value);
};
for (int i = 0; i < 10; i++)
{
action += () =>
{
Console.WriteLine(i);
};
}
}
public void DoSomething()
{
action();
}
}
最后结果
体现了 该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值
因为i始终是同一个i, 但如果在循环中定义 int index = i,存入委托的话,又不一样,因为每次index不同。
总结
总结
匿名函数的特殊写法 就是 lambad表达式
固定写法 就是(参数列表)=>{}
参数列表 可以直接省略参数类型
主要在 委托传递和存储时 为了方便可以直接使用匿名函数或者lambad表达式
缺点:无法指定移除