V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
8520ccc
V2EX  ›  Go 编程语言

分享:泛型+context 自动根据类型生成 ctx key

  •  
  •   8520ccc · 91 天前 · 1309 次点击
    这是一个创建于 91 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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
    }
    
    
    第 1 条附言  ·  89 天前
    #1 和 #15 都说明了这么写会有问题 所以不建议这么写
    16 条回复
    povsister
        1
    povsister  
       90 天前 via iPhone
    这种代码写的误人子弟。。
    官方 value context 的使用说明你是一点没读啊。
    8520ccc
        2
    8520ccc  
    OP
       90 天前 via iPhone
    @povsister 额请教一下,什么问题 确实没读……
    8520ccc
        3
    8520ccc  
    OP
       90 天前 via iPhone
    @povsister 老实说有点不爽,确实是我学艺不精,这个是我的问题,但是您的评论是不是有点尖锐了……
    tairan2006
        4
    tairan2006  
       90 天前 via Android
    官方推荐 type alias 就挺简单的
    lrh3321
        5
    lrh3321  
       90 天前 via Android
    我要取一个具体的 interface 出来呢?比如我只要 type interface Logger{Info(any)},我不关心具体类型,你这样要存进去还是要做额外处理
    dingyaguang117
        6
    dingyaguang117  
       90 天前
    没太看明白,类型做 key ?每种类型只能存一个数据?
    povsister
        7
    povsister  
       90 天前
    @8520ccc
    哥们我既没人身攻击也没任何脏字。。你这还是有点脆弱了啊。
    搁 Linus Torvalds 这种攻击性拉满的,就该直接骂你代码是 trash 了(斜眼笑

    不过给个建议,写偏基础性质的代码,务必搞懂每一行代码的作用和大致原理,尤其是参数上的一些设计,你写的时候就不好奇为什么 value context 的 key 和 value 都是 interface 类型么。
    不然就会有你这连 value context 设计和原理都没读的闹的笑话了。
    8520ccc
        8
    8520ccc  
    OP
       90 天前 via iPhone
    @dingyaguang117 对 因为我用这个方法主要存的是 *Struct 基本不存在需要一个结构体需要存多份的情况
    8520ccc
        9
    8520ccc  
    OP
       90 天前 via iPhone
    @povsister ……好吧 因为我的需求只是存*Struct 基本每个 ctx 只需要存一份 所以我这个是挺满足我自己的需求的
    povsister
        10
    povsister  
       90 天前
    @8520ccc
    我碰到很多也不读 value context 设计的研发,用的时候直接 key="keyString",然后 value 直接拐上去了。
    放你这个设计里是直接 boom 。
    8520ccc
        11
    8520ccc  
    OP
       90 天前
    @povsister 我这个专门放 *struct 还有 interface 的 我每个类型只放一次 如果那种的肯定不用这个方案了

    我这个只是为了那种只存放一个类型的 避免手写 key
    8520ccc
        12
    8520ccc  
    OP
       90 天前
    @lrh3321 依然可用 直接存 只是要注意 存进去时的类型 一定要是 Logger

    ```
    package gctx

    import (
    "context"
    "log"
    "testing"
    )

    // MockLogger 是一个实现 Logger 接口的模拟结构体
    type MockLogger struct {
    lastMessage any
    }

    func (m *MockLogger) Info(msg any) {
    log.Println("MockLogger:", msg)
    m.lastMessage = msg
    }

    type Logger interface {
    Info(msg any)
    }

    func TestLoggerContextStorage(t *testing.T) {
    // 创建一个基础 context
    ctx := context.Background()

    // 创建一个 MockLogger 实例
    logger := &MockLogger{}

    // 将 logger 存储到 context 中
    ctx = Put(ctx, Logger(logger))

    // 从 context 中检索 logger
    retrievedLogger, err := Get[Logger](ctx)
    if err != nil {
    t.Fatalf("Failed to retrieve logger from context: %v", err)
    }
    retrievedLogger.Info("Test log message")
    // 检查检索到的 logger 是否是原始 logger
    if retrievedLogger != logger {
    t.Errorf("Retrieved logger is not the same as the original logger")
    }

    // 使用检索到的 logger
    testMessage := "Test log message"
    retrievedLogger.Info(testMessage)

    // 验证消息是否正确记录
    if logger.lastMessage != testMessage {
    t.Errorf("Expected last message to be %q, but got %q", testMessage, logger.lastMessage)
    }

    // 测试检索不存在的类型
    _, err = Get[string](ctx)
    if err == nil {
    t.Error("Expected error when retrieving non-existent type, but got nil")
    }
    }

    ```
    povsister
        13
    povsister  
       90 天前 via iPhone
    @8520ccc
    血的教训告诉你约束要做在编译器上,而不是口头上
    8520ccc
        14
    8520ccc  
    OP
       90 天前 via iPhone
    @povsister emmm 好的
    FarmerChillax
        15
    FarmerChillax  
       90 天前
    一楼说的没错,你这确实误人子弟。建议阅读官方 context 文档: https://pkg.go.dev/context#WithValue

    一些大型项目/框架中的血泪史:
    - https://github.com/gin-gonic/gin/issues/3888
    - https://github.com/gin-gonic/gin/issues/4040


    最后建议在帖子后面追加说明吧,不然这真的误人子弟
    edcopclub
        16
    edcopclub  
       90 天前 via Android
    go 是怎么判断两个变量是==的?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2363 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 16:00 · PVG 00:00 · LAX 08:00 · JFK 11:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.