13.委托

13.委托和事件-委托


13.1 知识点

委托是什么

委托是函数(方法)的容器,可以理解为表示函数(方法)的变量类型,用来存储、传递函数(方法)。委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)。不同的函数(方法)必须对应和各自”格式”一致的委托。

申明委托的基本语法

关键字:delegate

语法:访问修饰符 delegate 返回值 委托名(参数列表);

写在哪里?

可以申明在 namespaceclass 语句块中,更多的写在 namespace 中。

简单记忆委托语法就是函数申明语法前面加一个 delegate 关键字。

委托的访问修饰默认不写为 public,在别的命名空间中也能使用。一般使用 public

定义自定义委托

// class语句块外 namespace语句块内
// 申明了一个可以用来存储无参无返回值函数的容器
// 这里只是定义了规则 并没有使用
delegate void MyDelegate1();

// 委托规则的申明 是不能重名(同一语句块中)没有重载的概念
// 表示用来装载或传递 返回值为int 有一个int参数的函数的 委托 容器规则
public delegate int MyDelegate2(int a);

// 委托是支持 泛型的 可以让返回值和参数 可变 更方便我们的使用
delegate T MyDelegate3<T, K>(T v, K k);

使用委托的基本语法和具体使用

基本语法

  • 存入函数到委托中的语法1:委托名 委托变量 = new 委托名(要存入的函数名);
  • 存入函数到委托中的语法2:委托名 委托变量 = 要存入的函数名;
  • 注意1:要存入的函数名中不带括号,只是函数名。
  • 注意2:当声明的委托类型变量和存入的函数不符时,报错。
  • 执行委托语法1:委托变量.Invoke();
  • 执行委托语法2:委托变量();
// class语句块内 主函数外
// 写几个方法当做使用委托时的例子

// 张三做什么的方法 无参无返回值的 
static void ZhangSanDoing()
{
    Console.WriteLine("张三做什么");
}

// 李四做什么的方法 无参无返回值的 
static void LiSiDoing()
{
    Console.WriteLine("李四做什么");
}

// 返回传进来的整数的方法 一个参数int 返回值int
static int ReturnInputInt(int value)
{
    return value;
}

// 返回空字符串的方法 
static string RetrunEmptyString()
{
    return "";
}

// 返回整型变量1的方法 
static int ReturnInt1()
{
    return 1;
}

// 参数是int和string没有返回值的方法
static void ParamIntAndStringReturnVoid(int i, string s)
{

}

主函数内

// MyDelegate1是无参无返回值的委托类型
// 声明MyDelegate1委托类型的MyDelegate11委托变量
// new出 MyDelegate11的时候 构造函数直接传入 张三做什么方法
// 用MyDelegate11来装载 张三做什么这个无参无返回值的方法
MyDelegate1 MyDelegate11 = new MyDelegate1(ZhangSanDoing);
// 这个时候不会有任何的调用,只是让 张三做什么方法存进去

// 调用MyDelegate11的Invoke方法 相当于调用MyDelegate1里装的所有函数
MyDelegate11.Invoke(); // 张三做什么
// 现在MyDelegate11只装了 张三做什么这个无参无返回值的方法
// 所以只会打印出 张三做什么

// 声明MyDelegate1委托类型的MyDelegate12委托变量
// 直接让 MyDelegate12 等于 张三做什么方法
// 用MyDelegate12来装载 张三做什么这个无参无返回值的方法
MyDelegate1 MyDelegate12 = ZhangSanDoing;
// 这个时候不会有任何的调用,只是让 张三做什么方法存进去

// MyDelegate12加一个括号 可以理解为把这个委托变量当做函数来调用 
MyDelegate12(); // 张三做什么
// 现在MyDelegate12只装了 张三做什么这个无参无返回值的方法
// 所以只会打印出 张三做什么

// MyDelegate2是一个参数int返回值int的委托类型
// new出MyDelegate21的时候 构造函数直接传入 返回传进来的整数的方法
MyDelegate2 MyDelegate21 = new MyDelegate2(ReturnInputInt);

