简体   繁体   English

如何打开reflect.Type?

[英]How to switch on reflect.Type?

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
}

First question, are you sure you want to switch on reflect.Type and not use a type switch?第一个问题,您确定要打开reflect.Type而不使用类型开关吗? 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
    }

}

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?你真正想要完成什么?

The answer to the initial question How to switch on reflect.Type?最初问题的答案如何打开reflect.Type? is: You can't.是:你不能。 However, you can do it with reflect.Value .但是,您可以使用reflect.Value来做到这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.给定一个变量v interface{}你可以调用reflect.TypeOf(v)reflect.ValueOf(v) ,它们返回一个reflect.Typereflect.Value ,分别是。
    • If the type of v is not interface{} then these function calls will convert it to interface{} .如果v的类型不是interface{}那么这些函数调用会将其转换为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. reflect.Type包含有关类型的各种运行时信息,但它包含任何可用于获取类型v根据需要在型交换机本身。
  • Hovewer, reflect.Value provides it through its Interface() method, which returns the underlying value as interface{} .然而, reflect.Value通过其Interface()方法提供它,该方法将底层值作为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.也许它有助于澄清一些可能导致 Go 中动态类型混淆的要点。 At least I was confused by this for quite some time.至少我对此感到困惑了很长一段时间。

reflect vs. interface{} reflectinterface{}

In Go there are two systems of run-time generics:在 Go 中有两个运行时泛型系统:

  • In the language : interface{} , useful for type switches/assertions,语言: interface{} ,对于类型切换/断言很有用,
  • In the library : The reflect package, useful for inspection of run-time generic types and values of such.库中reflect包,用于检查运行时泛型类型和此类的值。

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.例如,给定一个interface{} ,在普通 Go(使用安全代码)中,如果值是数组或切片,无论其元素类型如何,都无法获取第 i 个元素的值。 One needs to use reflect in order to do that.需要使用reflect才能做到这一点。 Conversely, with reflect it is impossible to make a type switch or assertion: convert it to interface{} , then you can do that.相反,使用reflect不可能进行类型切换或断言:将其转换为interface{} ,然后您可以这样做。

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.一方面,它是TypeOf()ValueOf()函数,它们接受interface{}并返回一个reflect结构。 In the other direction it is Value.Interface() .在另一个方向是Value.Interface()

It is a bit counter-intuitive that one needs a Value , not a Type , to do a type switch.需要一个Value而不是Type来进行类型切换有点违反直觉。 At least this is somewhat consistent with the fact that one needs a value construct a Type by calling TypeOf() .至少这与需要一个值通过调用TypeOf()构造一个Type的事实有些一致。

reflect.Kind

Both reflect.Type and reflect.Value have a Kind() method. reflect.Typereflect.Value都有一个Kind()方法。 Some suggest using the value these methods return, of type reflect.Kind , to imitate a type switch.有些人建议使用这些方法返回的值(类型为reflect.Kind )来模拟类型切换。

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例如,使用Kind无法区分int64time.Duration因为后者被定义

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. Kind可用于判断类型是否为任何类型的结构、数组、切片等,而不管它由何种类型组成。 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.) (旁注。我有同样的问题,发现这里没有任何有用的答案,所以我自己去弄清楚。重复的反问“你为什么要这样做?”,随后是无关的答案也没有帮助我。我有一个很好的原因,我想这样做正是这样。)

This might work.这可能会奏效。

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

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.正如其他人所说,通过打开reflect.Type 并不清楚您想要实现什么但是,我在尝试做类似的事情时遇到了这个问题,所以我会给你我的解决方案,以防它回答你的问题。

As captncraig said , a simple type switch could be done on a interface{} variable without needing to use reflect.正如captncraig 所说,可以在 interface{} 变量上完成简单的类型切换,而无需使用反射。

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.不需要直接在reflect.Type上切换,因为可以通过reflect提取类型,然后标准类型切换就可以了。 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: (关于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 Elem() 用于“取消引用”reflect 中的指针

) )

Well, I did this by first transfer it to interface and then use the .(type)好吧,我通过首先将其传输到界面然后使用.(type)来做到这一点

    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("Type: %s Value: %s \n",ty.Field(i).Name,vl.Field(i).String())
        case int:
            fmt.Printf("Type: %s Value: %d \n",ty.Field(i).Name,vl.Field(i).Int())
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM