Как проверить тип переменной во время выполнения на языке Go - PullRequest
49 голосов
/ 09 августа 2011

У меня есть несколько функций 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 type во время выполнения.Как мне это сделать, и является ли это хорошей идеей (если нет, что является хорошей практикой в ​​этом случае)?

Ответы [ 5 ]

72 голосов
/ 10 августа 2011

Кажется, что Go имеет специальную форму переключателя, посвященного этому (он называется 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)))
    } 
}
48 голосов
/ 06 октября 2013

Ответ @Darius - самый идиоматичный (и, вероятно, более производительный) метод.Одно ограничение заключается в том, что проверяемый тип должен иметь тип interface{}.Если вы используете конкретный тип, он потерпит неудачу.

Альтернативный способ определения типа чего-либо во время выполнения, включая конкретные типы, заключается в использовании пакета Go reflect.Объединяя в цепочку TypeOf(x).Kind() вместе, вы можете получить значение reflect.Kind, которое имеет тип uint: http://golang.org/pkg/reflect/#Kind

Затем вы можете выполнять проверки типов вне блока переключателей, например:

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

Опять же, это, вероятно, не самый предпочтительный способ сделать это, но полезно знать альтернативные варианты.

12 голосов
/ 09 августа 2011

См. Утверждения типа здесь:

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

Я бы утверждал только разумный тип (string, uint64) и т. Д. И оставлял его как можно более свободным, выполняя преобразование в родной тип последним.

3 голосов
/ 10 марта 2018

Ответ 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")
}
0 голосов
/ 09 августа 2011

Что не так с

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

и так далее?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...