// 调用MyDelegate21的Invoke方法 相当于调用ReturnInputInt 打印返回值
Console.WriteLine(MyDelegate21.Invoke(3)); // 3

// 直接让 MyDelegate22 等于 返回传进来的整数的方法
MyDelegate2 MyDelegate22 = ReturnInputInt;

// MyDelegate22加一个括号 可以理解为把这个委托变量当做函数来调用 打印返回值
Console.WriteLine(MyDelegate22(1)); // 1

// 当声明的委托类型变量和存入的函数不符时,报错
// 比如下面的 一个参数int返回值int的委托类型 不能和 无参不返回值函数匹配
// MyDelegate2 MyDelegate23 = ZhangSanDoing; // 报错
// MyDelegate2 MyDelegate24 = new MyDelegate2(ZhangSanDoing); // 报错

在类和函数中使用定义好的委托

class语句块外 namespace语句块内

// 委托变量是函数的容器
// 委托常用在:
// 1. 作为类的成员
// 2. 作为函数的参数

// 测试委托类
class TestDelegateClass
{
    // 定义委托变量
    public MyDelegate1 myDelegate1;
    public MyDelegate2 myDelegate2;

    public Action action;

    // 测试委托方法 相当于外部传入委托 再在里面执行
    // 为什么要传入委托再在函数中执行呢?这样看上去不是多此一举吗?
    // 因为有时候可能不知道这个委托要传入的参数是什么
    // 有时候要在函数中经过一些逻辑计算后 才知道这个委托要传入的参数是什么
    // 也可以把函数存起来 延迟执行
    public void TestDelegate(MyDelegate1 myDelegate1, MyDelegate2 myDelegate2)
    {
        // 可以先处理一些别的逻辑
        // int i = 1;
        // i *= 2;
        // i += 2;

        // 有时候要在函数中经过一些逻辑计算后 才知道这个委托要传入的参数是什么
        // myDelegate1();
        // myDelegate2(i);

        // 也可以把函数存起来 延迟执行
        // this.myDelegate1 = myDelegate1;
        // this.myDelegate2 = myDelegate2;
    }
}

主函数内

// 创建测试委托类
TestDelegateClass testDelegateClass1 = new TestDelegateClass();
// 调用测试委托类的测试委托方法 传入要使用的函数
testDelegateClass1.TestDelegate(ZhangSanDoing, ReturnInputInt);

委托变量可以存储多个函数(多播委托)

基本语法

委托变量添加要存储的函数的语法:委托变量 += 函数名委托变量 = 委托变量 + 函数名

委托变量移除要存储的函数的语法:委托变量 -= 函数名委托变量 = 委托变量 - 函数名

一个委托变量假如存了多个函数,会按添加的顺序逐个执行。移除的话会移除首个对应的函数。

注意:不能创建委托变量的时候同时添加或移除目标函数,会报错。

主函数内

// 创建MyDelegate1委托类型的myMulticastDelegate1委托变量 先设置为空
// (myMulticastDelegate1委托变量中文意思 我的多播委托1)
MyDelegate1 myMulticastDelegate1 = null;

// 存入张三做什么和李四做什么的方法
// myMulticastDelegate1 = myMulticastDelegate1 + ZhangSanDoing;
myMulticastDelegate1 += ZhangSanDoing;
myMulticastDelegate1 += LiSiDoing;
// 执行委托
myMulticastDelegate1();
// 张三做什么
// 李四做什么

// 移除张三做什么方法
myMulticastDelegate1 -= ZhangSanDoing;

// 多减一个张三做什么方法 不会报错 无非就是不处理而已
myMulticastDelegate1 -= ZhangSanDoing;
// 执行委托
myMulticastDelegate1();
// 李四做什么

// 清空容器
myMulticastDelegate1 = null;

// 执行委托时 假如委托为空 会报错
// 所以最好先判空在执行
if (myMulticastDelegate1 != null)
{
    myMulticastDelegate1();
}

