文章的内容仅仅是自己关于map的value类型定义为函数类型时的一点点思考,如有不对的地方,请不吝赐教。

学习过后才知道叫做 方法值

1、起因

最近在看老项目代码时,看到了一段类似于下面的定义,最开始看到的时候,对于 LotMap 的用法比较疑惑,为什么 map value 定义的函数类型是 func(r *Receiver, lot *Lot, msg *History),但是在初始化时,传递的值却是(*Receiver).handleStart

然后根据自己的有了下面的思考,如有不对,请多指教。

package main

var LotMap = map[string]func(r *Receiver, lot *Lot, msg *History){
"start": (*Receiver).handleStart,
"end": (*Receiver).handleEnd,
} type Lot struct {
} type History struct {
} type Receiver struct{} func (r *Receiver) handleStart(lot *Lot, msg *History) { } func (r *Receiver) handleEnd(lot *Lot, msg *History) { } func main() {
r := &Receiver{}
lot := &Lot{}
msg := &History{}
LotMap["start"](r, lot, msg)
}

2、原因

如果你有我一样的疑惑,接下来我们一起看看是为啥?

《go 语言圣经》中的第6.4. 方法值和方法表达式 有讲解关于方法值的介绍。

大家可以先去看看原文关于方法值的介绍后,再来看本文。

说实话,我看了上面的文章中的介绍后,对于方法值还是一头雾水,不知道在讲什么。(怪我太菜)

接下来我按照自己的理解,说说对于方法值的理解。

2.1、什么叫方法值

在Go语言中,方法值(method value)指的是将方法绑定到特定接收者实例上,从而创建一个函数值(function value)。方法值允许你将方法视为普通函数,可以将其传递给其他函数或者存储在变量中,之后在不同的上下文中调用。

例如,如果有一个方法 func (r *Receiver) handleStart(lot *Lot, msg *History),你可以使用 (*Receiver).handleStart 创建一个方法值,然后将其传递给其他函数或存储在变量中,如下所示:

// 方法值的创建
methodValue := (*Receiver).handleStart // 将方法值传递给其他函数
someFunction(methodValue) // 存储方法值到变量中
var myFunction func(r *Receiver, lot *Lot, msg *History)
myFunction = methodValue

在这里,methodValue 就是一个方法值,它与特定的接收者实例无关,可以在任何需要的地方使用,类似于普通的函数值。

2.2、 样例解释

看完了上面的的 何为方法值后,相比应该对方法值有一定的了解,对于样例的用法,也熟悉了不少。

接下里,我们在 main 函数中加入下面的语句

func main() {
r := &Receiver{}
lot := &Lot{}
msg := &History{}
LotMap["start"](r, lot, msg) fmt.Printf("%T\n", r.handleStart)
fmt.Printf("%T\n", (*Receiver).handleStart)
}

运行结果:

func(*main.Lot, *main.History)
func(*main.Receiver, *main.Lot, *main.History)

如果将 LotMap 中的(*Receiver)变成(Receiver),ide 也会提示,提示如下:

所以方法值的本质是:编译器会将 (*Receiver).handleStart 变成 func(*Receiver, *Lot, *History)

3、总结

第一点:

上面的 LotMap 这种写法,其实有一个专用的词,叫做查找表。使用查找表可以避免了冗长的 if-else 或 switch-case 语句,使代码更加清晰、易于维护。

第二点:

在Go语言中,(*Receiver).handleStart这种形式是方法值(method value)的写法,可以将方法绑定到特定的接收者实例上,从而创建一个函数值(function value)。在这种写法中,方法接收者(receiver)会被作为第一个参数传递给方法。

因此,(*Receiver).handleStart可以转换为func(r *Receiver, lot *Lot, msg *History)的函数签名,其中r *Receiver就对应方法接收者,而lot *Lotmsg *History则是方法的其他参数。

这种转换允许将方法作为普通函数一样传递给其他函数或者存储在映射(map)中,使得代码更加灵活和易于组织。

