开发者

How to check variable type at runtime in Go language

开发者 https://www.devze.com 2023-03-27 07:00 出处:网络
I have few C functions declared like this CURLcode curl_wrapper_easy_setopt_long(CURL* curl, CURLoption option, long 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 lik开发者_StackOverflow社区e 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)?


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)))
    } 
}


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.


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")
}


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...
}


What's wrong with

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

and so on?

0

精彩评论

暂无评论...
验证码 换一张
取 消