// 调用类的封装方法实现多播委托
// 增加要存储的函数
testDelegateClass1.AddMyDelegate(ZhangSanDoing, ReturnInputInt);
// 移除要存储的函数
testDelegateClass1.RemoveMyDelegate(ZhangSanDoing, ReturnInputInt);

测试委托类内

// 可以把委托增加要存储的函数和移除要存储的函数在类中封装成方法再调用

// 委托中增加要存储的函数
public void AddMyDelegate(MyDelegate1 fun, MyDelegate2 fun2)
{
    this.myDelegate1 += fun;
    this.myDelegate2 += fun2;
}

// 委托中增移除要存储的函数
public void RemoveMyDelegate(MyDelegate1 fun, MyDelegate2 fun2)
{
    this.myDelegate1 -= fun;
    this.myDelegate2 -= fun2;
}

系统定义好的委托

使用系统自带委托需要引用 using System;

系统自带 无参无返回值 的委托

// 系统自带 无参无返回值 的委托:Action 委托变量 = 函数名;
Action action1 = ZhangSanDoing;
action1 += LiSiDoing;
action1();
// 张三做什么
// 李四做什么

系统自带 指定返回值类型的 泛型委托

// 系统自带 指定返回值类型的 泛型委托:Func<返回值类型> 委托变量 = 函数名;
Func<string> funcString = RetrunEmptyString;
Func<int> funcInt = ReturnInt1;

系统自带 可以传n个参数的 泛型委托

// 系统自带 可以传n个参数的 泛型委托:Action<类型1, 类型2,...> 委托变量 = 函数名;
// 系统提供了 1到16个参数的委托
// 假如你的函数超过16个参数 那对应的委托只能自己定义 不能用系统自带的了
Action<int, string> action2 = ParamIntAndStringReturnVoid;

系统自带 可以传n个参数的 并且有返回值的 泛型委托

// 系统自带 可以传n个参数的 并且有返回值的 泛型委托:Func<类型1, 类型2,...,返回值类型> 委托变量 = 函数名;
// 系统提供了 1到16个参数的委托 最后一个传入类型固定是返回值的类型
// 假如你的函数超过16个参数 那对应的委托只能自己定义 不能用系统自带的了
Func<int, int> funcParamIntReturnInt = ReturnInputInt;

总结

  • 简单理解委托就是装载、传递函数的容器而已。
  • 可以用委托变量来存储函数或者传递函数。
  • 系统其实已经提供了很多委托给我们用。
  • Action: 没有返回值,参数提供了 0~16 个委托给我们用。
  • Func: 有返回值,参数提供了 0~16 个委托给我们用,最后一个传入的参数充当返回值类型。

13.2 知识点代码

using System;

namespace Lesson12_委托
{
    #region 知识点一 委托是什么
    //委托是 函数(方法)的容器 
    //可以理解为表示函数(方法)的变量类型
    //用来 存储、传递函数(方法)
    //委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
    //不同的 函数(方法)必须对应和各自"格式"一致的委托
    #endregion

    #region 知识点二 申明委托的基本语法
    //关键字 : delegate
    //语法:访问修饰符 delegate 返回值 委托名(参数列表);

    //写在哪里?
    //可以申明在namespace和class语句块中
    //更多的写在namespace中

    //简单记忆委托语法 就是 函数申明语法前面加一个delegate关键字

    //委托的访问修饰默认不写 为public 在别的命名空间中也能使用
    //private 其它命名空间就不能用了
    //一般使用public
    #endregion

    //class语句块外 namespace语句块内

    #region 知识点三 定义自定义委托

    //申明了一个可以用来存储无参无返回值函数的容器
    //这里只是定义了规则 并没有使用
    delegate void MyDelegate1();

    //委托规则的申明 是不能重名(同一语句块中)没有重载的概念
    //表示用来装载或传递 返回值为int 有一个int参数的函数的 委托 容器规则
    public delegate int MyDelegate2(int a);

    //委托是支持 泛型的 可以让返回值和参数 可变 更方便我们的使用
    delegate T MyDelegate3<T, K>(T v, K k);

    #endregion

    #region 知识点五 在类和函数中使用定义好的委托

