如何在Go语言中在运行时检查变量类型

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

How to check variable type at runtime in Go language

问题

我有几个C函数声明如下:

CURLcode curl_wrapper_easy_setopt_long(CURL* curl, CURLoption option, long param);
CURLcode curl_wrapper_easy_setopt_str(CURL* curl, CURLoption option, char* param);

我想将它们作为一个Go函数公开,如下所示:

func (e *Easy)SetOption(option Option, param interface{})

所以我需要能够在运行时检查param的类型。我该如何做到这一点,这个想法好吗(如果不好的话,在这种情况下有什么好的做法)?

英文:

I have few C functions declared like this

CURLcode curl_wrapper_easy_setopt_long(CURL* curl, CURLoption option, long param);
CURLcode curl_wrapper_easy_setopt_str(CURL* curl, CURLoption option, char* param);

I would like to expose those as one Go function like this

func (e *Easy)SetOption(option Option, param interface{})

so I need to be able to check param type at runtime. How do I do that and is this good idea (if not what is good practice in this case)?

答案1

得分: 136

似乎Go语言有一种特殊的switch形式专门用于此(称为类型switch):

func (e *Easy)SetOption(option Option, param interface{}) {

    switch v := param.(type) { 
    default:
        fmt.Printf("unexpected type %T", v)
    case uint64:
        e.code = Code(C.curl_wrapper_easy_setopt_long(e.curl, C.CURLoption(option), C.long(v)))
    case string:
        e.code = Code(C.curl_wrapper_easy_setopt_str(e.curl, C.CURLoption(option), C.CString(v)))
    } 
}
英文:

It seems that Go have special form of switch dedicate to this (it is called type switch):

func (e *Easy)SetOption(option Option, param interface{}) {

    switch v := param.(type) { 
    default:
        fmt.Printf("unexpected type %T", v)
    case uint64:
        e.code = Code(C.curl_wrapper_easy_setopt_long(e.curl, C.CURLoption(option), C.long(v)))
    case string:
        e.code = Code(C.curl_wrapper_easy_setopt_str(e.curl, C.CURLoption(option), C.CString(v)))
    } 
}

答案2

得分: 72

@Darius的答案是最惯用的(也可能更高效)方法。一个限制是你要检查的类型必须是interface{}类型。如果你使用具体类型,它将失败。

另一种在运行时确定某个东西的类型的方法,包括具体类型,是使用Go的reflect包。通过将TypeOf(x).Kind()链接在一起,你可以得到一个reflect.Kind值,它是一个uint类型:http://golang.org/pkg/reflect/#Kind

然后你可以在switch块之外进行类型检查,像这样:

import (
    "fmt"
    "reflect"
)

// ....

x := 42
y := float32(43.3)
z := "hello"

xt := reflect.TypeOf(x).Kind()
yt := reflect.TypeOf(y).Kind()
zt := reflect.TypeOf(z).Kind()

fmt.Printf("%T: %s\n", xt, xt)
fmt.Printf("%T: %s\n", yt, yt)
fmt.Printf("%T: %s\n", zt, zt)

if xt == reflect.Int {
    println(">> x is int")
}
if yt == reflect.Float32 {
    println(">> y is float32")
}
if zt == reflect.String {
    println(">> z is string")
}

输出结果为:

reflect.Kind: int
reflect.Kind: float32
reflect.Kind: string
>> x is int
>> y is float32
>> z is string

再次强调,这可能不是首选的方法,但了解其他选项也是很好的。

英文:

The answer by @Darius is the most idiomatic (and probably more performant) method. One limitation is that the type you are checking has to be of type interface{}. If you use a concrete type it will fail.

An alternative way to determine the type of something at run-time, including concrete types, is to use the Go reflect package. Chaining TypeOf(x).Kind() together you can get a reflect.Kind value which is a uint type: http://golang.org/pkg/reflect/#Kind

You can then do checks for types outside of a switch block, like so:

import (
    "fmt"
    "reflect"
)

// ....

x := 42
y := float32(43.3)
z := "hello"

xt := reflect.TypeOf(x).Kind()
yt := reflect.TypeOf(y).Kind()
zt := reflect.TypeOf(z).Kind()

fmt.Printf("%T: %s\n", xt, xt)
fmt.Printf("%T: %s\n", yt, yt)
fmt.Printf("%T: %s\n", zt, zt)

if xt == reflect.Int {
    println(">> x is int")
}
if yt == reflect.Float32 {
    println(">> y is float32")
}
if zt == reflect.String {
    println(">> z is string")
}

Which prints outs:

reflect.Kind: int
reflect.Kind: float32
reflect.Kind: string
>> x is int
>> y is float32
>> z is string

Again, this is probably not the preferred way to do it, but it's good to know alternative options.

答案3

得分: 27

quux00的答案只是关于比较基本类型的。

如果你需要比较自定义的类型,你不应该使用reflect.TypeOf(xxx),而是使用reflect.TypeOf(xxx).Kind()

有两种类型的类别:

  • 直接类型(你直接定义的类型)
  • 基本类型(int,float64,struct,...)

这是一个完整的例子:

type MyFloat float64
type Vertex struct {
    X, Y float64
}

type EmptyInterface interface {}

