What does the keyword type mean when used in a switch?

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

What does the keyword type mean when used in a switch?

问题

我在golang中看到了几个这样的代码实例:

func process(node ast.Node) Foo {
    switch n := node.(type) {
        // ... 省略部分代码
    }
}

ast.Node 是一个接口。node.(type) 这段代码是用来进行反射的吗?是为了找出接口的实际实现者吗?

英文:

I have seen several instances of this code in golang:

func process(node ast.Node) Foo {
    switch n := node.(type) {
        // ... removed for brevity
    }
}

ast.Node is an interface. Is the node.(type) snippet code to perform reflection; to find out the actual implementers of the interface ?

答案1

得分: 11

是的,这被称为类型开关(Type switch)。它允许你根据传递的接口的实际类型执行代码。

我认为官方文档中的示例很清楚:

开关语句也可以用于发现接口变量的动态类型。这种类型开关使用带有关键字type的类型断言语法。如果开关语句在表达式中声明了一个变量,那么该变量在每个分支中将具有相应的类型。在这种情况下,重用该名称是惯用的做法,实际上在每种情况下声明了一个具有相同名称但不同类型的新变量。

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    fmt.Printf("unexpected type %T", t)       // %T 打印 t 的类型
case bool:
    fmt.Printf("boolean %t\n", t)             // t 的类型是 bool
case int:
    fmt.Printf("integer %d\n", t)             // t 的类型是 int
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t) // t 的类型是 *bool
case *int:
    fmt.Printf("pointer to integer %d\n", *t) // t 的类型是 *int
}

在一个类型正确的程序中,你不应该经常使用这个,但在需要时它很方便。一个使用示例:假设你实现了一个数据库驱动程序,你可能需要根据 Go 变量的类型进行转换。这是go-sql/mysql 驱动程序的一个摘录

// Scan 实现了 Scanner 接口。
// 值的类型必须是 time.Time 或 string / []byte(格式化的时间字符串),
// 否则 Scan 将失败。
func (nt *NullTime) Scan(value interface{}) (err error) {
    if value == nil {
        nt.Time, nt.Valid = time.Time{}, false
        return
    }

    switch v := value.(type) {
    case time.Time:
        nt.Time, nt.Valid = v, true
        return
    case []byte:
        nt.Time, err = parseDateTime(string(v), time.UTC)
        nt.Valid = (err == nil)
        return
    case string:
        nt.Time, err = parseDateTime(v, time.UTC)
        nt.Valid = (err == nil)
        return
    }

    nt.Valid = false
    return fmt.Errorf("Can't convert %T to time.Time", value)
}
英文:

Yes. It's called a Type switch. It allows you to execute code depending on the actual type of the interface you pass.

I think the official documentation, with its example, is clear :

> A switch can also be used to discover the dynamic type of an interface
> variable. Such a type switch uses the syntax of a type assertion with
> the keyword type inside the parentheses. If the switch declares a
> variable in the expression, the variable will have the corresponding
> type in each clause. It's also idiomatic to reuse the name in such
> cases, in effect declaring a new variable with the same name but a
> different type in each case.

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    fmt.Printf("unexpected type %T", t)       // %T prints whatever type t has
case bool:
    fmt.Printf("boolean %t\n", t)             // t has type bool
case int:
    fmt.Printf("integer %d\n", t)             // t has type int
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
    fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}

You should not use that too often in a properly typed program but it's convenient when you need it. An example of use : Suppose you implement a database driver, you may have to do conversions depending on the type of the Go variables. Here's an extract of the go-sql/mysql driver :

// Scan implements the Scanner interface.
// The value type must be time.Time or string / []byte (formatted time-string),
// otherwise Scan fails.
func (nt *NullTime) Scan(value interface{}) (err error) {
	if value == nil {
		nt.Time, nt.Valid = time.Time{}, false
		return
	}

	switch v := value.(type) {
	case time.Time:
		nt.Time, nt.Valid = v, true
		return
	case []byte:
		nt.Time, err = parseDateTime(string(v), time.UTC)
		nt.Valid = (err == nil)
		return
	case string:
		nt.Time, err = parseDateTime(v, time.UTC)
		nt.Valid = (err == nil)
		return
	}

	nt.Valid = false
	return fmt.Errorf("Can't convert %T to time.Time", value)
}

答案2

得分: 2

它是**类型开关**:

> 开关还可以用于发现接口变量的动态类型。这种类型开关使用类型断言的语法,括号内使用关键字type。如果开关在表达式中声明一个变量,那么每个分支中的变量将具有相应的类型。在这种情况下,重用变量名是惯用的做法,实际上在每种情况下声明一个具有相同名称但不同类型的新变量。

使用类型开关,您可以根据接口值的类型进行开关(仅限接口值):

func do(v interface{}) string {
        switch u := v.(type) {
        case int:
                return strconv.Itoa(u*2) // u 的类型为 int
        case string:
                mid := len(u) / 2 // 分割 - u 的类型为 string
                return u[mid:] + u[:mid] // 拼接
        }
        return "unknown"
}

do(21) == "42"
do("bitrab") == "rabbit"
do(3.142) == "unknown"
英文:

It is TYPE SWITCH:

> A switch can also be used to discover the dynamic type of an interface
> variable. Such a type switch uses the syntax of a type assertion with
> the keyword type inside the parentheses. If the switch declares a
> variable in the expression, the variable will have the corresponding
> type in each clause. It's also idiomatic to reuse the name in such
> cases, in effect declaring a new variable with the same name but a
> different type in each case.

With a type switch you can switch on the type of an interface value (only):

func do(v interface{}) string {
        switch u := v.(type) {
        case int:
                return strconv.Itoa(u*2) // u has type int
        case string:
                mid := len(u) / 2 // split - u has type string
                return u[mid:] + u[:mid] // join
        }
        return "unknown"
}

do(21) == "42"
do("bitrab") == "rabbit"
do(3.142) == "unknown"

huangapple
  • 本文由 发表于 2014年2月26日 16:47:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/22036076.html
匿名

发表评论

匿名网友

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

确定