Unity/C#------委托与事件(一篇文章彻底搞懂...)

news2025/1/17 3:14:32

一:委托

        所有的代码语言创造者母语都是英语,我们从英语翻译到中文的过程中难免会存在一些不太能还原本意的词,比如我之前一直不理解构造函数和析构函数,只知道这俩货作用相反,直到我看到了它的英文意思,Construstor/Distructor,我才彻底理解了他们的作用。

        接下来我们来看委托,Delegate,来看两个例句,深入理解Delegate...

Can you delegate some tasks or projects?                                你能够分配一些任务或者项目吗?

So why not delegate more work to your employees?    所以你为啥不给你的员工多分分配点任务?

从上面的句子中我们可以看到,他就是分配,也就是委托的意思(但是感觉可能有些人对委托的理解不如分配来的直接,至少对我来说是这样)

微软官方的解释是委托可以获取一个或多个方法,但是类型和返回值必须和方法相同,可以理解成委托是方法的抽象,也就是说定义一个方法的模板,至于这个方法具体是怎么样的,就由方法自己去实现。这点和函数指针很像...后续写单播委托时候再添加这部分内容。

我们可以说,委托就是一个方法的类型

话不多说,看代码...

一、单播委托——一次只能装进去一个方法

Public delegate 返回值 MyDelegate(参数1,参数2)

就这么一步,我们就把委托定义出来了,接下来要做的就是把这玩意儿实例化出来,那我们怎么把我们的方法给委托呢?

第一点就是我们需要有和定义的委托类型一致的返回值和参数列表

其实委托就是起源于C语言的函数指针,不过在C#以委托的形式存在了

但是在Java中没有委托这么一说...

//使用Typedef将该函数指针声明为一种类型,它是指向两个参数为int,返回值为int的函数指针
typedef int (*Calculator)(int x , int y);

int Add(int a ,int b)
{
    return a+b;
}

int Multiply(int a ,int b)
{
    return a*b;
}

//函数指针的使用

Calculator Pointer1 = &Add;
Calculator Pointer2 = &Multiply;

//这样我们在调用函数的时候就不再写函数,而是采用函数指针的方法,间接的指向了该类型的函数
Pointer1(0,1);
Pointer2(1,2);

从上面的函数指针我们可以看出,我们在注册方法的时候,可以间接的声明一个和该方法类型和返回值都一致的指针类型,从而调用函数指针即可,那么我们的委托和它是类似的....

接下来看我们的委托的实例化和方法的插入,可以使用new,也可以直接引用方法:

//实例化委托
MyDelegate myDelegate = new MyDelegate(Function);

//简化写法
myDelegate = Telegate;



返回值类型 Function(参数1,参数2)
{
    方法体;
}

委托的调用,可以使用Invoke,也可以直接写委托名+()

可以通过Invoke进行调用委托
myDelegate.Invoke();

也可以直接myDelegate();

二、多播委托——一次装多个方法,但不安全

在上面的方法添加环节,我们只需要做小小的修改

myDelegate += ChangeColor;
myDelegate += Log;

但是其实我们没事儿也不会这么干,这样长期下来有可能会存在内存泄漏,如果顺序执行列表中方法有一个出错了,后面的就都不会执行了,所以我们还有其他更好的选择

三、Action委托和Func委托

大多数的情况下,我们不太需要自己去声明委托,而是使用现成的委托即可,Unity为我们内置了两种泛型委托

1)Action委托——返回值必须为空,参数可有可无

//声明无参数的Action委托
Action action;

//声明有参数的Action委托
Action<string,float> action1;

//Action的使用
action = new Action(同参数的方法1)
action1 = new Action<string ,float> (sayhello);

//sayhello方法
public void SayHello(string name,float num)
{
    Debug.log(sting.Fromat("{0} has {1} ChampionShips .",name,num));
}

2) Func委托——返回值必须有,但是参数可有可无

//声明Func委托,前面是参数,后面是返回值
Func<double,double,double> func1;

//使用
func1 = new Func<double,double,double>(Add);


//跟Func结构一样的方法
public double Add(doublea ,double b)
{
    return a+b;
}

二:事件

事件,使对象或类具备通知能力

