如何编写一个单一函数来处理不同类型的数据?

huangapple go评论82阅读模式
英文:

How I can write a single function to work with different types

问题

给定以下示例,是否有可能创建一个函数,可以完全复制(不仅仅是获取 reflect.Type)实际类型以进行进一步操作?我知道 Go 是静态类型的,虽然很酷的是我可以将任何结构体传递给定义了接口参数的函数,但是有没有可能反过来做更多的事情?

我已经查看了 reflect 包,但只找到了返回 reflect.Type 或 reflect.Value 的内容。我使用了 New() 方法,它返回一个新的 reflect.Value - 但在那里我无法设置任何字段。也许有经验的人可以告诉我,这是否肯定是可能的,或者是否有其他方法可以实现。

package main

import "fmt"

type User struct {
    Name string
}

func main() {
    user := User{Name:"FooBar"}
    DoSomethingGenericWithStruct(user)
}

func DoSomethingGenericWithStruct(i interface{}) {
    // 访问 i 的字段...
    // 或者创建 i 类型的切片 ([]User)...
    // 或者实例化 i 类型的新对象 (new User)...
    // ...
}
英文:

Given the following example, is it anyhow possible to create a function that can literally reproduce (not only get the reflect.Type) the actual type for further manipulation? I know go is statically typed and although it's very cool that I can pass any struct to a function that defines an interface parameter, is there any chance I can do more the other way around?

I already looked into the reflect package but only found stuff that returned a reflect.Type or reflect.Value. I used the New() method which returned a new reflect.Value - and there I couldn't set any fields. Maybe someone experienced with the reflect package can tell me if this is definitely possible or not - or if there's another way to do it.

package main

import "fmt"

type User struct {
    Name string
}

func main() {
    user := User{Name:"FooBar"}
    DoSomethingGenericWithStruct(user)
}

func DoSomethingGenericWithStruct(i interface{}) {
    // access fields of i ...
    // or create slice of type of i ([]User) ...
    // or instantiate new object of type of i (new User) ...
    // ...
}

答案1

得分: 1

你需要传递一个指向你的结构体的指针才能修改它。

同时要注意,使用反射会带来很高的运行时性能开销。

func DoSomethingGenericWithStruct(i interface{}) {
    val := reflect.ValueOf(i)
    if val.Kind() != reflect.Ptr {
        panic("需要一个指针")
    }
    val = val.Elem() // 现在你可以修改它了
    // 添加错误检查等,如果不是结构体或没有 "Name" 字段,这将会引发 panic
    val.FieldByName("Name").SetString("stuff")
}

要创建一个新的元素并赋值:

val = val.Elem()
nval := reflect.New(val.Type()).Elem() // 创建一个相同类型的新结构体
nval.FieldByName("Name").SetString("stuff")
val.Set(nval)

要修改实际的结构体,而不是 reflect.Value,你需要将 interface{} 转换为你的类型,例如:

nval := reflect.New(val.Type()).Elem() // 创建一个相同类型的新结构体
user := nval.Interface().(User)
user.Name = "Stuff"
val.Set(reflect.ValueOf(user))
英文:

You would have to pass a pointer to your struct to be able to modify it.

Also keep in mind that using reflection has a high runtime performance cost.

func DoSomethingGenericWithStruct(i interface{}) {
	val := reflect.ValueOf(i)
	if val.Kind() != reflect.Ptr {
		panic("need a pointer")
	}
	val = val.Elem() // now you can modify it
    // add error checking and such, this will panic if it's not a struct or there's no "Name" field
	val.FieldByName("Name").SetString("stuff")
}

<kbd>playground</kbd>

To create a new element and assign it:

val = val.Elem()
nval := reflect.New(val.Type()).Elem() // create a new struct of the same type
nval.FieldByName(&quot;Name&quot;).SetString(&quot;stuff&quot;) 
val.Set(nval)

to modify the actual struct, not reflect.Value, you will have to get the interface{} to it then assert it to your type, for example:

nval := reflect.New(val.Type()).Elem() // create a new struct of the same type
user := nval.Interface().(User)
user.Name = &quot;Stuff&quot;
val.Set(reflect.ValueOf(user))

huangapple
  • 本文由 发表于 2014年10月13日 06:52:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/26330827.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定