Go语言运行时检查变量类型的方法

发布于 2024-11-28 16:02:37 字数 408 浏览 1 评论 0原文

我有几个像这样声明的 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)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

始终不够 2024-12-05 16:02:38

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")
}
眼眸 2024-12-05 16:02:38

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

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

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

if reflect.kindOf(n) != reflect.kindOf(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.

只涨不跌 2024-12-05 16:02:38

有什么问题吗?

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?

陪我终i 2024-12-05 16:02:37

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

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)))
    } 
}
z祗昰~ 2024-12-05 16:02:37

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

掌心的温暖 2024-12-05 16:02:37

请参阅此处的类型断言:

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

我会仅断言合理的类型(字符串、uint64)等,并使其尽可能宽松,最后执行到本机类型的转换。

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

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...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文