事件,是委托字段的一个包装器,它对于委托字段的访问起了限制作用

对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能

日常开发中,自己声明事件的机会比较少,一般是用已有事件比较多...

java中没有委托,事件这么一说,只用Interface来实现

一、事件的五个重要因素

事件的拥有者

事件成员(Event)

事件的响应者(Event Subscriber)

事件处理器(Event handler,本质上是回调方法)

事件订阅(+=)

他们之间的关系可以如下几种:

1:事件的拥有者类和事件的响应者是不同的类

using System.Timers;
using UnityEngine;

public class EventTimothyLiu1 : MonoBehaviour
{
    private void Start()
    {
        //世间间隔,每过1s就触发Elesap事件
        Timer timer = new Timer();
        timer.Interval = 1000;

        Boy boy = new();
        Girl girl = new();

        timer.Elapsed += boy.OnAction;
        timer.Elapsed += girl.OnAction;

        timer.Start();
    }
}
public class Boy
{
    internal void OnAction(object sender, ElapsedEventArgs e)
    {
        Debug.Log("1");
    }
}
public class Girl
{
    internal void OnAction(object sender, ElapsedEventArgs e)
    {
        Debug.Log("2");
    }
}

在该示例中,事件的拥有者是Timer类,事件的响应者是自定义的Boy,Girl类

所以事件的响应者和拥有者是两个类

第二个例子:

using System;
using System.Windows.Forms;

namespace EventLiu
{
    class Program
    {
        static void Main(string[] args)
        {
            //事件的拥有者:Form
            Form form = new Form();
            //事件的响应者:Controller
            Controller controller = new Controller(form);
            form.ShowDialog();
        }
    }
    class Controller
    {
        private Form form;
        /// <summary>
        /// CTOR再加Tab即可编写出构造器
        /// 也叫构造函数,每个类都必须有
        /// 在构建类的引用时自动运行的方法!
        /// </summary>
        public Controller(Form form)
        {
            if (form!= null)
            {
                //this就是类的实例
                //this后是我们定义的字段,后面是参数form
                this.form = form;
                //Click是事件
                this.form.Click += this.FormClicked;
            }
        }
        /// <summary>
        /// 事件处理器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FormClicked(object sender, EventArgs e)
        {
            this.form.Text = DateTime.Now.ToString();
        }
    }
}

上述的代码段中,我们引入了Form名称空间

事件的拥有者是Form,事件的响应者是Controller

2:事件的拥有者同时也是事件的响应者

using System;
using System.Windows.Forms;

namespace EventLiu
{
    class Program1
    {
        static void Main(string[] args)
        {
            //事件的拥有者:myForm
            //事件的接受者:myForm
            MyForm myForm = new MyForm();
            //事件:Click
            myForm.Click += myForm.FormClicked;
        }
    }
    /// <summary>
    /// 继承Form
    /// </summary>
    class MyForm : Form
    {
        /// <summary>
        /// 事件的处理器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void FormClicked(object sender, EventArgs e)
        {
            this.Text = DateTime.Now.ToString();
        }
    }
}

事件的拥有者是Form

事件的响应者也是myForm的实例

3:事件的拥有者是事件的响应者的成员(频率最高)

事件的响应者用自己的方法订阅者自己的字段成员的事件

using System;
using System.Windows.Forms;

namespace EventLiu
{
    class Program1
    {
        static void Main(string[] args)
        {
            MyForm myForm = new MyForm();
            myForm.ShowDIalog();
        }
    }
    class MyForm :Form
    {
        private TextBox textBox;
        //从订阅看事件拥有者就是button
        private Button button;

        public MyForm()
        {
            this.textBox = new TextBox();
            this.button = new Button();
            this.Controls.Add(this.button);
            this.Controls.Add(this.textBox);

            //Click是事件
            //事件的响应者是this,也就是MyForm的实例对象
            this.button.Click += this.ButtonClicked;
        }
        //事件处理器
        private void ButtonClicked(object sender, EventArgs e)
        {
            this.textBox.Text = "Hello";
        }
    }
}

在该段代码中,我们自己创建MyForm类,继承自Form

事件的拥有者是该类中的成员Button,事件的响应者是该类的实例化

