C#每日学习日记
委托
知识点一 委托是什么
委托是 函数(方法)的容器
可以理解为表示函数(方法)的变量类型
用来 存储、传递函数(方法)
委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
不同的 函数(方法)必须对应和各自"格式"一致的委托
知识点二 基本语法
关键字 : 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.作为函数的参数class 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
知识点五 委托变量可以存储多个函数(多播委托)
class 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)
//{
// if (a.id > b.id)
// {
// 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做标准 负数在左(前) 正数在右(后)
if (a.id > b.id)
{
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.结合里氏替换原则理解class Father{}class Son:Father{}class Program{static void Main(string[] args){Console.WriteLine("协变逆变");#region 知识点二 作用(结合里氏替换原则理解)//协变 父类总是能被子类替换// 看起来 就是 son ——> fatherTestOut<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修饰的 泛型委托 可以相互装载(有父子关系的泛型)
协变 父类泛型委托装子类泛型委托 逆变 子类泛型委托装父类泛型委托
}