假设我有这样的代码:
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中,而无需复制和粘贴所有内容。
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这样的特定方法,但是我不想复制和粘贴loadConf1和loadConfOther1。在Go 1.18中,有什么方法可以用惯用的方式来做吗?
发布于 2022-07-04 08:23:31
实际上,问题中显示的代码除了将类型传递到json.Unmarshal中并格式化错误之外,什么也不做,因此您可以重写您的函数,使其表现得像它一样:
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,它还可以从类型参数中获益。
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实际上可以解组到任何一个结构中。
顶级函数中特定于类型的代码应该仍然不同,特别是因为您希望使用显式的具体类型实例化泛型函数。所以我建议保留LoadConfiguration1和LoadConfiguration2。
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
}但是,如果特定于类型的代码只是其中的一小部分,那么您很可能可以使用特定部分的类型切换,尽管在您的情况下,它似乎不是一个可行的选择。我看起来就像:
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://stackoverflow.com/questions/72853519
复制相似问题