将 Golang 接口转换为结构体

89

你好,我正在尝试检索一个结构体的函数/方法,但是我正在使用一个接口作为参数,使用这个接口来访问结构体的函数。为了演示我想要的,下面是我的代码:

// Here I'm trying to use "GetValue" a function of RedisConnection but since "c" is an interface it doesn't know that I'm trying to access the RedisConnection function. How Do I fix this?
func GetRedisValue(c Connection, key string) (string, error) {
    value, err := c.GetValue(key)

    return value, err
}

// Connection ...
type Connection interface {
    GetClient() (*redis.Client, error)
}

// RedisConnection ...
type RedisConnection struct {}

// NewRedisConnection ...
func NewRedisConnection() Connection {
    return RedisConnection{}
}

// GetClient ...
func (r RedisConnection) GetClient() (*redis.Client, error) {
    redisHost := "localhost"
    redisPort := "6379"

    if os.Getenv("REDIS_HOST") != "" {
        redisHost = os.Getenv("REDIS_HOST")
    }

    if os.Getenv("REDIS_PORT") != "" {
        redisPort = os.Getenv("REDIS_PORT")
    }

    client := redis.NewClient(&redis.Options{
        Addr:     redisHost + ":" + redisPort,
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    return client, nil
}

// GetValue ...
func (r RedisConnection) GetValue(key string) (string, error) {
    client, e := r.GetClient()
    result, err := client.Ping().Result()
    return result, nil
}

GetValue返回一个接口。使用redis.String()将其转换为字符串。 - Itamar Haber
2个回答

235
直接回答这个问题,即将一个“interface”转换为具体的类型,你需要执行以下操作:

代码如下:

v = i.(T)

其中i是接口,T是具体类型。如果底层类型不是T,则会引发panic。要进行安全转换,可以使用以下语法:

v, ok = i.(T)

如果底层类型不是 T,则将 ok 设置为 false,否则设置为 true。请注意,T 也可以是接口类型,如果是,代码会将 i 强制转换为一个新的接口而不是具体类型。

请注意,强制转换接口通常是设计不良的标志。在您的代码中,您应该问自己,您自定义的接口 Connection 是否仅需要 GetClient 还是始终需要 GetValue?您的 GetRedisValue 函数是否需要一个 Connection 或者它总是要求一个具体的结构体?

相应地更改您的代码。


1
我很高兴发现在golang中这种类型的转换是可能的,我正准备开始使用它,但当我读到你的最后一段时...然后 :( 回到设计图并避免使用这种类型的转换。你在笔记中是完全正确的,谢谢! - Edenshaw

13

你的 Connection 接口:

type Connection interface {
    GetClient() (*redis.Client, error)
}

只是说存在一个GetClient方法,没有提到是否支持GetValue

如果你想在这样的Connection上调用GetValue

func GetRedisValue(c Connection, key string) (string, error) {
    value, err := c.GetValue(key)
    return value, err
}

那么你应该在接口中包含 GetValue

type Connection interface {
    GetClient() (*redis.Client, error)
    GetValue(string) (string, error) // <-------------------
}

现在你在说所有的Connection都将支持你想要使用的GetValue方法。


是的,我想那会是这种情况。但是 Connection 接口具有 GetValue 方法并没有什么意义。因此,我考虑创建一个单独的接口,并在其中添加一个 GetValue 函数。我该如何处理呢? - MadzQuestioning
1
这取决于你想要深入了解多少。你真的需要你的 Connection 接口吗?也许你只需要一些类似于 type Valuer interface { GetValue(string) (string, error) } 的接口,然后只需将你的 Redis 连接包装在实现该接口的结构体中即可。 - mu is too short

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接