Golang Go语言中分享:泛型+context 自动根据类型生成 ctx key
package gctx
import (
“context”
“fmt”
“reflect”
)
type body[T any] struct {
Value T
}
type typeKey[T any] struct {
}
func Put[T any](ctx context.Context, value T) context.Context {
key := typeKey[body[T]]{}
return context.WithValue(ctx, key, body[T]{Value: value})
}
func Get[T any](ctx context.Context) (T, error) {
key := typeKey[body[T]]{}
value, ok := ctx.Value(key).(body[T])
if !ok {
var zero T
return zero, errors.New(“not found”)
}
return value.Value, nil
}
Golang Go语言中分享:泛型+context 自动根据类型生成 ctx key
更多关于Golang Go语言中分享:泛型+context 自动根据类型生成 ctx key的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这种代码写的误人子弟。。
官方 value context 的使用说明你是一点没读啊。
更多关于Golang Go语言中分享:泛型+context 自动根据类型生成 ctx key的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
额请教一下,什么问题 确实没读……
官方推荐 type alias 就挺简单的
我要取一个具体的 interface 出来呢?比如我只要 type interface Logger{Info(any)},我不关心具体类型,你这样要存进去还是要做额外处理
没太看明白,类型做 key ?每种类型只能存一个数据?
哥们我既没人身攻击也没任何脏字。。你这还是有点脆弱了啊。
搁 Linus Torvalds 这种攻击性拉满的,就该直接骂你代码是 trash 了(斜眼笑
不过给个建议,写偏基础性质的代码,务必搞懂每一行代码的作用和大致原理,尤其是参数上的一些设计,你写的时候就不好奇为什么 value context 的 key 和 value 都是 interface 类型么。
不然就会有你这连 value context 设计和原理都没读的闹的笑话了。
……好吧 因为我的需求只是存*Struct 基本每个 ctx 只需要存一份 所以我这个是挺满足我自己的需求的
我碰到很多也不读 value context 设计的研发,用的时候直接 key=“keyString”,然后 value 直接拐上去了。
放你这个设计里是直接 boom 。
我这个专门放 *struct 还有 interface 的 我每个类型只放一次 如果那种的肯定不用这个方案了
我这个只是为了那种只存放一个类型的 避免手写 key
依然可用 直接存 只是要注意 存进去时的类型 一定要是 Logger <br>package gctx<br><br>import (<br> "context"<br> "log"<br> "testing"<br>)<br><br>// MockLogger 是一个实现 Logger 接口的模拟结构体<br>type MockLogger struct {<br> lastMessage any<br>}<br><br>func (m *MockLogger) Info(msg any) {<br> log.Println("MockLogger:", msg)<br> m.lastMessage = msg<br>}<br><br>type Logger interface {<br> Info(msg any)<br>}<br><br>func TestLoggerContextStorage(t *testing.T) {<br> // 创建一个基础 context<br> ctx := context.Background()<br><br> // 创建一个 MockLogger 实例<br> logger := &MockLogger{}<br><br> // 将 logger 存储到 context 中<br> ctx = Put(ctx, Logger(logger))<br><br> // 从 context 中检索 logger<br> retrievedLogger, err := Get[Logger](ctx)<br> if err != nil {<br> t.Fatalf("Failed to retrieve logger from context: %v", err)<br> }<br> <a target="_blank" href="http://retrievedLogger.Info" rel="nofollow noopener">retrievedLogger.Info</a>("Test log message")<br> // 检查检索到的 logger 是否是原始 logger<br> if retrievedLogger != logger {<br> t.Errorf("Retrieved logger is not the same as the original logger")<br> }<br><br> // 使用检索到的 logger<br> testMessage := "Test log message"<br> <a target="_blank" href="http://retrievedLogger.Info" rel="nofollow noopener">retrievedLogger.Info</a>(testMessage)<br><br> // 验证消息是否正确记录<br> if logger.lastMessage != testMessage {<br> t.Errorf("Expected last message to be %q, but got %q", testMessage, logger.lastMessage)<br> }<br><br> // 测试检索不存在的类型<br> _, err = Get[string](ctx)<br> if err == nil {<br> t.Error("Expected error when retrieving non-existent type, but got nil")<br> }<br>}<br><br>
血的教训告诉你约束要做在编译器上,而不是口头上
emmm 好的
一楼说的没错,你这确实误人子弟。建议阅读官方 context 文档: https://pkg.go.dev/context#WithValue
一些大型项目/框架中的血泪史:
- https://github.com/gin-gonic/gin/issues/3888
- https://github.com/gin-gonic/gin/issues/4040
最后建议在帖子后面追加说明吧,不然这真的误人子弟
go 是怎么判断两个变量是==的?
在Go语言中,泛型与context
的结合使用可以极大提升代码的复用性和类型安全性。当你需要在context
中传递不同类型的数据时,为每个类型手动生成一个唯一的key可能会显得繁琐且容易出错。利用泛型,我们可以自动化这一过程。
首先,我们需要理解context.WithValue
函数需要一个interface{}
类型的key,这通常是通过一个未导出的变量来实现的,以避免key冲突。然而,手动为每个类型创建这样的变量并不优雅。
利用泛型,我们可以创建一个辅助函数,自动生成一个基于类型的唯一key。这里是一个简单的实现思路:
package main
import (
"context"
"fmt"
"reflect"
)
// NewContextKey 生成基于类型的唯一key
func NewContextKey[T any]() interface{} {
return struct{}{typeKey: reflect.TypeOf(T{}).String()}{}
}
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, NewContextKey[string](), "example")
// 从context中获取值
val := ctx.Value(NewContextKey[string]()).(string)
fmt.Println(val) // 输出: example
}
注意,由于interface{}
不能直接作为map的key,我们使用了一个包含匿名字段的结构体来绕过这一限制。这里的typeKey
字段实际上并不存储值,只是利用了其类型信息来确保唯一性。
这种方法使得我们可以根据类型自动生成唯一的context
key,提高了代码的可读性和维护性。