type Abser interface {
    Abs() float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (f MyFloat) Abs() float64 {
    return math.Abs(float64(f))
}

var ia, ib Abser
ia = Vertex{1, 2}
ib = MyFloat(1)
fmt.Println(reflect.TypeOf(ia))
fmt.Println(reflect.TypeOf(ia).Kind())
fmt.Println(reflect.TypeOf(ib))
fmt.Println(reflect.TypeOf(ib).Kind())

if reflect.TypeOf(ia) != reflect.TypeOf(ib) {
    fmt.Println("Not equal typeOf")
}
if reflect.TypeOf(ia).Kind() != reflect.TypeOf(ib).Kind() {
    fmt.Println("Not equal kind")
}

ib = Vertex{3, 4}
if reflect.TypeOf(ia) == reflect.TypeOf(ib) {
    fmt.Println("Equal typeOf")
}
if reflect.TypeOf(ia).Kind() == reflect.TypeOf(ib).Kind() {
    fmt.Println("Equal kind")
}

输出将会是:

main.Vertex
struct
main.MyFloat
float64
Not equal typeOf
Not equal kind
Equal typeOf
Equal kind

正如你所见,reflect.TypeOf(xxx)返回的是你可能想要使用的直接类型,而reflect.TypeOf(xxx).Kind()返回的是基本类型。


这是结论。如果你需要与基本类型比较,使用reflect.TypeOf(xxx).Kind();如果你需要与自定义类型比较,使用reflect.TypeOf(xxx)

if reflect.TypeOf(ia) == reflect.TypeOf(Vertex{}) {
    fmt.Println("self-defined")
} else if reflect.TypeOf(ia).Kind() == reflect.Float64 {
    fmt.Println("basic types")
}
英文:

quux00's answer only tells about comparing basic types.

If you need to compare types you defined, you shouldn't use reflect.TypeOf(xxx). Instead, use reflect.TypeOf(xxx).Kind().

There are two categories of types:

  • direct types (the types you defined directly)
  • basic types (int, float64, struct, ...)

Here is a full example:

type MyFloat float64
type Vertex struct {
	X, Y float64
}

type EmptyInterface interface {}

type Abser interface {
	Abs() float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (f MyFloat) Abs() float64 {
	return math.Abs(float64(f))
}

var ia, ib Abser
ia = Vertex{1, 2}
ib = MyFloat(1)
fmt.Println(reflect.TypeOf(ia))
fmt.Println(reflect.TypeOf(ia).Kind())
fmt.Println(reflect.TypeOf(ib))
fmt.Println(reflect.TypeOf(ib).Kind())

if reflect.TypeOf(ia) != reflect.TypeOf(ib) {
	fmt.Println("Not equal typeOf")
}
if reflect.TypeOf(ia).Kind() != reflect.TypeOf(ib).Kind() {
	fmt.Println("Not equal kind")
}

ib = Vertex{3, 4}
if reflect.TypeOf(ia) == reflect.TypeOf(ib) {
	fmt.Println("Equal typeOf")
}
if reflect.TypeOf(ia).Kind() == reflect.TypeOf(ib).Kind() {
	fmt.Println("Equal kind")
}

The output would be:

main.Vertex
struct
main.MyFloat
float64
Not equal typeOf
Not equal kind
Equal typeOf
Equal kind

As you can see, reflect.TypeOf(xxx) returns the direct types which you might want to use, while reflect.TypeOf(xxx).Kind() returns the basic types.


Here's the conclusion. If you need to compare with basic types, use reflect.TypeOf(xxx).Kind(); and if you need to compare with self-defined types, use reflect.TypeOf(xxx).

if reflect.TypeOf(ia) == reflect.TypeOf(Vertex{}) {
	fmt.Println("self-defined")
} else if reflect.TypeOf(ia).Kind() == reflect.Float64 {
	fmt.Println("basic types")
}

答案4

得分: 24

请参考这里的类型断言:

http://golang.org/ref/spec#Type_assertions

我会断言一个合理的类型(例如string,uint64等),并尽可能保持它的松散性,最后将其转换为本机类型。

func (e *Easy)SetOption(option Option, param interface{}) {
    if s, ok := param.(string); ok {
        // 这里s是string类型
    }
    // 其他情况...
}
英文:

See type assertions here:

http://golang.org/ref/spec#Type_assertions

I'd assert a sensible type (string, uint64) etc only and keep it as loose as possible, performing a conversion to the native type last.

func (e *Easy)SetOption(option Option, param interface{}) {
    if s, ok := param.(string); ok {
        // s is string here
    }
    // else...
}

答案5

得分: 0

func (e *Easy)SetStringOption(option Option, param string)
func (e *Easy)SetLongOption(option Option, param long)

等等?

英文:

What's wrong with

func (e *Easy)SetStringOption(option Option, param string)
func (e *Easy)SetLongOption(option Option, param long)

and so on?

答案6

得分: 0

有另一种方法可以断言变量类型,即“直接类型(直接定义的类型)”,如@MewX所评论的。
这是通过在比较中直接实例化类型来实现的。

// 我的“直接”类型
type deck []string

d := deck{"foo", "bar"}

if reflect.TypeOf(n) != reflect.TypeOf(deck{}) {
   ...
}

deck{}创建了该类型的一个空实例,在这种情况下,是一个空的string切片,比较可以正常工作。

英文:

There's yet another way to assert a variable type of the kind "direct types (the types you defined directly)" as @MewX commented.
It is by instantiating the type directly in the comparison.

// My "direct" type
type deck []string

d := deck{"foo", "bar"}

if reflect.kindOf(n) != reflect.kindOf(deck{}) {
   ...
}

The deck{} creates an empty instance of the type, in this case, an empty slice of string and the comparison works.

huangapple
  • 本文由 发表于 2011年8月9日 21:12:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/6996704.html
匿名

发表评论

匿名网友

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

确定