Golang中为什么要使用反射?

Golang中为什么要使用反射? 大家好,我对Go语言还比较陌生,在阅读kubernetes/kubernetes的代码时遇到了这段代码:

	keys := reflect.ValueOf(providers).MapKeys()
	stringKeys := make([]string, len(keys))
	for ix := range keys {
		stringKeys[ix] = keys[ix].String()
	}
	sort.Strings(stringKeys)

var providers = make(map[string]DockerConfigProvider) […]

为什么这里要使用反射呢?映射的键类型是已知的字符串类型,作者为什么要在这里使用反射?

有什么想法吗? 地道的Go语言写法应该是直接遍历映射。 查看提交历史,这样做是为了使凭证提供者配置加载具有确定性。 原因是为了对映射键进行排序,但我仍然不理解为什么这里要使用反射。


更多关于Golang中为什么要使用反射?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

或许你可以通过邮件发送答案。

更多关于Golang中为什么要使用反射?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


从这段代码片段来看,它没有使用锁来获取映射的快照列表,因为这可能导致数据竞争,它只是省去了一些麻烦的工作。你可以尝试查看 MapKeys 的源代码。

但为什么不构建一个包含所有键的数组,并在不使用反射的情况下对其进行排序呢? 访问数据结构时缺少互斥锁看起来也不对,我不确定 MapKeys 如何能缓解这个问题。

Git blame 为这个问题提供了一些线索这里。简而言之,他们希望这个算法比简单地遍历 map 键更具确定性,因为键的访问顺序是随机的。

MapKeys

供参考

阅读 Go 的 map 源代码显示,实际上有两种不同的 map 实现:实验性的 SwissMap 以及我找到的这篇很棒的博客文章。

SwissMap: A smaller, faster Golang Hash Table

SwissMap: 一个更小、更快的 Go 哈希表

SwissMap 的初始版本,是 Abseil 的 flat_hash_map 的 Go 语言移植。

在 Go 中,映射的键迭代顺序是随机的,这是语言设计上的有意行为,目的是让开发者不要依赖映射的顺序。然而,在某些场景下,我们需要确定性的输出,比如在 Kubernetes 中,为了确保配置加载的顺序一致,就需要对映射的键进行排序。

你提供的代码片段中,使用反射的原因是为了获取映射的所有键,然后进行排序。虽然映射的键类型是已知的字符串,但直接遍历映射无法保证顺序。反射在这里的作用是提取键的列表,以便进行排序。

以下是代码的详细解释:

// 使用反射获取映射的所有键
keys := reflect.ValueOf(providers).MapKeys()

// 创建一个字符串切片来存储键
stringKeys := make([]string, len(keys))

// 将反射值转换为字符串
for ix := range keys {
    stringKeys[ix] = keys[ix].String()
}

// 对键进行排序
sort.Strings(stringKeys)

实际上,不使用反射也可以实现相同的功能,代码如下:

// 直接遍历映射,收集键
stringKeys := make([]string, 0, len(providers))
for key := range providers {
    stringKeys = append(stringKeys, key)
}

// 对键进行排序
sort.Strings(stringKeys)

在 Kubernetes 的代码中,使用反射可能是历史原因,或者是为了处理更复杂的情况。但在你的场景中,直接遍历映射并排序是更地道的 Go 写法。

如果你需要确定性的映射键顺序,建议使用直接遍历并排序的方法,而不是反射。反射通常用于处理类型未知的情况,而这里键类型已知,因此反射不是必需的。

回到顶部