Golang中验证包含接口指针映射的架构

Golang中验证包含接口指针映射的架构 以下是我想要实现的功能。 一个单一的账本,其类型为:

map[string]interface{}

问题是,每次有人想从账本中读取数据时,都必须将值复制到一个局部变量中,然后对其进行操作。在我的项目中,这种“复制”可能会导致性能问题。

因此,我希望这个映射能持有插入其中的对象的引用。这样,任何使用该映射的人都不需要复制到局部变量,而是可以直接指向原始对象/修改原始对象(允许修改)并完成任务。因此,我将映射结构改为:

map[string]*interface{}

现在的问题是:这种思路正确吗?Go语言是否提供了其他替代工具或机制来实现我想要的功能?

继续,如果我尝试这样做,它会失败:

s:= "some random string"
singleLedger := make(map[string]*interface{})
singleLedger["first_entry"] = &s

哪里出错了? 我想要实现的功能是可能的吗?

图片

Go Playground - The Go Programming Language


更多关于Golang中验证包含接口指针映射的架构的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

我猜@Christophe_Meessen,这是在@NobbZ提到之后确保功能的第二种方法。对使用反射持保留态度。

更多关于Golang中验证包含接口指针映射的架构的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


func (l *Ledger) AddElement(key string, value interface{}){
    if reflect.ValueOf(value).Kind() != reflect.Ptr {
        panic("AddElement accept only a pointer value")
    }
    // ...
}

谢谢 @NobbZ

NobbZ: 将获取和设置值的操作封装到函数/方法中,并确保通过这些函数只存储指针。

定义会是什么样子?

func (l *Ledger) AddElement(key string, value interface{}){
//如何将接口包装在接口指针内?
}

只需创建一个 map[string]interface{} 类型的映射,并且只存储指针。

将对值的获取和设置操作封装到函数/方法中,并通过这些封装来保证只存储指针。

或者,你也可以将 s 声明为 interface{} 类型,不过我不确定之后是否能重建其原始类型。 https://play.golang.org/p/DIagkFCm_lB

func main() {
    fmt.Println("hello world")
}

创建一个展示实现的小例子。然而,它创建的是一个副本对象,而不是指针。 https://play.golang.org/p/AXWNo7X069f

package main

import (
"fmt"
)

type Ledger struct {
kvm map[string]*interface{}
}

func NewLedger() *Ledger {

return &Ledger{kvm: make(map[string]*interface{})}
} 

func (l *Ledger) AddToLedger(key string, val *interface{}) {
l.kvm[key] =val
}

func (l *Ledger) ReadFromLedger(key string) *interface{} {
return l.kvm[key]
}



func main() {

s:=NewLedger()

str:=interface{}("some random string")

s.AddToLedger("first",&str)

ledgerPtrInterfaceType:=s.ReadFromLedger("first")
ledgerPtrStringType:=(*ledgerPtrInterfaceType).(string)// is this creating a new 'copy' object? 

fmt.Println(ledgerPtrStringType)
str ="updated"
fmt.Println("updated_string:",ledgerPtrStringType)//the string is not updated, what am I missing?

}

在 Go 中,map[string]*interface{} 确实可以存储任意类型的指针,但你的代码失败是因为类型不匹配。&s 的类型是 *string,而映射期望的是 *interface{}。你需要先将值转换为接口类型,再取其指针。

以下是修正后的示例:

package main

import "fmt"

func main() {
    s := "some random string"
    singleLedger := make(map[string]*interface{})
    
    // 正确方式:先转换为 interface{},再取指针
    var i interface{} = s
    singleLedger["first_entry"] = &i
    
    // 读取和修改示例
    if entry, ok := singleLedger["first_entry"]; ok {
        // 通过指针修改值
        *entry = "modified string"
        fmt.Println(*entry) // 输出: modified string
    }
    
    // 存储结构体指针示例
    type Data struct {
        Value int
    }
    d := &Data{Value: 42}
    singleLedger["struct_entry"] = &d
    
    // 修改结构体字段
    if entry, ok := singleLedger["struct_entry"]; ok {
        dataPtr := (*entry).(*Data)
        dataPtr.Value = 100
        fmt.Println(dataPtr.Value) // 输出: 100
    }
}

然而,使用 *interface{} 会带来类型断言的开销和复杂性。更常见的做法是直接存储具体类型的指针:

// 使用 map[string]interface{} 存储指针
ledger := make(map[string]interface{})
s := "string"
ledger["str"] = &s

// 修改值
if ptr, ok := ledger["str"].(*string); ok {
    *ptr = "new value"
    fmt.Println(*ptr) // 输出: new value
}

// 存储结构体指针
type Record struct {
    Count int
}
r := &Record{Count: 1}
ledger["record"] = r

// 修改结构体
if rec, ok := ledger["record"].(*Record); ok {
    rec.Count = 2
    fmt.Println(rec.Count) // 输出: 2
}

如果你需要类型安全,可以考虑使用泛型:

type Ledger[T any] struct {
    data map[string]*T
}

func NewLedger[T any]() *Ledger[T] {
    return &Ledger[T]{data: make(map[string]*T)}
}

func (l *Ledger[T]) Set(key string, value *T) {
    l.data[key] = value
}

func (l *Ledger[T]) Get(key string) (*T, bool) {
    val, ok := l.data[key]
    return val, ok
}

// 使用示例
func main() {
    ledger := NewLedger[string]()
    s := "hello"
    ledger.Set("test", &s)
    
    if val, ok := ledger.Get("test"); ok {
        *val = "world"
        fmt.Println(*val) // 输出: world
    }
}

对于性能关键场景,直接存储指针确实能避免复制开销,但需要注意并发访问的安全性。如果多个 goroutine 同时访问,需要使用 sync.RWMutexsync.Map 进行保护。

回到顶部