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 中独特函数的集合 的相关文章

随机推荐

  • 在 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