    //委托变量是函数的容器
    //委托常用在:
    //1.作为类的成员
    //2.作为函数的参数

    //测试委托类
    class TestDelegateClass
    {
        //定义委托变量
        public MyDelegate1 myDelegate1;
        public MyDelegate2 myDelegate2;

        public Action action;

        //测试委托方法 相当于外部传入委托 再在里面执行
        //为什么要传入委托再在函数中执行呢?这样看上去不是多此一举吗?
        //因为有时候可能不知道这个委托要传入的参数是什么
        //有时候要在函数中经过一些逻辑计算后 才知道这个委托要传入的参数是什么
        //也可以把函数存起来 延迟执行
        public void TestDelegate(MyDelegate1 myDelegate1, MyDelegate2 myDelegate2)
        {
            //可以先处理一些别的逻辑
            //int i = 1;
            //i *= 2;
            //i += 2;

            //有时候要在函数中经过一些逻辑计算后 才知道这个委托要传入的参数是什么
            //myDelegate1();
            //myDelegate2(i);

            //也可以把函数存起来 延迟执行
            //this.myDelegate1 = myDelegate1;
            //this.myDelegate2 = myDelegate2;
        }




        //测试委托类内

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

        //可以把委托增加要存储的函数和移除要存储的函数在类中封装成方法再调用

        //委托中增加要存储的函数
        public void AddMyDelegate(MyDelegate1 fun, MyDelegate2 fun2)
        {
            this.myDelegate1 += fun;
            this.myDelegate2 += fun2;
        }

        //委托中增移除要存储的函数
        public void RemoveMyDelegate(MyDelegate1 fun, MyDelegate2 fun2)
        {
            this.myDelegate1 -= fun;
            this.myDelegate2 -= fun2;
        }

        #endregion


    }
    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("委托");

            //主函数内

            #region 知识点四 使用委托的基本语法和具体使用

            //基本语法

            //存入函数到委托中的语法1:委托名 委托变量 = new 委托名(要存入的函数名);
            //new出委托变量的时候构造函数直接传入要存入的函数名

            //存入函数到委托中的语法2:委托名 委托变量 = 要存入的函数名;
            //直接让委托变量等于要存入的函数名

            //注意1:要存入的函数名中不带括号 只是函数名

            //注意2:当声明的委托类型变量和存入的函数不符时 报错

            //执行委托语法1:委托变量.Invoke();
            //调用委托变量的Invoke方法 会调用所有存入当前委托变量的函数

            //执行委托语法2:委托变量();
            //委托变量加一个括号 可以理解为把这个委托变量当做函数来调用  会调用所有存入当前委托变量的函数





            //MyDelegate1是无参无返回值的委托类型

            //声明MyDelegate1委托类型的MyDelegate11委托变量
            //new出 MyDelegate11的时候 构造函数直接传入 张三做什么方法
            //用MyDelegate11来装载 张三做什么这个无参无返回值的方法
            MyDelegate1 MyDelegate11 = new MyDelegate1(ZhangSanDoing);
            //这个时候不会有任何的调用 只是让 张三做什么方法存进去

            //调用MyDelegate11的Invoke方法 相当于调用MyDelegate1里装的所有函数
            MyDelegate11.Invoke();// 张三做什么 
            //现在MyDelegate11只装了 张三做什么这个无参无返回值的方法
            //所以只会打印出 张三做什么

            //声明MyDelegate1委托类型的MyDelegate12委托变量
            //直接让 MyDelegate12 等于 张三做什么方法
            //用MyDelegate12来装载 张三做什么这个无参无返回值的方法
            MyDelegate1 MyDelegate12 = ZhangSanDoing;
            //这个时候不会有任何的调用 只是让 张三做什么方法存进去

            //MyDelegate12加一个括号 可以理解为把这个委托变量当做函数来调用 
            MyDelegate12();//张三做什么
            //现在MyDelegate12只装了 张三做什么这个无参无返回值的方法
            //所以只会打印出 张三做什么






            //MyDelegate2是一个参数int返回值int的委托类型