也就是儿子有事件,爸爸订阅了。                                                                                                                                           

                                                                                                                                                                                                                                                                                 

4:事件的响应者是事件的拥有者的成员

二、事件的完整声明格式

用以下代码来看:

using System;
using System.Threading;
using UnityEngine;

public class EventTimothyLiu2 : MonoBehaviour
{
    private void Start()
    {
        Customer1 customer1 = new();
        Waiter1 waiter1 = new();

        customer1.Order += waiter1.Action;
        customer1.Action();
        customer1.PayTheBill();
    }
}
/// <summary>
/// 派生自EventArgs,习惯
/// </summary>
public class OrderEventArgs1:EventArgs
{
    public string DishName { get; set; }
    public string size { get; set; }
}

//把委托放置在类外
/// <summary>
/// 用EvenetHandler原因:
/// 1:别人看到这个后缀就知道这个委托是专门用来声明事件的
/// 2:EventHandle表明委托来约束事件处理器
/// 3:委托的实例是用来存储事件处理器
/// </summary>
/// <param name="customer"></param>
/// <param name="e"></param>
public delegate void OrderEventHandler1(Customer1 customer, OrderEventArgs1 e);


/// <summary>
/// Customer1是事件的拥有者,它拥有事件Order
/// </summary>
public class Customer1
{
    private OrderEventHandler1 OrderEventHandler1;
    public event OrderEventHandler1 Order
    {
        add { this.OrderEventHandler1 += value; }
        remove { this.OrderEventHandler1 -= value; }
    }

    public double Bill { get; set; }

    public void PayTheBill()
    {
        Debug.Log("I will pay $" + this.Bill);
    }

    public void WalkIn()
    {
        Debug.Log("Walk into the restaurant.");
    }
    public void SitDown()
    {
        Debug.Log("Sit down.");
    }
    public void Think()
    {
        for (int i = 0; i < 5; i++)
        {
            Debug.Log("Let me think...");
            Thread.Sleep(1000);
        }
        if(this.OrderEventHandler1 != null)
        {
            OrderEventArgs1 e = new OrderEventArgs1
            {
                DishName = "Kongpao Chicken",
                size = "large"
            };
            this.OrderEventHandler1.Invoke(this,e);
        }
    }
    public void Action()
    {
        this.WalkIn();
        this.SitDown();
        this.Think();
    }
}

/// <summary>
/// Waiter1是事件的接受者,它拥有事件的响应器Action
/// </summary>
public class Waiter1
{
    public void Action(Customer1 customer1, OrderEventArgs1 e)
    {
        Debug.Log("I will serve you  the dish -{0} "+ e.DishName);
        double price = 10;
        switch (e.size)
        {
            case "small":
            price = price * 0.5;
                break;
            case "large":
                price = price * 1.5;
                break;
            default:
                break;
        }
        customer1.Bill += price;
    }
}

从以上代码可以看出

Customer进入餐厅,走进,坐下,思考,然后触发点餐事件

Customer是点餐事件的拥有者

Waiter是点餐事件的接受者,它还得有点餐的响应事件,就是算出价格,并采取行动

在自定义点餐事件的时候,我们采用了EventHandler委托来给事件做支撑

创建了EventArgs类来承载事件,谁发起的事件,谁就是委托的第一个参数

但是这样写比较复杂,我们来看事件的简略声明格式

三、事件的简单声明格式

using System;
using System.Threading;
using UnityEngine;

public class EventTimothyLiu3 : MonoBehaviour
{
    private void Start()
    {
        Customer2 customer2 = new();
        Waiter2 waiter2 = new Waiter2();

        customer2.Order2 += waiter2.Action;

        customer2.Action();
        customer2.PayTheBill();
    }
}
/// <summary>
/// 先声明委托,委托两个参数分别是事件的拥有者和事件需要使用的参数
/// </summary>
/// <param name="customer2"></param>
/// <param name="e"></param>
public delegate void OrderEventHandler2(Customer2 customer2, OrderEventArgs2 e);

/// <summary>
/// 容纳事件参数
/// </summary>
public class OrderEventArgs2:EventArgs
{
    public string DishName { get; set; }
    public string size { get; set; }
}

