【发布时间】:2013-12-27 22:56:16
【问题描述】:
两者的主要区别是什么:
v = t.(aType) // type assertionv = aType(t) // type conversion
我应该在哪里使用类型断言或类型转换?
【问题讨论】:
-
也有点跑题了,但值得注意的是,强制转换通常意味着某种运行时支持,但断言纯粹是编译时构造,也是一种向编译器提示您希望代码如何执行的方法进行分析。
标签: go
两者的主要区别是什么:
v = t.(aType) // type assertionv = aType(t) // type conversion我应该在哪里使用类型断言或类型转换?
【问题讨论】:
标签: go
类型断言断言t(接口类型)实际上是aType,而t 将是aType;即包装在t 接口中的那个。例如。如果您知道您的var reader io.Reader 实际上是*bytes.Buffer,您可以使用var br *bytes.Buffer = reader.(*bytes.Buffer)。
类型转换将一种(非接口)类型转换为另一种,例如var x uint8 to 和 int64 像 var id int64 = int64(x)。
经验法则:如果您必须将具体类型包装到接口中并希望返回具体类型,请使用类型断言(或类型开关)。如果您需要将一种具体类型转换为另一种,请使用类型转换。
【讨论】:
t没有修改?
v,而不是t
t 而不是v。 @nemo:不,t 不会被类型断言或类型转换修改。之后我强烈推荐 Tour of Go 和语言规范。
v 而不是t。 t 在这种情况下是某种接口类型,例如interface{} 和断言 on t 之后,resulting 值 v 将是 aType 类型。
tl;dr 断言对接口的动态值进行操作并在运行时进行评估,而转换对静态类型进行操作并在编译时进行评估。
您知道 Go 接口基本上是一个方法集规范,您可以将任何类型实现该方法集的值分配给接口变量1。
type assertion 写成x.(T) 断言存储在接口x 中的值是T 类型。当您想要取消包装该值时,您可以使用类型断言。
最常见的用途之一是当您拥有interface{} 并且您需要检索具体值时。例如,Context 值:
func foo(ctx context.Context) {
s := ctx.Value("my_key").(string) // signature is `Value(key interface{}) interface{}`
// do something with s...
}
因为有关存储在接口中的值的信息仅在运行时可用,所以未经检查的类型断言可能会出现恐慌——您必须使用特殊的形式赋值 v, ok := x.(T) 来避免它。
ctx = context.WithValue(ctx, "my_key", "foo")
s := ctx.Value("my_key").(int) // panic
v, ok := ctx.Value("my_key").(string)
fmt.Println(v, ok) // "foo" true
此外,当x.(T) 中的T 本身是一个接口时,断言检查存储在x 中的值是否实现了T。结果和上面一样。
将type conversion 写成T(x) 而不是“将表达式的类型更改为转换指定的类型”,即将x 的类型更改为T。可能与断言的最大区别在于类型转换是静态检查2。编译时将捕获无效转换:
type Foo string
type Bar int
a := "foo"
fmt.Println(Bar(a)) // cannot convert a (type string) to type Bar
控制类型转换的规则还涵盖了比类型断言更多的情况。主要的是assignability,但是对于数值类型、字符串和字节/符文切片、有向通道、切片和数组指针等之间的转换有特殊情况。
通常,当您已经知道所涉及的类型是什么时,您会使用转换,并且只是想将一种转换为另一种:
b := []byte("foo") // converts string literal to byte slice
注意事项:
1:更正式地说,当值的方法集是接口方法集的超集时;这也是为什么空接口interface{} 可以保存任何值的原因,因为任何集合都是空集合的超集。
2:当x.(T) 中的类型T 没有实现接口时,也会在编译时检查类型断言。实际上,当 x 为 interface{} 时,这不会帮助您捕获错误,因为所有类型都实现了它。
【讨论】: