Go 中独特函数的集合

2023-12-10

我正在尝试在 go 中实现一组功能。上下文是一个事件服务器;我想防止(或至少警告)为一个事件多次添加相同的处理程序。

我读过,地图通常用作集合,因为可以轻松检查成员资格:

if _, ok := set[item]; ok {
    // don't add item
} else {
    // do add item
}

不过,我在使用这种函数范式时遇到了一些麻烦。这是我的第一次尝试:

// this is not the actual signature
type EventResponse func(args interface{})

type EventResponseSet map[*EventResponse]struct{}

func (ers EventResponseSet) Add(r EventResponse) {
    if _, ok := ers[&r]; ok {
        // warn here
        return
    }
    ers[&r] = struct{}{}
}

func (ers EventResponseSet) Remove(r EventResponse) {
    // if key is not there, doesn't matter
    delete(ers, &r)
}

很明显为什么这行不通:函数不是 Go 中的引用类型,尽管有些人会告诉你它们是。我有证据,尽管我们不应该需要它,因为语言规范规定除了映射、切片和指针之外的所有内容都是按值传递的。

尝试2:

func (ers EventResponseSet) Add(r *EventResponse) {
// ...
}

这有几个问题:

  • 任何 EventResponse 都必须声明为fn := func(args interface{}){}因为您无法寻址以通常方式声明的函数。

  • 你根本无法通过闭包。

  • 使用包装器不是一种选择,因为传递给包装器的任何函数都将从包装器获取新地址 - 没有函数可以通过地址唯一标识,并且所有这些仔细的规划都是徒劳的。

我不接受将函数定义为变量作为解决方案是愚蠢的吗?还有另一个(好的)解决方案吗?

需要明确的是,我承认有些情况我无法捕获(关闭),这很好。我设想的用例是定义一堆处理程序,并且相对安全,如果有意义的话,我不会意外地将一个处理程序添加到同一事件两次。


你可以使用reflect.Value由 Uvelichitel 提出,或者函数地址作为string获得者fmt.Sprint()或地址为uintptr获得者reflect.Value.Pointer()(更多内容在答案中如何比较 Go 中的两个函数?),但我建议不要这样做。

由于语言规范不允许比较函数值,也不允许获取他们的地址,您无法保证程序中一次有效的东西将始终有效,包括特定的运行,并包括不同的(未来)Go 编译器。我不会使用它。

由于规范对此很严格,这意味着编译器可以生成例如在运行时更改函数地址的代码(例如卸载未使用的函数,然后在需要时再次加载)。我目前不知道这样的行为,但这并不意味着未来的 Go 编译器不会利用这样的东西。

如果您存储函数地址(以任何格式),则该值不再算作保留函数值。如果没有其他人再“拥有”该函数值,则生成的代码(以及 Go 运行时)将可以“自由”修改/重新定位该函数(从而更改其地址)——而不会违反规范和 Go 的类型安全。所以你不能理所当然地对编译器感到愤怒和责备,而只能对你自己感到愤怒和责怪。

如果您想检查是否重用,可以使用接口值。

假设您需要带有签名的函数:

func(p ParamType) RetType

创建一个接口:

type EventResponse interface {
    Do(p ParamType) RetType
}

例如,您可能有一个未导出的struct类型,并且指向它的指针可以实现您的EventResponse界面。创建导出函数以返回单个值,因此不会创建新值。

E.g.:

type myEvtResp struct{}

func (m *myEvtResp) Do(p ParamType) RetType {
    // Your logic comes here
}

var single = &myEvtResp{}

func Get() EventResponse { return single }

是否真的需要将实现隐藏在包中,并且只创建和“发布”单个实例?不幸的是,是的,因为否则你可以创造其他价值,比如&myEvtResp{}这可能是不同的指针但仍然具有相同的Do()方法,但接口包装值可能不相等:

接口值具有可比性。如果两个接口值相等,则它们具有完全相同的动态类型和相等的动态值或者两者都有值nil.

[...和...]

指针值具有可比性。如果两个指针值指向同一个变量或者两者的值为 nil,则它们相等。指向不同的指针零尺寸变量可能相等也可能不相等。