/// <summary>
/// 事件的拥有者
/// </summary>
public class Customer2
{
    //原本需要先实例化委托,然后将委托赋予事件中进行使用
    //private OrderEventHandler1 OrderEventHandler1;
    //public event OrderEventHandler1 Order
    //{
    //    add { this.OrderEventHandler1 += value; }
    //    remove { this.OrderEventHandler1 -= value; }
    //}
    //现在进行简化
    /// <summary>
    /// 只需要事件+委托即可
    /// </summary>
    public event OrderEventHandler2 Order2;

    public double Bill { get; set; }
    public void PayTheBill()
    {
        Debug.Log("I will pay $ " + this.Bill);
    }
    public void WalkIn()
    {
        Debug.Log("I'm Coming");
    }
    public void SitDown()
    {
        Debug.Log("I'm sit down");
    }
    public void Think()
    {
        for (int i = 0; i < 5; i++)
        {
            Debug.Log("Let me think..");
            Thread.Sleep(1000);
        }
        if(Order2 != null)
        {
            OrderEventArgs2 e = new OrderEventArgs2();
            e.DishName = "Kongpao Chicken";
            e.size = "large";
            this.Order2.Invoke(this, e);
        }
    }
    public void Action()
    {
        this.WalkIn();
        this.SitDown();
        this.Think();
    }
}
/// <summary>
/// 事件的接收者和Action事件
/// </summary>
public class Waiter2
{
    public void Action(Customer2 customer2, OrderEventArgs2 e)
    {
        Debug.Log("I will serve you  the dish: " + e.DishName);
        double price = 10;
        switch (e.size)
        {
            case "small":
                price = price * 0.5;
                break;
            case "large":
                price = price * 1.5;
                break;
            default:
                break;
        }
        customer2.Bill += price;
    }
}

还是上述的代码,我们进行了简化

我们将如下代码:

private OrderEventHandler1 OrderEventHandler1;
    public event OrderEventHandler1 Order
    {
        add { this.OrderEventHandler1 += value; }
        remove { this.OrderEventHandler1 -= value; }
    }

简化成了:

public event OrderEventHandler2 Order2

这样的简化是微软后台进行了,我们可以直接如此书写

其实并非不存在委托字段的声明,还是存在的,只是存在于后台中,微软后台声明了,我们不需要看到,所以可以进行简化

还可以进一步简化:

将声明委托的部分省略掉,即使用微软定义好的EventHandler的委托类型,代码如下:

using System;
using System.Threading;
using UnityEngine;

public class EventTimothyLiu4 : MonoBehaviour
{
    private void Start()
    {
        Customer3 customer3 = new Customer3();
        Waiter3 waiter3 = new Waiter3();

        customer3.Order3 += waiter3.Action;

        customer3.Action();
        customer3.PayTheBill();
    }
}
public class OrderEventArgs3:EventArgs
{
    public string DishName { get; set; }
    public string size { get; set; }
}
public class Customer3
{
    public event EventHandler Order3;
    public double Bill { get; set; }

    public void WalkIn()
    {
        Debug.Log("Im coming");
    }
    public void SitDown()
    {
        Debug.Log("Im Sitting");
    }
    public void Think()
    {
        for (int i = 0; i < 5; i++)
        {
            Debug.Log("Im thinking");
            Thread.Sleep(1000);
        }
        if(Order3 != null)
        {
            OrderEventArgs3 e = new OrderEventArgs3();
            e.DishName = "KongpaoChicken";
            e.size = "large";
            this.Order3.Invoke(this, e);
        }
    }
    internal void Action()
    {
        this.WalkIn();
        this.SitDown();
        this.Think();
    }

    internal void PayTheBill()
    {
        Debug.Log("I will Pay for $" + this.Bill);
    }
}
public class Waiter3
{
    public void Action(object sender,EventArgs e)
    {
        Customer3 customer3 = sender as Customer3;
        OrderEventArgs3 orderinfo = e as OrderEventArgs3;

        Debug.Log("i will serve  you the dish" + orderinfo.DishName);
        double price = 10;
        switch (orderinfo.size)
        {
            case "small":
                price = price * 0.5;
                break;
            case "large":
                price = price * 1.5;
                break;
            default:
                break;
        }
        customer3.Bill += price;
    }
}

