【问题标题】:What is the difference between type conversion and type assertion?类型转换和类型断言有什么区别?
【发布时间】:2013-12-27 22:56:16
【问题描述】:

两者的主要区别是什么:

  1. v = t.(aType) // type assertion
  2. v = aType(t) // type conversion

我应该在哪里使用类型断言或类型转换?

【问题讨论】:

  • 也有点跑题了,但值得注意的是,强制转换通常意味着某种运行时支持,但断言纯粹是编译时构造,也是一种向编译器提示您希望代码如何执行的方法进行分析。

标签: go


【解决方案1】:

类型断言断言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 将是一个 aType" - 你能详细说明一下吗? t没有修改?
  • @nemo 我想他会说v,而不是t
  • @LucianoQ:不,是t 而不是v。 @nemo:不,t 不会被类型断言或类型转换修改。之后我强烈推荐 Tour of Go 和语言规范。
  • @Volker 感谢您的推荐,这是很好的讲座!但是,我仍然认为您的意思是v 而不是tt 在这种情况下是某种接口类型,例如interface{} 和断言 on t 之后,resultingv 将是 aType 类型。
  • @nemo。不,我的意思是 t 的动态类型是 aType。 v 的类型是无趣的。
【解决方案2】:

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 没有实现接口时,也会在编译时检查类型断言。实际上,当 xinterface{} 时,这不会帮助您捕获错误,因为所有类型都实现了它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-02-18
    • 1970-01-01
    • 2016-10-09
    • 2011-02-11
    • 2011-05-02
    • 2017-07-31
    • 1970-01-01
    • 2016-09-26
    相关资源
    最近更新 更多