如何打开 reflect.Type?

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

How to switch on reflect.Type?

问题

我已经成功完成了这个任务,但是它看起来并不高效:

var t reflect.Type
switch t {
case reflect.TypeOf(([]uint8)(nil)):
    // 处理 []uint8 数组类型
}
英文:

I have managed to do this, but it does not look efficient:

var t reflect.Type
switch t {
case reflect.TypeOf(([]uint8)(nil)):
	// handle []uint8 array type
}

答案1

得分: 10

首先的问题,你确定要使用 reflect.Type 而不是使用类型切换(type switch)吗?例如:

switch x := y.(type) {
case []uint8:
  // x 现在是一个 []uint8
}

假设这种方法对你的情况不适用,我建议将这些变量定义为包级变量。例如:

var uint8SliceType = reflect.TypeOf(([]uint8)(nil))

func Foo() {
    var t reflect.Type
    switch t {
    case uint8SliceType:
        // 处理 []uint8 数组类型
    }

}
英文:

First question, are you sure you want to switch on reflect.Type and not use a type switch? Example:

switch x := y.(type) {
case []uint8:
  // x is now a []uint8
}

Assuming that will not work for your situation, my recommendation is to make those package variables. Example:

var uint8SliceType = reflect.TypeOf(([]uint8)(nil))

func Foo() {
    var t reflect.Type
    switch t {
    case uint8SliceType:
        // handle []uint8 array type
    }

}

答案2

得分: 8

如果你只是想检测类型,可能不需要使用 reflect。

switch t := myVar.(type) {
  case []uint8:
    // t 是 []uint8 类型
  case *Foo:
    // t 是 *Foo 类型
  default:
    panic("未知类型")
}

你实际上想要实现什么?

英文:

you may not need reflect if you are just trying to detect type.

switch t := myVar.(type){
  case []uint8:
    // t is []uint8
  case *Foo:
    // t is *Foo
  default:
    panic("unknown type")
}

What are you actually trying to accomplish?

答案3

得分: 6

初始问题“如何打开reflect.Type?”的答案是:你不能。然而,你可以使用reflect.Value来实现。

  • 给定一个变量v interface{},你可以调用reflect.TypeOf(v)reflect.ValueOf(v),分别返回reflect.Typereflect.Value
    • 如果v的类型不是interface{},那么这些函数调用将把它转换为interface{}类型。
  • reflect.Type包含有关类型的各种运行时信息,但它不包含任何可用于检索v本身类型的信息,这在类型切换中是需要的。
  • 然而,reflect.Value通过其Interface()方法提供了这个信息,它将底层值作为interface{}返回。你可以在类型切换或类型断言中使用它。
import "fmt"
import "reflect"

var v int
var rt reflect.Type = reflect.TypeOf(v)
fmt.Println(rt.String(), " has awesome properties: Its alignment is",
	rt.Align(), ", it has", rt.Size(), "bytes, is it even comparable?",
	rt.Comparable())
// … but reflect.Type won’t tell us what the real type is :(
// Let’s see if reflect.Value can help us.
var rv reflect.Value = reflect.ValueOf(v)
// Here we go:
vi := rv.Interface()
switch vi.(type) {
// Mission accomplished.
}

也许解释一些关于Go动态类型的细节会有所帮助。至少我曾经对此感到困惑很长一段时间。

reflectinterface{}

在Go语言中,有两个运行时泛型系统:

  • 语言层面interface{},用于类型切换和断言,
  • 库层面reflect包,用于检查运行时泛型类型和值。

这两个系统是相互独立的,其中一个系统可以做到的事情在另一个系统中是不可能的。例如,给定一个interface{},在纯粹的Go代码中(使用安全代码)无法做到的是,无论其元素类型如何,如果该值是数组或切片,都无法获取第i个元素的值。必须使用reflect才能实现这一点。相反,使用reflect无法进行类型切换或断言:需要将其转换为interface{},然后才能进行操作。

这两个系统之间只有非常少的接口。在一方面,是接受interface{}并返回reflect结构的TypeOf()ValueOf()函数。在另一方面,是Value.Interface()方法。

有点违反直觉的是,进行类型切换需要一个Value而不是一个Type。至少这在某种程度上与通过调用TypeOf()需要一个值来构造一个Type是一致的。

reflect.Kind

reflect.Typereflect.Value都有一个Kind()方法。有人建议使用这些方法返回的reflect.Kind类型的值来模拟类型切换。