            //new出MyDelegate21的时候 构造函数直接传入 返回传进来的整数的方法
            MyDelegate2 MyDelegate21 = new MyDelegate2(ReturnInputInt);

            //调用MyDelegate21的Invoke方法 相当于调用ReturnInputInt 打印返回值
            Console.WriteLine(MyDelegate21.Invoke(3));//3

            //直接让 MyDelegate22 等于 返回传进来的整数的方法
            MyDelegate2 MyDelegate22 = ReturnInputInt;

            //MyDelegate22加一个括号 可以理解为把这个委托变量当做函数来调用 打印返回值
            Console.WriteLine(MyDelegate22(1));//1

            //当声明的委托类型变量和存入的函数不符时 报错
            //比如下面的 一个参数int返回值int的委托类型 不能和 无参不返回值函数匹配
            //MyDelegate2 MyDelegate23 = ZhangSanDoing;//报错
            //MyDelegate2 MyDelegate24 = new MyDelegate2(ZhangSanDoing);//报错

            #endregion

            #region 知识点五 在类和函数中使用定义好的委托

            //创建测试委托类
            TestDelegateClass testDelegateClass1 = new TestDelegateClass();
            //调用测试委托类的测试委托方法 传入要使用的函数
            testDelegateClass1.TestDelegate(ZhangSanDoing, ReturnInputInt);

            #endregion

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

            //基本语法

            //委托变量添加要存储的函数的语法:委托变量 += 函数名 或 委托变量 = 委托变量 + 函数名
            //委托变量移除要存储的函数的语法:委托变量 -= 函数名 或 委托变量 = 委托变量 - 函数名
            //一个委托变量假如存了多个函数 会按添加的顺序逐个执行 移除的话会移除首个对应的函数

            //注意:不能创建委托变量的时候同时添加或移除目标函数 会报错 要创好的委托变量再哪来用
            //MyDelegate1 mMD1 += ZhangSanDoing;//报错
            //MyDelegate1 mMD2 -= LiSiDoing;//报错

            //创建MyDelegate1委托类型的myMulticastDelegate1委托变量 先设置为空
            //(myMulticastDelegate1委托变量中文意思 我的多播委托1)
            MyDelegate1 myMulticastDelegate1 = null;

            //存入张三做什么和李四做什么的方法
            //myMulticastDelegate1 = myMulticastDelegate1 + ZhangSanDoing;
            myMulticastDelegate1 += ZhangSanDoing;
            myMulticastDelegate1 += LiSiDoing;
            //执行委托
            myMulticastDelegate1();
            //张三做什么
            //李四做什么

            //移除张三做什么方法
            myMulticastDelegate1 -= ZhangSanDoing;

            //多减一个张三做什么方法 不会报错 无非就是不处理而已
            myMulticastDelegate1 -= ZhangSanDoing;
            //执行委托
            myMulticastDelegate1();
            //李四做什么

            //清空容器
            myMulticastDelegate1 = null;

            //执行委托时 假如委托为空 会报错
            //所以最好先判空在执行
            if (myMulticastDelegate1 != null)
            {
                myMulticastDelegate1();
            }



            //调用类的封装方法实现多播委托
            //增加要存储的函数
            testDelegateClass1.AddMyDelegate(ZhangSanDoing, ReturnInputInt);
            //移除要存储的函数
            testDelegateClass1.RemoveMyDelegate(ZhangSanDoing, ReturnInputInt);

            #endregion

            #region 知识点七 系统定义好的委托

            //使用系统自带委托 需要引用using System;

            //系统自带 无参无返回值 的委托:Action 委托变量 = 函数名;
            Action action1 = ZhangSanDoing;
            action1 += LiSiDoing;
            action1();
            //张三做什么
            //李四做什么

            //系统自带 指定返回值类型的 泛型委托:Func<返回值类型> 委托变量 = 函数名;
            Func<string> funcString = RetrunEmptyString;
            Func<int> funcInt = ReturnInt1;