方式*myEvtResp实施EventResponse所以你可以注册它的值(唯一的值,可以通过Get())。你可以有一张类型的地图map[EventResponse]bool您可以在其中存储注册的处理程序、接口值作为键,以及true作为价值观。使用不在映射中的键对映射进行索引会产生映射值类型的零值。所以如果地图的值类型是bool,使用不存在的键对其进行索引将导致false– 告诉它不在地图上。已注册的索引EventResponse(现有的密钥)将导致存储的值 –true– 告诉它在地图上,它已经注册了。

您可以简单地检查是否已经注册:

type EventResponseSet map[*EventResponse]bool

func (ers EventResponseSet) Add(r EventResponse) {
    if ers[r] {
        // warn here
        return
    }
    ers[r] = true
}

Closing:为了避免重复使用,这似乎有点太麻烦了。我同意,而且我不会这么做。但如果你想...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go 中独特函数的集合 的相关文章

  • 结构体指针运算符猜想(理论)

    结构体指针的使用非常频繁 因此有一个特殊的运算符 gt 下面的表达式是等价的 x y x gt y 将此运算符简单地视为如下定义的预处理器宏是否公平 define x gt x 为什么或者为什么不 或者它从一开始就被编码为运算符 这有何不同
  • void* 指针的 C++ 替代品(不是模板)

    看来我对 C 有一个根本性的误解 我喜欢多态容器解决方案 谢谢你让我注意到这一点 所以 我们需要创建一个相对通用的容器类型对象 它还恰好封装了一些业务相关的逻辑 然而 我们需要在这个容器中存储基本上任意的数据 从原始数据类型到复杂类的所有数
  • 我应该使用指针还是移动语义来传递大块数据?

    我对推荐的编码技术有疑问 我有一个用于模型分析的工具 有时需要传递大量数据 从工厂类到保存多个异构块的数据 我的问题是 对于我是否应该使用指针或移动所有权是否存在一些共识 我需要尽可能避免复制 因为数据块的大小可能大到 1 GB 指针版本如
  • Go MSSQL 连接

    如何提供 MSSQL 连接 它说它始终与代码相关 即使信息不正确 也不会报错 package main import database sql fmt github com denisenkom go mssqldb log var ser
  • Golang Appengine 项目无法构建

    我有一个使用 golang 的应用程序引擎项目 我已经大约一年没有碰过了 我现在无法让它在之前构建的机器上构建 我收到以下错误 go app builder 解析输入失败 解析器 src golang org x net internal
  • 使用 crypto/ssh 的 golang scp 文件

    我正在尝试通过 ssh 下载远程文件 以下方法在 shell 上运行良好 ssh hostname tar cz opt local folder gt folder tar gz 然而 golang 上的相同方法在输出工件大小方面存在一些
  • 取消用户特定的 goroutine [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个应用程序 网络应用程序 允许用户使用 twitter oauth 登录并提供自动推文删除功能 用户登录到 Web 应用程序后
  • 共享来自单独命令/进程的属性

    我提供带有多个命令和子命令的命令行工具 我使用cobra https github com spf13 cobra命令行 我有两个单独的命令首先是前提条件e 给其他人 例如第一个命令是通过创建临时文件夹并验证某些文件来首选环境 第二个命令应
  • 安全移动 C++ 对象

    我听到过一些警告 不要通过以下方式将对象运送到另一个内存位置memcpy 但不知道具体原因 除非它包含的成员做了依赖于内存位置的棘手事情 否则这应该是完全安全的 或者不是 编辑 预期的用例是像这样的数据结构vector 它存储对象 不是po
  • Go 中的 WebP 编码器/解码器

    是否有一个完整的 WebP 编码器和解码器与当前每周 或可分叉 兼容 它的速度与标准 png 相当吗 这个人在 GitHub 上有一个包 其中包含 WebP 的编码器和解码器 https github com chai2010 webp h
  • 是否支持动态变量?

    我想知道Go中是否可以动态创建变量 我在下面提供了一个伪代码来说明我的意思 我将新创建的变量存储在切片中 func method slice make type for i 0 i lt 10 i var variable i i slic
  • 无法通过键获取 Gorilla 会话值

    我无法通过这种方式从会话中获取价值 它是nil session initSession r valWithOutType session Values key 完整代码 package main import fmt github com
  • Box、ref、&、*的理解及关系

    我对 Rust 中的指针如何工作有点困惑 有ref Box 而且我不确定他们如何一起工作 目前我的理解是这样的 Box并不是真正的指针 它是一种在堆上分配数据并在函数参数中传递未调整大小的类型 尤其是特征 的方法 ref在模式匹配中用于借用
  • nsq 无法通过连接到 nsqlookupd 来消费消息

    我尝试使用 docker compose 来运行 nsq docker compose yml如下 version 3 services nsqlookupd image nsqio nsq command nsqlookupd ports
  • 在 Go 中使用电子邮件地址创建证书签名请求 (CSR)

    我尝试使用 crypto x509 包生成 CSR 但没有找到将 emailAddress 字段添加到其主题中的方法 根据文档证书申请 http golang org pkg crypto x509 CertificateRequest结构
  • 如何从非英语字符串解析go中的月份

    我想将以下字符串解析为 go 中的日期 This item will be released on March 9 2014 我跟着this https stackoverflow com questions 14106541 go par
  • char* argv[] 在 c/c++ 中如何工作? [复制]

    这个问题在这里已经有答案了 我知道它用于使用命令行中的参数 但我没有得到声明 字符 argv 它是否意味着指向 char 数组的指针 如果是的话为什么没有大小 如果不是动态数组 就不需要有大小吗 我做了一些研究 发现有人说它会衰减为 cha
  • 使用覆盖率信息测试 Go 中的 os.Exit 场景 (coveralls.io/Goveralls)

    这个问题 如何在 Go 中测试 os exit 场景 https stackoverflow com questions 26225513 how to test os exit scenarios in go 以及其中得票最高的答案 列出
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • (转)如何使用toml文件?

    正如标题 我想知道如何使用 golang 中的 toml 文件 在此之前 我展示了我的 toml 示例 这样对吗 datatitle enable true userids 12345 67890 datatitle 12345 prop1

随机推荐

  • 在 Delphi 中以无边框形式/窗口平滑调整大小

    我正在尝试调整无边框表单的大小 但是当我使用右侧 底部增加大小时 边框和旧客户区域之间存在间隙 该间隙取决于移动鼠标的速度 当您从左边框甚至从左下角调整大小时 效果会更加明显 到处都很可怕 我尝试使用其他商业应用程序 它也会发生 当我更改为
  • 在 WPF 中将组合框绑定到 XML

    我知道这个问题已经被问死了 但是我已经尝试了很多我找到的建议答案 并且当我在 VS2013 中启动 WPF 时 组合框仍然没有填充 就这样吧 我有一个名为 People xml 的 XML 文档 其格式如下
  • 如何在 HTML 中定义内联内容库以与 Magnific-Popup 一起使用?

    我的页面上有许多画廊 可以通过各自的按钮启动 我喜欢在按钮旁边定义页面中画廊的标记 然后使用隐藏的想法 mfp hide 但是 当我添加时 我无法激活弹出窗口delegate关键字 否则 这是我到目前为止的代码 HTML div class
  • 如何更改 Rails 迁移 t.timestamps 以在 postgres 中使用 `timestamp(0) without timezone`

    我正在尝试找出如何更改本机数据类型t timestamps用于 Rails 迁移 postgres 中的默认类型是timestamp without timezone 我想要的是timestamp 0 without timezone 我想
  • Spring:确保首先初始化特定的 bean

    我有一个库进行 log4j 的运行时设置和配置 没有 log4j properties 或 log4j xml 我已经定义了一个名为 MyLoggerFactory 的 bean 我希望它成为第一个使用 spring 初始化的 bean 我
  • jQuery DOM 操作 - 性能比较?

    我正在学习使用 jQuery 进行 DOM 操作 并希望了解浏览器性能最佳实践 假设我有两个 DOM 元素 div p ol 等 我希望用户只能看到第一个元素 然后只能看到第二个元素 I could 使用替换 remove 第一个元素和 a
  • javascript中删除小数点后的数字

    var randomNumber Math random 3 3 5 randomNumber alert randomNumber 这段代码返回一个数字 例如 4 589729345235789 我需要它返回 4 5 因此需要它删除小数点
  • 多语言站点,子目录作为语言(RewriteRule)

    对于我的网站 我想将我的网址重写为 www xxx com en index php 而不是 www xxx com index php language enwww xxx com nl index php 而不是 www xxx com
  • 今天全日历开始日期

    我正在努力从今天开始制定完整的日历 当我这样做时 它会显示整周 calendar fullCalendar Options calendar fullCalendar gotoDate currentDate 其中 currentDate
  • 内存数据库的概念以及如何查看我的数据是否已填充到 HSQL DB 中?

    我在内存数据库中使用 HSQL 来测试我的应用程序 并使用 SQL Server 作为主数据库 现在在进行测试时 HSQL 数据库将填充与我在 SQL Server 中相同的数据 现在我正在尝试测试特定的数据从数据库检索数据的服务 如果直接
  • sql 从函数或存储过程返回表

    这更多的是一个语法问题 我正在尝试编写一个可以嵌入到查询中的存储过程或函数 例如 select from MyBigProcOrFunction 我正在尝试定义一个表格函数 但我不明白如何执行此操作 因为我构建临时表来计算数据 然后才最终在
  • 选择最近 7 天的最大值

    我通过每小时插入描述这些项目的记录作为 cron 作业来跟踪某些项目 我有一张桌子 steamid int eventid auto increment itemid int value int time unix format 我使用每小
  • AWS部署经常出现故障

    因此 我在 EC2 实例上进行了非常基本的部署 除了几个大问题外 该部署基本上可以正常工作 现在我只是通过 ssh 进入盒子并运行 python m SimpleHTTPServer 80 我在安全组上有一个框 允许端口 80 上的 htt
  • 在 SQL Server Management Studio 中执行 NHibernate 生成的准备好的语句

    配置 NHibernate 以显示执行的 SQL 可以完成预期的任务 但是每当需要将 SQL 字符串复制粘贴到 SQL Server Management Studio 中时 我们就必须对其进行大幅重新排列才能兼容 在我开始开发自己的应用程
  • 与下一行、分组、data.table 进行比较

    我有一个数据框 其中包含每个用户每周的页面浏览量 我想确定每个用户在特定事件发生后他们的观点是否增加 减少或保持不变 我的数据如下所示 Userid week xeventinweek numviews Alice 1 2 5 Alice
  • 如何用StepVerifier验证提供的Mono没有完成?

    With StepVerifier很容易检查是否提供Mono已完成 仅通过expectComplete 中的方法StepVerifier 但是如果需要检查相反的情况该怎么办 我尝试使用这种方法 Test public void neverM
  • 使用 Glide 和 FireBase android 存储和显示图像

    我正在尝试使用 Glide 和 Firebase 上传并显示个人资料图片 上传部分工作成功 但是 如果我尝试从数据库加载该图像 它会显示空白 我的动机是在用户进入活动一次时加载 profile image 他可以点击现有图像并根据自己的意愿
  • 如何在 TeamCity 中构建 Delphi 项目

    我正在尝试在 TeamCity 中构建一个 delphi 项目 但无法让它工作 我正在使用 MSBuild 来构建项目 并且还向构建添加了 BDS 参数 但我不断收到错误 MSB4040 项目中没有目标 您需要注意几件事 首先 需要设置De
  • 运行导出 apk 时出现“java.lang.ClassNotFoundException”错误

    我在 Eclipse 中编写了一个 Android 应用程序 并在手机上使用运行命令 在我的手机上运行成功 但我使用 Android Tools gt Export Signed Application Package 导出我的应用程序 然
  • Go 中独特函数的集合

    我正在尝试在 go 中实现一组功能 上下文是一个事件服务器 我想防止 或至少警告 为一个事件多次添加相同的处理程序 我读过 地图通常用作集合 因为可以轻松检查成员资格 if ok set item ok don t add item els