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

hsgswve4  于 2023-06-03  发布在  Go
关注(0)|答案(6)|浏览(199)

我有几个这样声明的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 类型。我如何做到这一点,这是一个好主意(如果不是什么是良好的做法在这种情况下)?

jgwigjjp

jgwigjjp1#

Go语言似乎有一种特殊形式的switch专门用于此(它被称为 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)))
    } 
}
p8ekf7hl

p8ekf7hl2#

@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

同样,这可能不是首选的方法,但知道替代方案是很好的。

sdnqo3pr

sdnqo3pr3#

quux 00的答案只告诉了比较基本类型。
如果你需要比较你定义的类型,你不应该使用reflect.TypeOf(xxx)。使用reflect.TypeOf(xxx).Kind()
有两类类型:

  • 直接类型(您直接定义的类型)
  • 基本类型(int,float 64,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")
}
wfauudbj

wfauudbj4#

请参见这里的类型Assert:
http://golang.org/ref/spec#Type_assertions
我只Assert一个合理的类型(string,uint64)等,并尽可能保持宽松,最后执行到本地类型的转换。

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

sauutmhj5#

怎么了

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

等等

unftdfkk

unftdfkk6#

还有另一种方法可以Assert一个变量类型,如@MewX所评论的“direct types(您直接定义的类型)”。它是通过在比较中直接示例化类型。

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

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

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

deck{}创建了类型的一个空示例,在本例中,是string的一个空切片,比较工作正常。

相关问题