注意在Waiter类中的实例化Cutsome3和OrderEventArgs3的实例化使用

采用实例化,参数 as 的方法,将参数转化为我们需要的

为什么有了委托字段/属性,还需要事件?

为了程序逻辑更加有道理,更加安全,谨防“借刀杀人”

三:UnityEvent/UnityAction

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/387315.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring重点整理篇--springMVC(嘿嘿,开心哟)

Spring MVC是的基于JavaWeb的MVC框架&#xff0c;是Spring框架中的一个组成部分(WEB模块) MVC设计模式&#xff1a; Controller&#xff08;控制器&#xff09; Model&#xff08;模型&#xff09; View&#xff08;视图&#xff09; 重点来了&#x1f604; SpringMVC的工作机制…

Nginx+Tomcat实现负载均衡、动静分离(脚本方法)

一、Nginx 负载均衡实现原理1、Nginx 实现负载均衡是通过反向代理实现反向代理&#xff08;Reverse Proxy&#xff09; 是指以 代理服务器&#xff08;例&#xff1a;Nginx&#xff09; 来接受 internet 上的连接请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0…

【PGMAN:无监督的生成式多对抗网络】

PGMAN: An Unsupervised Generative Multiadversarial Network for Pansharpening &#xff08;PGMAN&#xff1a;一种无监督的生成式多对抗网络&#xff09; 全色锐化的目的是融合低分辨率多光谱&#xff08;MS&#xff09;图像和由卫星获取的高分辨率全色&#xff08;PAN&a…

OnlyOffice验证(三)OnlyOffice编译结果自制Docker镜像

OnlyOffice验证&#xff08;三&#xff09;OnlyOffice编译结果自制Docker镜像 之前验证了OnlyOffice验证&#xff08;二&#xff09;在Centos7上部署OnlyOffice编译结果&#xff0c;由于现在普遍都是容器化部署&#xff0c;所以还是验证下Doker镜像打包是否可行&#xff0c;因为…

渗透five86-2

kali的ip为192.168.56.104&#xff0c;利用nmap或arp-scan -l查看发现了靶机ip192.168.56.120这里存在20、21、80端口&#xff0c;这边需要将靶机ip进行域名绑定&#xff0c;对/etc/hosts改写&#xff0c;将192.168.56.120 与five86-2绑定输入ip后&#xff0c;发现了有log in由…

Kubernetes的网络架构及其安全风险

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/129137821 一、常见的Kubernetes网络架构 如图所示&#xff1a; 说明&#xff1a; 1、集群由多个节点组成。 2、每个节点上运行若干个Pod。 3、每个节点上会创建一个CNI网桥&#xff08;默认设备名称…

Qt——自定义界面之QStyle

1. Qt控件结构简介 首先我们要来讲讲GUI控件结构&#xff0c;这里以QComboBox为例&#xff1a; 一个完整的控件由一种或多种GUI元素构成&#xff1a; Complex Control Element。Primitive Element。Control Element。 1.1 Complex Control Element Complex control elements …

【C语言跬步】——指针数组和数组指针(指针进阶)

一.指针数组和数组指针的区别 1.指针数组是数组&#xff0c;是一种存放指针的数组&#xff1b; 例如&#xff1a; int* arr[10]; 2.数组指针是指针&#xff0c;是一种指向数组的指针&#xff0c;存放的是数组的地址&#xff1b; 例如&#xff1a; int arr[5]; int (p)[5]&a…

Redis学习(二):Redis安装测试

概述 Redis是什么 Redis, Remote Dictionary Server, 即远程字典服务。免费开源的数据库。 由C语言编写&#xff0c;支持网络&#xff0c;可基于内存亦可持久化的日志型、KV数据库&#xff0c;并提供所种语言的API。 Redis能干嘛 用于内存存储&#xff0c;持久化。rdb、ao…

[学习笔记]黑马程序员Spark全套视频教程,4天spark3.2快速入门到精通,基于Python语言的spark教程

