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.作为函数的参数

cs 复制代码
    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

知识点五 委托变量可以存储多个函数(多播委托)

cs 复制代码
   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.结合里氏替换原则理解

cs 复制代码
    class 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修饰的 泛型委托 可以相互装载(有父子关系的泛型)

协变 父类泛型委托装子类泛型委托 逆变 子类泛型委托装父类泛型委托

}

相关推荐
代码的余温30 分钟前
Maven引入第三方JAR包实战指南
java·maven·jar
pianmian14 小时前
类(JavaBean类)和对象
java
我叫小白菜4 小时前
【Java_EE】单例模式、阻塞队列、线程池、定时器
java·开发语言
天水幼麟4 小时前
动手学深度学习-学习笔记(总)
笔记·深度学习·学习
Albert Edison5 小时前
【最新版】IntelliJ IDEA 2025 创建 SpringBoot 项目
java·spring boot·intellij-idea
超级小忍5 小时前
JVM 中的垃圾回收算法及垃圾回收器详解
java·jvm
weixin_446122465 小时前
JAVA内存区域划分
java·开发语言·redis
勤奋的小王同学~6 小时前
(javaEE初阶)计算机是如何组成的:CPU基本工作流程 CPU介绍 CPU执行指令的流程 寄存器 程序 进程 进程控制块 线程 线程的执行
java·java-ee
TT哇6 小时前
JavaEE==网站开发
java·redis·java-ee
2401_826097626 小时前
JavaEE-Linux环境部署
java·linux·java-ee