            //系统自带 可以传n个参数的 泛型委托:Action<类型1, 类型2,...> 委托变量 = 函数名;
            //系统提供了 1到16个参数的委托
            //假如你的函数超过16个参数 那对应的委托只能自己定义 不能用系统自带的了
            Action<int, string> action2 = ParamIntAndStringReturnVoid;

            //系统自带 可以传n个参数的 并且有返回值的 泛型委托:Func<类型1, 类型2,...,返回值类型> 委托变量 = 函数名;
            //系统提供了 1到16个参数的委托 最后一个传入类型固定是返回值的类型
            //假如你的函数超过16个参数 那对应的委托只能自己定义 不能用系统自带的了
            Func<int, int> funcParamIntReturnInt = ReturnInputInt;

            #endregion
        }

        //class语句块内 主函数外

        #region 知识点四 使用委托的基本语法

        //写几个方法 当做使用委托时的例子

        //张三做什么的方法 无参无返回值的 
        static void ZhangSanDoing()
        {
            Console.WriteLine("张三做什么");
        }

        //李四做什么的方法 无参无返回值的 
        static void LiSiDoing()
        {
            Console.WriteLine("李四做什么");
        }

        //返回传进来的整数的方法 一个参数int 返回值int
        static int ReturnInputInt(int value)
        {
            return value;
        }

        //返回空字符串的方法 
        static string RetrunEmptyString()
        {
            return "";
        }

        //返回 整型变量1 的方法 
        static int ReturnInt1()
        {
            return 1;
        }

        //参数是int和string没有返回值的方法
        static void ParamIntAndStringReturnVoid(int i, string s)
        {

        }

        #endregion

    }

    //总结
    //简单理解 委托 就是装载、传递函数的容器而已
    //可以用委托变量 来存储函数或者传递函数的
    //系统其实已经提供了很多委托给我们用 
    //Action:没有返回值,参数提供了 0~16个委托给我们用
    //Func:有返回值,参数提供了 0~16个委托给我们用 最后一个传入的参数充当返回值类型
}

13.3 练习题

一家三口,妈妈做饭,爸爸妈妈和孩子都要吃饭,用委托模拟做饭——>开饭——>吃饭的过程

class语句块外 namespace语句块内

// 人类基类
abstract class Person
{
    // 共同行为 吃饭
    public abstract void Eat();
}

// 妈妈类
class Mother : Person
{
    // 开始吃饭委托变量 存开始吃饭时要执行的函数
    public Action beginEat;

    public override void Eat()
    {
        Console.WriteLine("妈妈吃饭");
    }

    // 妈妈做饭方法
    public void Cook()
    {
        Console.WriteLine("妈妈做饭");

        Console.WriteLine("妈妈做饭做好了");

        // 做好饭了 执行开始吃饭委托
        beginEat?.Invoke();
    }
}

// 爸爸类
class Father : Person
{
    public override void Eat()
    {
        Console.WriteLine("爸爸吃饭");
    }
}

// 孩子类
class Child : Person
{
    public override void Eat()
    {
        Console.WriteLine("孩子吃饭");
    }

主函数内

// 创建爸爸妈妈孩子
Mother mother = new Mother();
Father father = new Father();
Child child = new Child();

// 告诉妈妈 一会做好饭了 爸爸妈妈孩子都要吃饭
// 所以吃饭的函数都存在妈妈的开始吃饭委托中
mother.beginEat += father.Eat;
mother.beginEat += child.Eat;
mother.beginEat += mother.Eat;

// 让妈妈做饭
mother.Cook();

// 妈妈做饭
// 妈妈做饭做好了
// 爸爸吃饭
// 孩子吃饭
// 妈妈吃饭

怪物死亡后,玩家要加10块钱,面板界面要更新数据,成就要累加怪物击杀数,请用委托来模拟实现这些功能,只用写核心逻辑表现这个过程,不用写的太复杂

class语句块外 namespace语句块内

// 怪物类
class Monster
{
    // 当怪物死亡时 把自己作为参数传出去 
    public Action<Monster> deadDoSomthing;

    // 怪物成员变量 特征 价值多少钱
    public int money = 10;