文章目录视频资料&#xff1a;一、Spark基础入门&#xff08;环境搭建、入门概念&#xff09;第二章&#xff1a;Spark环境搭建-Local2.1 课程服务器环境2.2 Local模式基本原理2.3 安装包下载2.4 Spark Local模式部署第三章&#xff1a;Spark环境搭建-StandAlone3.1 StandAlone…

驾考笔记_2023

科目一1> 扣分制度1.1> 超速1.2> 超载1.3> 车牌1.4> 速记口诀2> 满分学习2.1> 消分学习2.2> 满分重考&#xff1b;3> 罚款制度3.1> 考证3.2> 审验教育3.3> 其他4> 车速规定4.1> 高速_最低限速4.2> 普路_最高限速5> 证件相关5.…

双指针法|位运算|离散化|区间合并

目录 双指针算法 位运算 离散化 序列合并 双指针算法 题目描述&#xff1a;1.输入n个单词&#xff0c;每个单词在输入的时候按空格隔开&#xff0c;之后打印出每个单词且换行 #include<iostream> #include <string>using namespace std; int main() {strin…

计算机网络中的原码、反码、补码

写在前面 原码、反码、补码是计算机组成原理中的概念&#xff0c;是计算机网络的基础知识之一。这些概念是为了处理二进制数的符号位而引入的&#xff0c;常用于计算机中的整数运算&#xff0c;也常用于数据存储和传输等领域。因此&#xff0c;了解和掌握这些概念对于理解计算机…

软测入门(五)接口测试Postman

Postman 一款Http接口收工测试工具。如果做自动化测试会使用jemter做。 安装 去官网下载即可。 https://www.postman.com/downloads/?utm_sourcepostman-home 功能介绍 页面上的单词基本上都能了解&#xff0c;不多介绍。 转代码&注释 可将接口的访问转为其他语言的…

一款小巧、开源免费、可观性强的流量监控软件——TrafficMonitor

名人说&#xff1a;君子生非异也&#xff0c;善假于物也。——荀子 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; o(‐&#xff3e;▽&#xff3e;‐)o很高兴你打开了这篇博客&#xff0c;跟着步骤一步步尝试安装吧。✧ 目录背景缘由一、…

自动驾驶目标检测项目实战(一)—基于深度学习框架yolov的交通标志检测

自动驾驶目标检测项目实战——基于深度学习框架yolov的交通标志检测 目前目标检测算法有很多&#xff0c;流行的就有faster-rnn和yolov&#xff0c;本文使用了几年前的yolov3框架进行训练&#xff0c;效果还是很好&#xff0c;当然也可以使用更高版本的Yolov进行实战。本代码使…

分布式事务之TCC与SAGA

分布式事务之TCC与SAGA 在《关于分布式事务的理解》&#xff0c;介绍了可靠消息队列的实现原理&#xff0c;虽然它也能保证最终的结果是相对可靠的&#xff0c;过程也足够简单&#xff08;相对于 TCC 来说&#xff09;&#xff0c;但现在你已经知道&#xff0c;可靠消息队列的…

从零起步认识XAML

新建WPF项目 在Visual Studio 2022中&#xff0c;选择"创建新项目" 选择“WPF Application” 点击下一步 点击创建。 执行DebugStart Debugging菜单命令&#xff0c;或者快捷键为F5&#xff0c;或者工具栏上的图标。 在Solution Explorer窗口&#xff08;ViewSoluti…

smardaten应用商超 | 邀您沉浸式体验企业级无代码开发

宝子们&#xff0c;还在发愁登录smardaten后缺少模板和数据吗&#xff1f;从0到1构建完整应用费时费力&#xff1f;别急&#xff0c;一大波模板资源已经装上车&#xff0c;这就给你安排上&#xff01;【smardaten应用商超】基于数睿数据长期服务软件企业、大型客户的经验&#…

Redis实现服务注册与服务发现源码阅读(Go语言)

Redis实现服务注册与服务发现源码阅读 背景 近期在看开源项目CloudWeGo中看到目前GoLang微服务框架Hertz中支持通过Redis实现服务注册与服务发现功能。便想着阅读下源码 源码阅读 gut clone了hertz-contrib后看到在一级目录下有目前各种主流的服务注册与发现的实现方案。为…