获取Go结构体中字段的reflect.Ptr类型

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

Obtaining reflect.Ptr type to field in a Go struct

问题

我正在尝试将一个指向结构体字段的可变指针列表传递给第三方包。该包接受一个可变的interface{}列表(func Persist(...interface{}) error),其中每个接口值都是一个指向变量的指针。我创建了一个模拟第三方库的函数,并打印出指针的类型和种类(在下面的mockFunction中)。

当我以非可变方式传递结构体变量的地址时,在模拟函数中使用反射调用时,它们具有其原始类型和值。然而,当我以可变方式使用扩展传递它们时,它们具有Type: reflect.ValueKind: struct。第三方包不知道如何处理这种形式的参数。

如果可能的话,我想找出一种使用interface{}切片调用第三方包的方法(例如inv := make([]interface{}, 3)),并在调用Persist(inv...)时使用可变参数扩展。

以下是带有Go Playground链接的代码:

package main

import (
  "fmt"
  "reflect"
)

type Investment struct {
  Price  float64
  Symbol string
  Rating int64
}

func main() {
  inv := Investment{Price: 534.432, Symbol: "GBG", Rating: 4}
  s := reflect.ValueOf(&inv).Elem()
  variableParms := make([]interface{}, s.NumField())
  for i := 0; i < s.NumField(); i++ {
    variableParms[i] = s.Field(i).Addr()
  }

  // 非可变参数调用
  mockFunction(&inv.Price, &inv.Symbol, &inv.Rating)
  // 可变参数调用
  mockFunction(variableParms...)
}

func mockFunction(values ...interface{}) {
  for i, value := range values {
    rv := reflect.ValueOf(value)
    fmt.Printf("value %d has Type: %s and Kind %s\n", i, rv.Type(), rv.Kind())
  }
}

当我使用非可变参数运行它时,对mockFunction的调用返回本机类型和种类,并且第三方包可以正确处理它们:

value 0 has Type: *float64 and Kind ptr
value 1 has Type: *string and Kind ptr
value 2 has Type: *int64 and Kind ptr

当我使用可变参数运行它时,值是不同的,第三方包不知道如何处理这些类型:

value 0 has Type: reflect.Value and Kind struct
value 1 has Type: reflect.Value and Kind struct
value 2 has Type: reflect.Value and Kind struct

是否有任何方法可以构造切片定义和调用,以便它可以进行可变参数扩展,并且看起来像以非可变方式传递指向结构体字段的指针?

英文:

I am trying to pass to a third-party package a variadic list of pointers to fields in a struct. The package accepts a variadic interface{} list ( func Persist(...interface) error ), where each of the interface values is a pointer to a variable. I created a function that mocks how the third-party library and prints out the Type and Kind of the pointers (called mockFunction below).

When I pass it the address of the struct variables in a non-variadic way, they have their primitive Types and Values within the mocked function using the reflect calls. However, when I pass them in a variadic way using expansion, they have Type: Type: reflect.Value and Kind: struct. The third-party package does not know how to handle them in this form.

I would like to figure out a way to call the third-party package with a slice of interface{} (e.g. inv := make([]interface{}, 3) and use variadic expansion on the call Persist(inv...) if at all possible.

Here is the code with a link to Go Playground below:

package main

import (
  &quot;fmt&quot;
  &quot;reflect&quot;
)

type Investment struct {
  Price  float64
  Symbol string
  Rating int64
}

func main() {
  inv := Investment{Price: 534.432, Symbol: &quot;GBG&quot;, Rating: 4}
  s := reflect.ValueOf(&amp;inv).Elem()
  variableParms := make([]interface{}, s.NumField())
  for i := 0; i &lt; s.NumField(); i++ {
    variableParms[i] = s.Field(i).Addr()
  }

  // non-variadic call
  mockFunction(&amp;inv.Price, &amp;inv.Symbol, &amp;inv.Rating)
  //variadic call
  mockFunction(variableParms...)
}

func mockFunction(values ...interface{}) {
  for i, value := range values {
    rv := reflect.ValueOf(value)
    fmt.Printf(&quot;value %d has Type: %s and Kind %s\n&quot;, i, rv.Type(), rv.Kind())
  }
}

Go Playground Link

When I run it with the non-variadic parameters, the call to mockFunction returns the native Types and Kinds and the third-party package processes them fine:

value 0 has Type: *float64 and Kind ptr
value 1 has Type: *string and Kind ptr
value 2 has Type: *int64 and Kind ptr

When I run it with the variadic parameters, the values are different and the third-party package does not know how to handle these types:

value 0 has Type: reflect.Value and Kind struct
value 1 has Type: reflect.Value and Kind struct
value 2 has Type: reflect.Value and Kind struct

Is there any way to structure the slice definition and the call to what is placed in to the slice so that it can be variadic expanded and look like passing the pointers to the struct fields in the non-variadic way?

答案1

得分: 4

Addr() 返回字段指针的反射 Value。调用值上的 Ptr() 方法可以将实际指针作为 interface{} 获取。

variableParms[i] = s.Field(i).Addr().Ptr()

<kbd>playground</kbd>

英文:

Addr() returns the reflect Value for the field pointer. Call Ptr() on the value to get the actual pointer as an interface{}.

variableParms[i] = s.Field(i).Addr().Ptr()

<kbd>playground</kbd>

答案2

得分: 1

我认为自2014年以来,Go对这种情况的处理可能已经发生了变化-当然,上面的代码在我使用Go 1.10时不再起作用...

然而,以下代码对我来说可以创建一个适当的[]interface{}以便按照描述的方式使用...

func settableSliceFromStruct(inStruct interface{}) ([]interface{}, error) {
    t := reflect.TypeOf(inStruct)
    if t.Kind() != reflect.Ptr {
        return nil, errors.New("can only assign values with pointer to struct")
    }
    v := reflect.ValueOf(inStruct).Elem()
    t = t.Elem()

    dataColumns := make([]interface{}, 0, t.NumField())
    for i := 0; i < t.NumField(); i++ {
        if weWantToIncludeThis(t.Field(i)) {
            dataColumns = append(dataColumns, v.Field(i).Addr().Interface())
        }
    }
    return dataColumns, nil
}

这里的关键部分是你的代码使用:

variableParms[i] = s.Field(i).Addr().Interface()
英文:

I think that perhaps Go's handling for this case has changed since 2014 - certainly the code above no longer works for me with Go 1.10...

However the following code works for me to create an appropriate []interface{} to use in the described way...

func settableSliceFromStruct(inStruct interface{}) ([]interface{}, error) {
	t := reflect.TypeOf(inStruct)
	if t.Kind() != reflect.Ptr {
		return nil, errors.New(&quot;can only assign values with pointer to struct&quot;)
	}
	v := reflect.ValueOf(inStruct).Elem()
	t = t.Elem()

	dataColumns := make([]interface{}, 0, t.NumField())
	for i := 0; i &lt; t.NumField(); i++ {
		if weWantToIncludeThis(t.Field(i)) {
			dataColumns = append(dataColumns, v.Field(i).Addr().Interface())
		}
	}
	return dataColumns, nil
}

The critical part here would be for your code to use:

variableParms[i] = s.Field(i).Addr().Interface()

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

发表评论

匿名网友

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

确定