    // 死亡函数
    public void Dead()
    {
        Console.WriteLine("怪物死亡");

        // 死亡要做的事不为空
        deadDoSomthing?.Invoke(this);

        // 执行完一次一般清空
        // 一般情况下 委托关联的函数 有加 就有减(或者直接清空)
        deadDoSomthing = null;
    }
}

// 玩家类
class Player
{
    // 玩家当前有多少钱
    private int playerMoney = 0;

    // 当怪物死了玩家做什么函数
    public void WhenMonsterDeadPlayerDoSomthing(Monster monster)
    {
        // 玩家的金币增加
        playerMoney += monster.money;
        Console.WriteLine("现在有{0}元钱", playerMoney);
    }
}

// 面板界面类
class Panel
{
    // 当前面板界面显示有多少钱
    private int nowShowMoney = 0;

    // 当怪物死了面板界面做什么函数
    public void WhenMonsterDeadPanelDoSomething(Monster monster)
    {
        // 面板界面的显示金币增加
        nowShowMoney += monster.money;
        Console.WriteLine("当前面板显示{0}元钱", nowShowMoney);
    }
}

// 成就类
class Achievement
{
    // 击杀怪物数
    private int nowKillMonsterNum = 0;

    // 当怪物死了成就做什么函数
    public void WhenMonsterDeadAchievementDoSomething(Monster monster)
    {
        // 击杀怪物数+1
        nowKillMonsterNum += 1;
        Console.WriteLine("当前击杀了{0}怪物", nowKillMonsterNum);
    }

主函数内

// 创建怪物 玩家 面板界面 成就系统
Monster monster1 = new Monster();
Player player = new Player();
Panel panel = new Panel();
Achievement achievement = new Achievement();

// 把怪物死了要执行的函数存进来
monster1.deadDoSomthing += player.WhenMonsterDeadPlayerDoSomthing;
monster1.deadDoSomthing += panel.WhenMonsterDeadPanelDoSomething;
monster1.deadDoSomthing += achievement.WhenMonsterDeadAchievementDoSomething;

monster1.Dead();
// 怪物死亡
// 现在有10元钱
// 当前面板显示10元钱
// 当前击杀了1怪物

monster1.Dead();
// 怪物死亡

// 在创一个怪物试一试
Monster monster2 = new Monster();
monster2.deadDoSomthing += player.WhenMonsterDeadPlayerDoSomthing;
monster2.deadDoSomthing += panel.WhenMonsterDeadPanelDoSomething;
monster2.deadDoSomthing += achievement.WhenMonsterDeadAchievementDoSomething;
monster2.Dead();
// 怪物死亡
// 现在有20元钱
// 当前面板显示20元钱
// 当前击杀了2怪物

13.4 练习题代码

using System;

namespace Lesson12_练习题
{
    //class语句块外 namespace语句块内

    #region 练习题一
    //一家三口,妈妈做饭,爸爸妈妈和孩子都要吃饭
    //用委托模拟做饭——>开饭——>吃饭的过程

    //人类基类
    abstract class Person
    {
        //共同行为 吃饭
        public abstract void Eat();
    }

    //妈妈类
    class Mother : Person
    {
        //开始吃饭委托变量 存开始吃饭时要执行的函数
        public Action beginEat;

        public override void Eat()
        {
            Console.WriteLine("妈妈吃饭");
        }

        //妈妈做饭方法
        public void Cook()
        {
            Console.WriteLine("妈妈做饭");

            Console.WriteLine("妈妈做饭做好了");

            //做好饭了 执行开始吃饭委托
            if (beginEat != null)
            {
                beginEat();
            }
        }
    }

    //爸爸类
    class Father : Person
    {
        public override void Eat()
        {
            Console.WriteLine("爸爸吃饭");
        }
    }

    //孩子类
    class Child : Person
    {
        public override void Eat()
        {
            Console.WriteLine("孩子吃饭");
        }
    }

    #endregion

    #region 练习题二
    //怪物死亡后,玩家要加10块钱,面板界面要更新数据
    //成就要累加怪物击杀数,请用委托来模拟实现这些功能
    //只用写核心逻辑表现这个过程,不用写的太复杂