golang中关于map的value类型定义为函数类型时(方法值)的一点点思考的更多相关文章

  1. 1.4.2 solr字段类型--(1.4.2.1)字段类型定义和字段类型属性

    1.4.2 solr字段类型 (1.4.2.1) 字段类型定义和字段类型属性. (1.4.2.2) solr附带的字段类型 (1.4.2.3) 使用货币和汇率 (1.4.2.4) 使用Dates(日期 ...

  2. golang中的map

    1. 声明与初始化 // map的声明与初始化 userInfo := map[string]string{"name": "马亚南", "age&q ...

  3. python3中内建函数map()与reduce()的使用方法

    map()的使用    map()的使用方法形如map(f(x),Itera).对,它有两个参数,第一个参数为某个函数,第二个为可迭代对象.如果不懂什么是函数,不懂什么是可迭代对象没关系,记住下面的例 ...

  4. Django中利用filter与simple_tag为前端自定义函数的实现方法

    转自:http://www.jb51.net/article/116303.htm 前言 Django的模板引擎提供了一般性的功能函数,通过前端可以实现多数的代码逻辑功能,这里称之为一般性,是因为它仅 ...

  5. JS基础语法---函数---介绍、定义、函数参数、返回值

    函数: 把一坨重复的代码封装,在需要的时候直接调用即可 函数的作用: 代码的重用 函数需要先定义,然后才能使用 函数名字:要遵循驼峰命名法 函数一旦重名,后面的会把前面的函数覆盖 Ctrl +鼠标左键 ...

  6. Golang中的Map(集合)

    Map 是一种无序的键值对的集合.Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值. Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它.不过,Map 是无 ...

  7. golang中,map作为函数参数是如何传递的

    当你声明一个map的时候: m := make(map[int]int) 编译器会调用 runtime.makemap: // makemap implements a Go map creation ...

  8. Golang的时间生成,格式化,以及获取函数执行时间的方法

    最近都在通过完成一些列功能强化自己对常用api的熟悉. 然而关于时间的api几乎是最常用的api类型,所以总结一些常用的. 以YY-mm-dd HH:MM:SS.9位 输出当前时间: func mai ...

  9. Vue组件库新增的prop属性类型是Object或者Array时默认值的设置

    在Vue开发中提供组件库时常常需要添加相关属性,用来接收父组件向子组件传递的数据,通常也会给属性设置默认值,那么当属性的类型是Object或者Array类型时如何设置默认值比较合理呢?下面将揭晓这一过 ...

  10. Go_14:GoLang中 json、map、struct 之间的相互转化

    1. golang 中 json 转 struct <1. 使用 json.Unmarshal 时,结构体的每一项必须是导出项(import field).也就是说结构体的 key 对应的首字母 ...

随机推荐

  1. Docker镜像的基本操作总结

    摘要 容器化是上个十年比较火的技术. 现在看起来在进行总计有点晚了. 不过linux是三十年前的,我依旧没有总结好 道理是一样的. 技术不在于新旧, 重要的是学习到原理. Docker的重要概念 Re ...

  2. Spring缓存是如何实现的?如何扩展使其支持过期删除功能?

    前言:在我们的应用中,有一些数据是通过rpc获取的远端数据,该数据不会经常变化,允许客户端在本地缓存一定时间. 该场景逻辑简单,缓存数据较小,不需要持久化,所以不希望引入其他第三方缓存工具加重应用负担 ...

  3. 【DP】DMOPC '21 Contest 8 P5 - Tree Building

    Problem Link 给定 \(n,m\) 和一个长为 \(m\) 的代价序列,对于一棵 \(n\) 个节点,每个节点度数不超过 \(m\) 的树,定义它的代价为 \(\sum\limits_{i ...

  4. TypeScript工具类 Partial 和 Required 的详细讲解

    场景描述: 场景描述:一个接口(IPerson)有很多个的字段,可能有几百.而且这些字段都是必须的. 我们需要使用这个接口,但是我又不可能使用它的全部.可能只会使用几个. 我还必须要使用这接口.这个时 ...

  5. NetCore高级系列文章01---创建项目及配置文件

    .NET Core是适用于 Windows.Linux 和 macOS 的免费.开源托管的计算机软件框架,作为.NET开发人员,全面拥抱.NetCore将成为趋势. 本系列文章将分为两大部分讲解.Ne ...

  6. [1] HEVD 学习笔记:HEVD 环境搭建

    1. HEVD 概述 + 环境搭建 ​ HEVD作为一个优秀的内核漏洞靶场受到大家的喜欢,这里选择x86的驱动来学习内核漏洞,作为学习笔记记录下来 实验环境 环境 备注 调试主机操作系统 Window ...

  7. JVM底层原理

    目录 1.类加载器与ClassFileFormate 2.JVM内存模型 3.对象在JVM中的创建与内存分配 4.对象引用与垃圾回收算法 5.JVM垃圾回收 6.G1垃圾回收器 7.ZGC垃圾回收器

  8. linux下面权限的含义以及修改

    linux中的权限 前言 数字权限 三位数字权限 读(r) 写(w) 执行(x) 无权限(-) 三位数字权限的转换 如何设置权限 最高位的含义 四位数字权限 SUID SGID SBIT 四位数字权限 ...

  9. Docker中Nginx部署go应用

    docker配合Nginx部署go应用 Nginx 名词解释 正向代理 反向代理 构建镜像 Nginx镜像 配置nginx.conf server_name Nginx中的负载均衡 轮询 upstre ...

  10. C++ 基于Boost.Asio实现端口映射器

    Boost.Asio 是一个功能强大的 C++ 库,用于异步编程和网络编程,它提供了跨平台的异步 I/O 操作.在这篇文章中,我们将深入分析一个使用 Boost.Asio 实现的简单端口映射服务器,该 ...