委托
知识点一 委托是什么
委托是 函数(方法)的容器
可以理解为表示函数(方法)的变量类型
用来 存储、传递函数(方法)
委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
不同的 函数(方法)必须对应和各自"格式"一致的委托
知识点二 基本语法
关键字 :delegate
语法:
访问修饰符 delegate 返回值 委托名(参数列表);
写在哪里?
可以申明在namespace和class语句块中
更多的写在namespace中
简单记忆委托语法 就是 函数申明语法前面加一个delegate关键字
知识点三 定义自定义委托
访问修饰默认不写 为public 在别的命名空间中也能使用
private 其它命名空间就不能用了
一般使用public
申明了一个可以用来存储无参无返回值函数的容器
这里只是定义了规则 并没有使用
delegate void MyFun();
委托规则的申明 是不能重名(同一语句块中)
表示用来装载或传递 返回值为int 有一个int参数的函数的 委托 容器规则
public delegate int MyFun2(int a);
委托是支持 泛型的 可以让返回值和参数 可变 更方便我们的使用
delegate T MyFun3<T, K>(T v, K k);
知识点四 使用定义好的委托
委托变量是函数的容器 委托常用在:
1.作为类的成员
2.作为函数的参数
csclass Test { public MyFun fun; public MyFun2 fun2; public Action action; 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 RemoveFun(MyFun fun, MyFun2 fun2) { //this.fun = this.fun - fun; this.fun -= fun; this.fun2 -= fun2; } #endregion } #endregion
知识点五 委托变量可以存储多个函数(多播委托)
csclass 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(); MyFun f2 = Fun; Console.WriteLine("1"); Console.WriteLine("2"); Console.WriteLine("3"); Console.WriteLine("4"); Console.WriteLine("5"); f2(); MyFun2 f3 = Fun2;//注意:这里使用的是方法组转换(method group conversion),也就是简写形式,自动匹配方法签名。 Console.WriteLine(f3(1)); MyFun2 f4 = new MyFun2(Fun2); Console.WriteLine(f4.Invoke(3)); Test t = new Test(); t.TestFun(Fun, Fun2); Console.WriteLine("***************"); //如何用委托存储多个函数 MyFun ff = null; //ff = ff + Fun; ff += Fun; ff += Fun3; ff(); //从容器中移除指定的函数 ff -= Fun; //多减 不会报错 无非就是不处理而已 ff -= Fun; ff(); //清空容器 ff = null; if( ff != null ) { ff(); } } static void Fun() { Console.WriteLine("张三做什么"); } static void Fun3() { Console.WriteLine("李四做什么"); } static string Fun4() { return ""; } static int Fun5() { return 1; } static void Fun6(int i, string s) { } static int Fun2(int value) { return value; } }
知识点六 系统定义好的委托
使用系统自带委托 需要引用using System;
无参无返回值
Action action = Fun;
action += Fun3;
action();
可以指定返回值类型的 泛型委托
Func<string> funcString = Fun4;
Func<int> funcInt = Fun5;
可以传n个参数的 系统提供了 1到16个参数的委托 直接用就行了
Action<int, string> action2 = Fun6;
可以传n个参数的 并且有返回值的 系统也提供了 16个委托
Func<int, int> func2 = Fun2;
总结
简单理解 委托 就是装载、传递函数的容器而已
可以用委托变量 来存储函数或者传递函数的
系统其实已经提供了很多委托给我们用
Action:没有返回值,参数提供了 0~16个委托给我们用
Func:有返回值,参数提供了 0~16个委托给我们用委托本质上是一个 函数指针的封装体,它可以把方法当作参数进行传递或赋值。
-
回调函数(Callback)
-
事件机制(Event)
-
策略模式(Strategy Pattern)
-
组合多个方法执行(多播委托)
List排序
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Lesson16_List排序
{
class Item : IComparable<Item>
{
public int money;
public Item(int money)
{
this.money = money;
}
public int CompareTo(Item other)
{
返回值的含义
小于0:
放在传入对象的前面
等于0:
保持当前的位置不变
大于0:
放在传入对象的后面可以简单理解 传入对象的位置 就是0
如果你的返回为负数 就放在它的左边 也就前面
如果你返回正数 就放在它的右边 也就是后面if( this.money > other.money )
{
return -1;
}
else
{
return 1;
}
}
}
class ShopItem
{
public int id;
public ShopItem(int id)
{
this.id = id;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("List排序");
#region 知识点一 List自带排序方法
List<int> list = new List<int>();
list.Add(3);
list.Add(2);
list.Add(6);
list.Add(1);
list.Add(4);
list.Add(5);
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
list提供了排序方法
list.Sort();
Console.WriteLine("**************");
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
ArrayList中也有Sort排序方法
#endregion
#region 知识点二 自定义类的排序
List<Item> itemList = new List<Item>();
itemList.Add(new Item(45));
itemList.Add(new Item(10));
itemList.Add(new Item(99));
itemList.Add(new Item(24));
itemList.Add(new Item(100));
itemList.Add(new Item(12));
排序方法
itemList.Sort();
for (int i = 0; i < itemList.Count; i++)
{
Console.WriteLine(itemList[i].money);
}
#endregion
#region 知识点三 通过委托函数进行排序
List<ShopItem> shopItems = new List<ShopItem>();
shopItems.Add(new ShopItem(2));
shopItems.Add(new ShopItem(1));
shopItems.Add(new ShopItem(4));
shopItems.Add(new ShopItem(3));
shopItems.Add(new ShopItem(6));
shopItems.Add(new ShopItem(5));
//shopItems.Sort(SortShopItem);
匿名函数
//shopItems.Sort(delegate (ShopItem a, ShopItem b)
//{
// {
// return -1;
// }
// else
// {
// return 1;
// }
//});
lambad表达式 配合 三目运算符的 完美呈现
shopItems.Sort((a, b) =>{ return a.id > b.id ? 1 : -1;});
Console.WriteLine("*********************");
for (int i = 0; i < shopItems.Count; i++)
{
Console.WriteLine(shopItems[i].id);
}
#endregion
}
static int SortShopItem( ShopItem a, ShopItem b )
{
传入的两个对象 为列表中的两个对象
进行两两的比较 用左边的和右边的条件 比较
返回值规则 和之前一样 0做标准 负数在左(前) 正数在右(后){
return -1;
}
else
{
return 1;
}
}
}
总结
系统自带的变量(int,float,double.....) 一般都可以直接Sort
自定义类Sort有两种方式
1.继承接口 IComparable
2.在Sort中传入委托函数}
协变逆变
知识点一 什么是协变逆变
协变:
和谐的变化,自然的变化
因为 里氏替换原则 父类可以装子类
所以 子类变父类
比如 string 变成 object
感受是和谐的
逆变:
逆常规的变化,不正常的变化
因为 里氏替换原则 父类可以装子类 但是子类不能装父类
所以 父类变子类
比如 object 变成 string
感受是不和谐的
协变和逆变是用来修饰泛型的
协变:out
逆变:in用于在泛型中 修饰 泛型字母的
只有泛型接口和泛型委托能使用
知识点二 作用
1.返回值 和 参数
用out修饰的泛型 只能作为返回值
delegate T TestOut<out T>();
用in修饰的泛型 只能作为参数
delegate void TestIn<in T>(T t);
2.结合里氏替换原则理解
csclass Father { } class Son:Father { } class Program { static void Main(string[] args) { Console.WriteLine("协变逆变"); #region 知识点二 作用(结合里氏替换原则理解) //协变 父类总是能被子类替换 // 看起来 就是 son ------> father TestOut<Son> os = () => { return new Son(); }; TestOut<Father> of = os; Father f = of();//实际上 返回的 是os里面装的函数 返回的是Son //逆变 父类总是能被子类替换 //看起来像是 father------>son 明明是传父类 但是你传子类 不和谐的 TestIn<Father> iF = (value) => { }; TestIn<Son> iS = iF; iS(new Son());//实际上 调用的是 iF #endregion } }
总结
协变 out
逆变 in用来修饰 泛型替代符的 只能修饰接口和委托中的泛型
作用两点
1.out修饰的泛型类型 只能作为返回值类型 in修饰的泛型类型 只能作为 参数类型
2.遵循里氏替换原则的 用out和in修饰的 泛型委托 可以相互装载(有父子关系的泛型)协变 父类泛型委托装子类泛型委托 逆变 子类泛型委托装父类泛型委托
}