    //怪物类
    class Monster
    {
        //当怪物死亡时 把自己作为参数传出去 
        public Action<Monster> deadDoSomthing;

        //怪物成员变量 特征 价值多少钱
        public int money = 10;

        //死亡函数
        public void Dead()
        {
            Console.WriteLine("怪物死亡");

            //死亡要做的事不为空
            if (deadDoSomthing != null)
            {
                //就执行死亡要做的事
                deadDoSomthing(this);
            }

            //执行完一次一般清空
            //一般情况下 委托关联的函数 有加 就有减(或者直接清空)
            deadDoSomthing = null;
        }
    }

    //玩家类
    class Player
    {
        //玩家当前有多少钱
        private int playerMoney = 0;

        //当怪物死了玩家做什么函数
        public void WhenMonsterDeadPlayerDoSomthing(Monster monster)
        {
            //玩家的金币增加
            playerMoney += monster.money;
            Console.WriteLine("现在有{0}元钱", playerMoney);
        }
    }

    //面板界面类
    class Panel
    {
        //当前面板界面显示有多少钱
        private int nowShowMoney = 0;

        //当怪物死了面板界面做什么函数
        public void WhenMonsterDeadPanelDoSomething(Monster monster)
        {
            //面板界面的显示金币增加
            nowShowMoney += monster.money;
            Console.WriteLine("当前面板显示{0}元钱", nowShowMoney);
        }
    }

    //成就类
    class Achievement
    {
        //击杀怪物数
        private int nowKillMonsterNum = 0;

        //当怪物死了成就做什么函数
        public void WhenMonsterDeadAchievementDoSomething(Monster monster)
        {
            //击杀怪物数+1
            nowKillMonsterNum += 1;
            Console.WriteLine("当前击杀了{0}怪物", nowKillMonsterNum);
        }
    }

    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("委托练习题");

            //主函数内

            #region 练习题一

            //创建爸爸妈妈孩子
            Mother mother = new Mother();
            Father father = new Father();
            Child child = new Child();

            //告诉妈妈 一会做好饭了 爸爸妈妈孩子都要吃饭
            //所以吃饭的函数都存在妈妈的开始吃饭委托中
            mother.beginEat += father.Eat;
            mother.beginEat += child.Eat;
            mother.beginEat += mother.Eat;

            //让妈妈做饭
            mother.Cook();

            //妈妈做饭
            //妈妈做饭做好了
            //爸爸吃饭
            //孩子吃饭
            //妈妈吃饭

            #endregion

            #region 练习题二

            //创建怪物 玩家 面板界面 成就系统
            Monster monster1 = new Monster();
            Player player = new Player();
            Panel panel = new Panel();
            Achievement achievement = new Achievement();

            //把怪物死了要执行的函数存进来
            monster1.deadDoSomthing += player.WhenMonsterDeadPlayerDoSomthing;
            monster1.deadDoSomthing += panel.WhenMonsterDeadPanelDoSomething;
            monster1.deadDoSomthing += achievement.WhenMonsterDeadAchievementDoSomething;

            monster1.Dead();
            //怪物死亡
            //现在有10元钱
            //当前面板显示10元钱
            //当前击杀了1怪物

            monster1.Dead();
            //怪物死亡

            //在创一个怪物试一试
            Monster monster2 = new Monster();
            monster2.deadDoSomthing += player.WhenMonsterDeadPlayerDoSomthing;
            monster2.deadDoSomthing += panel.WhenMonsterDeadPanelDoSomething;
            monster2.deadDoSomthing += achievement.WhenMonsterDeadAchievementDoSomething;
            monster2.Dead();
            //怪物死亡
            //现在有20元钱
            //当前面板显示20元钱
            //当前击杀了2怪物

            #endregion
        }
    }
}

13.5 补充知识点

有返回值的委托存储多个函数 调用时如何获取多个返回值?


解决方法:如果想要获取到每一个函数执行后的返回值,委托容器中存在方法GetInvocationList()可以返回一个委托数组进行遍历输出



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