虽然在某些情况下这可能有用,但它不能替代类型切换。例如,使用Kind无法区分int64time.Duration,因为后者被定义为:

type Duration int64

Kind可以告诉我们一个类型是否是任何类型的结构体、数组、切片等,而不考虑它所组成的类型。这是无法通过类型切换找出的。

(顺便说一句,我也有同样的问题,并没有在这里找到有用的答案,所以我自己去弄清楚了。反复问“你为什么要这样做?”然后得到无关的答案并没有帮助我。我有一个很好的理由,为什么我想要以这种方式做。)

英文:

The answer to the initial question How to switch on reflect.Type? is: You can’t. However, you can do it with reflect.Value.

  • Given a variable v interface{} you can call reflect.TypeOf(v) and reflect.ValueOf(v), which return a reflect.Type or reflect.Value, resp.
    • If the type of v is not interface{} then these function calls will convert it to interface{}.
  • reflect.Type contains various run-time information about the type, but it does not contain anything usable to retrieve the type of v itself as needed in a type switch.
  • Hovewer, reflect.Value provides it through its Interface() method, which returns the underlying value as interface{}. This you can use in a type switch or type assertion.
import "fmt"
import "reflect"

var v int
var rt reflect.Type = reflect.TypeOf(v)
fmt.Println(rt.String(), " has awesome properties: Its alignment is",
	rt.Align(), ", it has", rt.Size(), "bytes, is it even comparable?",
	rt.Comparable())
// … but reflect.Type won’t tell us what the real type is :(
// Let’s see if reflect.Value can help us.
var rv reflect.Value = reflect.ValueOf(v)
// Here we go:
vi := rv.Interface()
switch vi.(type) {
// Mission accomplished.
}

Perhaps it helps to clarify a few points which may cause confusion about dynamic typing in Go. At least I was confused by this for quite some time.

reflect vs. interface{}

In Go there are two systems of run-time generics:

  • In the language: interface{}, useful for type switches/assertions,
  • In the library: The reflect package, useful for inspection of run-time generic types and values of such.

These two systems are separated worlds, and things that are possible with one are impossible with the other. For example, Given an interface{}, it is in plain Go (with safe code) impossible to, say, if the value is an array or slice, regardless of its element type, then get the value of the i-th element. One needs to use reflect in order to do that. Conversely, with reflect it is impossible to make a type switch or assertion: convert it to interface{}, then you can do that.

There are only very few points of an interface between these systems. In one direction it is the TypeOf() and ValueOf() functions which accept interface{} and return a reflect struct. In the other direction it is Value.Interface().

It is a bit counter-intuitive that one needs a Value, not a Type, to do a type switch. At least this is somewhat consistent with the fact that one needs a value construct a Type by calling TypeOf().

reflect.Kind

Both reflect.Type and reflect.Value have a Kind() method. Some suggest using the value these methods return, of type reflect.Kind, to imitate a type switch.

While this may be useful in certain situations, it is not a replacement for a type switch. For example, using Kind one cannot distinguish between int64 and time.Duration because the latter is defined as

type Duration int64

Kind is useful to tell if a type is any kind of struct, array, slice etc., regardless of the types it is composed of. This is not possible to find out with a type switch.

(Side note. I had the same question and found no answer here helpful so I went to figure it out myself. The repeated counter-question “why are you doing this?”, followed by unrelated answers did not help me either. I have a good reason why I want to do it precisely this way.)

答案4

得分: 1

这可能有效。

switch t := reflect.TypeOf(a).String() {
   case "[]uint8":
   default:
}
英文:

This might work.

switch t := reflect.TypeOf(a).String() {
   case "[]uint8":
   default:
}

答案5

得分: 0

正如其他人所说,你通过打开 reflect.Type 来实现什么目标并不清楚。然而,我在尝试做类似事情时遇到了这个问题,所以我将给出我的解决方案,以防它能回答你的问题。

正如 captncraig 所说,可以在 interface{} 变量上进行简单的类型切换,而不需要使用 reflect。

func TypeSwitch(val interface{}) {
    switch val.(type) {
        case int:
            fmt.Println("int with value", val)
        case string:
            fmt.Println("string with value", val)
        case []uint8:
            fmt.Println("Slice of uint8 with value", val)
        default:
            fmt.Println("Unhandled", "with value", val)
    }
}

然而,超越这一点,在原始问题的上下文中,反射的有用性可能在于接受一个具有任意类型字段的结构体的函数,然后使用类型切换根据其类型处理字段。不需要直接在 reflect.Type 上进行切换,因为可以通过 reflect 提取类型,然后使用标准的类型切换即可。例如:

type test struct {
    I int
    S string
    Us []uint8
}

func (t *test) SetIndexedField(index int, value interface{}) {
    e := reflect.ValueOf(t).Elem()
    p := e.Field(index)
    v := p.Interface()
    typeOfF := e.Field(index).Type()
    switch v.(type) {
        case int:
            p.SetInt(int64(value.(int)))
        case string:
            p.SetString(value.(string))
        case []uint8:
            p.SetBytes(value.([]uint8))
        default:
            fmt.Println("Unsupported", typeOfF, v, value)
    }
}

以下示例演示了使用此函数的用法:

var t = test{10, "test string", []uint8{1, 2, 3, 4}}
fmt.Println(t)
(&t).SetIndexedField(0, 5)
(&t).SetIndexedField(1, "new string")
(&t).SetIndexedField(2, []uint8{8, 9})
fmt.Println(t)

关于 Go 中的反射的一些要点:

  1. 必须导出结构体字段,以便 reflect 能够使用它们,因此字段名首字母大写。
  2. 为了修改字段的值,需要像这个示例函数中一样使用结构体的指针。
  3. 使用 Elem() 来“解引用”反射中的指针。
英文:

As others have said, it's not clear what you are trying to achieve by switching on reflect.Type However, I came across this question when probably trying to do something similar, so I will give you my solution in case it answers your question.

As captncraig said, a simple type switch could be done on a interface{} variable without needing to use reflect.

func TypeSwitch(val interface{}) {
    switch val.(type) {
        case int:
            fmt.Println("int with value", val)
        case string:
            fmt.Println("string with value ", val)
        case []uint8:
            fmt.Println("Slice of uint8 with value", val)
        default:
            fmt.Println("Unhandled", "with value", val)
    }
}

However, going beyond this, the usefulness of reflection in the context of the original question could be in a function that accepts a struct with arbitrarily typed fields, and then uses a type switch to process the field according to its type. It is not necessary to switch directly on reflect.Type, as the type can be extracted by reflect and then a standard type switch will work. For example:

type test struct {
    I int
    S string
    Us []uint8
}

func (t *test) SetIndexedField(index int, value interface{}) {
    e := reflect.ValueOf(t).Elem()
    p := e.Field(index)
    v := p.Interface()
    typeOfF := e.Field(index).Type()
    switch v.(type) {
        case int:
            p.SetInt(int64(value.(int)))
        case string:
            p.SetString(value.(string))
        case []uint8:
            p.SetBytes(value.([]uint8))
        default:
            fmt.Println("Unsupported", typeOfF, v, value)
    }
}

The following examples demonstrate the use of this function:

var t = test{10, "test string", []uint8 {1, 2, 3, 4}}
fmt.Println(t)
(&t).SetIndexedField(0, 5)
(&t).SetIndexedField(1, "new string")
(&t).SetIndexedField(2, []uint8 {8, 9})
fmt.Println(t)

(A few points on reflection in go:

  1. It is necessary to export the struct fields for reflect to be able to use them, hence the capitalisation of the field names
  2. In order to modify the field values, it would be necessary to use a pointer to the struct as in this example function
  3. Elem() is used to "dereference" the pointer in reflect

)

答案6

得分: 0

好的,以下是翻译好的内容:

	ty := reflect.TypeOf(*c)
	vl := reflect.ValueOf(*c)
	for i := 0; i < ty.NumField(); i++ {
		switch vl.Field(i).Interface().(type) {
		case string:
			fmt.Printf("类型: %s 值: %s \n", ty.Field(i).Name, vl.Field(i).String())
		case int:
			fmt.Printf("类型: %s 值: %d \n", ty.Field(i).Name, vl.Field(i).Int())
		}
	}
英文:

Well, I did this by first transfer it to interface and then use the.(type)

	ty := reflect.TypeOf(*c)
	vl := reflect.ValueOf(*c)
	for i:=0;i&lt;ty.NumField();i++{
		switch vl.Field(i).Interface().(type) {
		case string:
			fmt.Printf(&quot;Type: %s Value: %s \n&quot;,ty.Field(i).Name,vl.Field(i).String())
		case int:
			fmt.Printf(&quot;Type: %s Value: %d \n&quot;,ty.Field(i).Name,vl.Field(i).Int())
		}
	}

huangapple
  • 本文由 发表于 2015年10月31日 03:50:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/33443867.html
匿名

发表评论

匿名网友

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

确定