首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从Go 1.18中的单个方法返回两种不同的具体类型?

如何从Go 1.18中的单个方法返回两种不同的具体类型?
EN

Stack Overflow用户
提问于 2022-07-04 08:05:48
回答 1查看 115关注 0票数 3

假设我有这样的代码:

代码语言:javascript
复制
type Type1 struct {
    Name string `json:"name,omitempty"`
    Path string `json:"path"`
    File string `json:"file"`
    Tag  int    `json:"tag"`
    Num  int    `json:"num"`
}

func LoadConfiguration(data []byte) (*Type1, error) {
    config, err := loadConf1(data)
    if err != nil {
        return nil, err
    }
    confOther, err := loadConfOther1()
    if err != nil {
        return nil, err
    }
    // do something with confOther
    fmt.Println("confOther", confOther)
    if confOther.Tag == 0 {
        config.Num = 5
    }

    // do something with config attributes of type1
    if config.Tag == 0 {
        config.Tag = 5
    }
    if config.Num == 0 {
        config.Num = 4
    }

    return config, nil
}

func loadConf1(bytes []byte) (*Type1, error) {
    config := &Type1{}
    if err := json.Unmarshal(bytes, config); err != nil {
        return nil, fmt.Errorf("cannot load config: %v", err)
    }

    return config, nil
}

func loadConfOther1() (*Type1, error) {
    // return value of this specific type
    flatconfig := &Type1{}

    // read a file as []byte
    // written as a fixed array to simplify this example
    fileContent := []byte{10, 22, 33, 44, 55}

    if err := json.Unmarshal(fileContent, flatconfig); err != nil {
        return nil, fmt.Errorf("cannot read config %v", err)
    }

    return flatconfig, nil
}

唯一的公共职能是LoadConfiguration

它基于真正的代码,用于将json数据作为特定的结构读取。如果某些东西看起来毫无用处,那是因为我简化了原始代码。

上面的代码是可以的,但是现在我想创建另一个名为" Type2“的结构类型,并重复使用相同的方法将数据读取到Type2中,而无需复制和粘贴所有内容。

代码语言:javascript
复制
type Type2 struct {
    Name  string                  `json:"name,omitempty"`
    Path  string                  `json:"path"`
    Map   *map[string]interface{} `json:"map"`
    Other string                  `json:"other"`
}

基本上,我希望能够调用LoadConfiguration来获取Type2。我可以接受调用LoadConfiguration2这样的特定方法,但是我不想复制和粘贴loadConf1loadConfOther1。在Go 1.18中,有什么方法可以用惯用的方式来做吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-04 08:23:31

实际上,问题中显示的代码除了将类型传递到json.Unmarshal中并格式化错误之外,什么也不做,因此您可以重写您的函数,使其表现得像它一样:

代码语言:javascript
复制
func LoadConfiguration(data []byte) (*Type1, error) {
    config := &Type1{}
    if err := loadConf(data, config); err != nil {
        return nil, err
    }
    // ...
}

// "magically" accepts any type
// you could actually get rid of the intermediate function altogether
func loadConf(bytes []byte, config any) error {
    if err := json.Unmarshal(bytes, config); err != nil {
        return fmt.Errorf("cannot load config: %v", err)
    }
    return nil
}

如果代码实际上所做的不仅仅是将指针传递到json.Unmarshal,它还可以从类型参数中获益。

代码语言:javascript
复制
type Configurations interface {
    Type1 | Type2
}

func loadConf[T Configurations](bytes []byte) (*T, error) {
    config := new(T)
    if err := json.Unmarshal(bytes, config); err != nil {
        return nil, fmt.Errorf("cannot load config: %v", err)
    }
    return config, nil
}

func loadConfOther[T Configurations]() (*T, error) {
    flatconfig := new(T)
    // ... code
    return flatconfig, nil
}

在这些情况下,您可以使用new(T)创建任意类型的新指针,然后json.Unmarshal将负责将字节片或文件的内容反序列化到其中--条件是JSON实际上可以解组到任何一个结构中。

顶级函数中特定于类型的代码应该仍然不同,特别是因为您希望使用显式的具体类型实例化泛型函数。所以我建议保留LoadConfiguration1LoadConfiguration2

代码语言:javascript
复制
func LoadConfiguration1(data []byte) (*Type1, error) {
    config, err := loadConf[Type1](data)
    if err != nil {
        return nil, err
    }
    confOther, err := loadConfOther[Type1]()
    if err != nil {
        return nil, err
    }

    // ... type specific code

    return config, nil
}

但是,如果特定于类型的代码只是其中的一小部分,那么您很可能可以使用特定部分的类型切换,尽管在您的情况下,它似乎不是一个可行的选择。我看起来就像:

代码语言:javascript
复制
func LoadConfiguration[T Configuration](data []byte) (*T, error) {
    config, err := loadConf[T](data)
    if err != nil {
        return nil, err
    }
    // let's pretend there's only one value of type parameter type
    // type-specific code
    switch t := config.(type) {
        case *Type1:
            // ... some *Type1 specific code
        case *Type2:
            // ... some *Type2 specific code
        default:
            // can't really happen because T is restricted to Configuration but helps catch errors if you extend the union and forget to add a corresponding case
            panic("invalid type")
    }

    return config, nil
}

最小示例操场:https://go.dev/play/p/-rhIgoxINTZ

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72853519

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档