在Go中解释类型断言 [英] Explain Type Assertions in Go

查看:409
本文介绍了在Go中解释类型断言的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读关于类型断言 x。(T) in Go编程语言并不理解它们。



据我所知,场景:


  • T是一个具体类型或接口
  • 一个(声明值?)或可以返回两个(ok)值



这是我不明白的:


  • 为什么要使用它们?

  • 它们返回的是什么?



我也在这个主题上搜索了一下,但仍然不明白。

解决方案

在一行中:


x。(T)断言 x 不为零,而存储在 x 类型为 T


我会使用它们:




  • 检查 x 是否为零

  • 检查它是否可以转换(assert)为另一种类型
  • 转换(assert)为另一种类型


    他们返回的是什么:


    • t:=如果 x 为零,x。(T) => t的类型为 T 它恐慌。


    • t,ok:= x。(T) => if x 是零或者不是 T => ok false 否则 ok true t 类型为 T




    < hr>

    想象一下,您需要计算4个不同s的面积hapes:圆形,正方形,矩形和三角形。
    所以你可以使用新方法定义新类型,如 Area(),如下所示:

     类型Circle结构{
    半径float64
    }
    func(t Circle)Area()float64 {
    return math .Pi * t.Radius * t.Radius
    }

    并且对于 Triangle

    type Triangle struct {
    A,B,C float64 //三角形边的长度。
    }
    func(t三角形)面积()float64 {
    p:=(tA + tB + tC)/ 2.0 //周长一半
    返回math.Sqrt(p * p - tA)*(p - tB)*(p - tC))
    }

    对于 Rectangle

    type Rectangle struct {
    A,B float64
    }

    func(t Rectangle)Area()float64 {
    return tA * tB
    }

    对于 Square

    类型Square结构{
    A float64
    }
    func(t Square)Area )float64 {
    return tA * tA
    }

    这里 Circle ,其半径为1.0,其他形状为它们的边:

      shapes:= [] Shape {
    Circle {1.0},
    Square {1.772453},
    Rectangle {5,10},
    Triangle {10,4, 7},
    }

    有趣!如何将它们全部收集在一个地方?

    首先你需要 Shape接口将它们全部收集在一个Slice of Shape中 [ ] Shape :

    类型Shape接口{
    区域()float64
    }

    现在您可以像这样收集它们:


    $ b $ $ {$}},
    Square {1.772453}

      shapes:= [ ,
    Rectangle {5,10},
    Triangle {10,4,7},
    }

    毕竟 Circle 是一个形状三角形也是形状

    现在您可以使用单个语句 v打印每个形状的区域。 Area()


    $ b

      for _,v:=范围形状{
    fmt.Println(v,\ tArea:,v.Area())
    }

    因此 Area()是常见的i所有形状之间的接口。
    现在如何使用上面的 shapes 来计算和调用像三角形之类的非常规方法:

      func(t Triangle)Angles()float64 {
    return [] float64 {angle(tB,tC,tA),angle(tA,tC, (a * b + b * bc),角度(tA,tB,tC)}
    } * c)/(2 * a * b))* 180.0 / math.Pi
    }



    <现在是时候从形状中提取三角形



    for _,v:=范围形状{
    fmt.Println(v,\ tArea:,v.Area() )
    如果t,ok:= v。(三角形); OK {
    fmt.Println(Angles:,t.Angles())
    }
    }

    使用 t,ok:= v。(三角形)我们请求了类型断言。
    表示我们从编译器询问尝试将 Shape 类型的 v 转换为类型三角,所以如果成功, ok 将会是 true 否则 false ,那么如果成功调用 t.Angles()来计算三角形的三个角度。



    这是输出:

    $ p $ Circle(Radius:1)Area:3.141592653589793
    Square面积:10.88746497197197
    角度:[128.68218745348943 18.194872338766785 33.12294020774379]面积:3.1415896372090004
    方形(面积:5,10)面积:50
    面积:

    以及整个工作示例代码:

      package main 

    importfmt
    importmath

    func main() {
    shapes:= [] Shape {
    Circle {1.0},
    Square {1.772453},
    Rectangle {5,10},
    Triangl e {10,4,7},
    }
    for _,v:=范围形状{
    fmt.Println(v,\ tArea:,v.Area())
    if t,ok:= v。(Triangle); OK {
    fmt.Println(Angles:,t.Angles())
    }
    }
    }

    类型Shape接口{
    Area()float64
    }
    类型Circle结构{
    半径float64
    }
    类型三角形结构{
    A,B,C float64 //三角形边的长度。
    }
    类型矩形struct {
    A,B float64
    }
    类型Square结构{
    A float64
    }

    func(t Circle)Area()float64 {
    return math.Pi * t.Radius * t.Radius
    }

    // Heron公式一个三角形
    func(t三角形)面积()float64 {
    p:=(tA + tB + tC)/ 2.0 //周长一半
    返回数学sqrt(p *(p - tA )*(p - tB)*(p - tC))
    }
    func(t Rectangle)Area()float64 {
    return tA * tB
    }

    func(t Square)Area()float64 {
    return tA * tA
    }

    func(t Circle)String()string {
    return fmt.Sprint(Circle(Radius:,t.Radius,))
    }
    func(t Triangle)String()string {
    return fmt.Sprint(Triangle Sides:,tA,,,tB,,,tC,))
    }
    func(t Rectangle)String()string {
    return fmt.Sprint 矩形(Sides:,tA,,,tB,))
    }
    func(t Square)String()string {
    返回fmt.Sprint(Square(Sides:,tA,))
    }

    func(t三角形)Angles()[] float64 {
    return [] float64 {角度(tB,tC,tA),角度(tA,tC,tB),角度(tA,tB,tC)}
    }
    函数角度(a,b,c float64)
    return math.Acos((a * a + b * bc * c)/(2 * a * b))* 180.0 / math.Pi
    }



    另见:

    类型断言


    $ b


    对于接口类型和类型的表达式x T,主
    表达式

      x。(T)

    断言x不为零,并且存储在x中的值为类型T.记号x。(T)称为类型断言。更精确地说,如果T不是接口类型,x。(T)断言x的
    动态类型与类型T.在这种情况下,T必须
    实现(接口) x的类型;否则类型断言是
    无效,因为x不能存储类型T的值。如果
    T是一个接口类型,x。(T)断言动态类型x
    实现了接口T。



    如果类型断言成立,则表达式的值是存储在x中的值
    ,其类型为T. 如果类型断言为false,则发生
    运行时恐慌。
    换句话说,即使
    x的动态类型只在运行时才知道,即x的类型。 (T)在一个
    正确的程序中是已知的。

      var x interface {} = 7 // x有动态类型int和值7 
    i:= x。(int)//我有类型int和值7

    类型I接口{m()}
    var y I
    s:= y。(string)//非法:字符串不实现I(缺少方法m)
    r:= y。(io.Reader)// r具有类型io.Reader和y must实现I和io.Reader

    类型asse rtion用于赋值或
    特殊格式的初始化中

    $ $ $ $ $ $ $ $ $ $ $ $ bv,ok:= x。(T)
    var v,ok = x。(T)

    会产生一个额外的无类型布尔值。如果
    声明成立,则ok的值为true。否则它是假的,v的值是类型T的
    零值。在这种情况下不会发生运行时恐慌

    编辑:

    问题:当T是一个 $ b时,断言 x。(T) code> interface {}
    而不是具体类型?

    答案:


    声明x不为零,并且存储在x中的值为T类型。


    例如这个恐慌(编译:成功,运行: panic:interface conversion:interface is nil,not interface {} ):

    < pre class =lang-golang prettyprint-override> package main

    func main(){
    var i interface {} // nil
    var _ = i。(interface {})
    }

    ):

    package main

    importfmt

    func main(){
    var i interface {} // nil
    b,ok:= i。(interface {})
    fmt.Println(b,ok)/ /< nil> false

    i = 2
    c,ok:= i。(interface {})
    fmt.Println(c,ok)// 2 true

    // var j int = c //在赋值中不能使用c(type interface {})作为int类型:need type assertion
    //fmt.Println(j)
    }

    输出:

     < ;零> false 
    2 true

    注意: c 类型为界面{} 而不是 int



    请看带有注释输出的示例代码:

      package main 
    $ b importfmt

    func main(){
    const fm =''%T'\\'t'%#[1] v '\t'%[1] v'\t%v\\\

    var i interface {}
    b,ok:= i。(interface {})
    fmt。 printf(fm,b,ok)//'< nil>''< nil>''< nil>'false

    i = 2 $ b $,ok = i。 {})
    fmt.Printf(fm,b,ok)//'int''2''2'true

    i =Hi
    b,ok = i。 (interface {})
    fmt.Printf(fm,b,ok)//'string''Hi''Hi'true

    i = new(interface {})
    b,ok = i。(interface {})
    fmt.Printf(fm,b,ok)//'* interface {}''(* interface {} )(0xc042004330)''0xc042004330'true

    i = struct {} {
    b,ok = i。(interface {})
    fmt.Printf(fm,b,ok )//结构{}结构{} {}'true}

    i = fmt.Println
    b,ok = i。(interface {})
    fmt.Printf(fm,b,ok)//'func(... interface {})(int,error)''(func(... interface {})(int,error))(0x456740)'' 0x456740'true

    i = Shape.Area
    b,ok = i。(interface {})
    fmt.Printf(fm,b,ok)//'func(main。形状)float64''(func(main.Shape)float64)(0x401910)''0x401910'true
    }

    类型Shape接口{
    Area()float64
    }


    I'm reading about type assertions x.(T) in The Go Programming Language and don't understand them.

    I understand that there are different scenarios:

    • T is a concrete type or an interface
    • One (asserted value?) or two (ok) values can be returned

    This is what I don't understand:

    • Why would I use them?
    • What exactly do they return?

    I have also googled on the topic and still don't understand.

    解决方案

    In one line:

    x.(T) asserts that x is not nil and that the value stored in x is of type T.

    Why would I use them:

    • to check x is nil or not
    • to check is it convertible (assert) to another type
    • convert (assert ) to another type

    What exactly do they return:

    • t := x.(T) => t is of type T, if x is nil it panics.

    • t,ok := x.(T) => if x is nil or not of type T => ok is false otherwise ok is true and t is of type T.


    Imagine you need to calculate Area of 4 different shapes: Circle, Square, Rectangle and Triangle. So you may define new types with new method called Area(), like this:

    type Circle struct {
        Radius float64
    }
    func (t Circle) Area() float64 {
        return math.Pi * t.Radius * t.Radius
    }
    

    And for Triangle:

    type Triangle struct {
        A, B, C float64 // lengths of the sides of a triangle.
    }
    func (t Triangle) Area() float64 {
        p := (t.A + t.B + t.C) / 2.0 // perimeter half
        return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
    }
    

    And for Rectangle:

    type Rectangle struct {
        A, B float64
    }
    
    func (t Rectangle) Area() float64 {
        return t.A * t.B
    }
    

    And for Square:

    type Square struct {
        A float64
    }
    func (t Square) Area() float64 {
        return t.A * t.A
    }
    

    Here you are Circle with radius of 1.0 and other shapes with their sides:

    shapes := []Shape{
        Circle{1.0},
        Square{1.772453},
        Rectangle{5, 10},
        Triangle{10, 4, 7},
    }
    

    Interesting! How to collect them all in one place?
    first you need Shape interface to collect them all in one Slice of Shape []Shape :

    type Shape interface {
        Area() float64
    }
    

    Now you can collect them like this:

    shapes := []Shape{
        Circle{1.0},
        Square{1.772453},
        Rectangle{5, 10},
        Triangle{10, 4, 7},
    }
    

    After all Circle is a Shape and Triangle is a Shape too.
    Now you may print area of each shape using single statement v.Area() :

    for _, v := range shapes {
        fmt.Println(v, "\tArea:", v.Area())
    }
    

    So Area() is common interface between all shapes. Now how to calculate and call uncommon method like angles of triangle using above shapes:

    func (t Triangle) Angles() []float64 {
        return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
    }
    func angle(a, b, c float64) float64 {
        return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
    }
    

    Now it's time to extract Triangle from above shapes:

    for _, v := range shapes {
        fmt.Println(v, "\tArea:", v.Area())
        if t, ok := v.(Triangle); ok {
            fmt.Println("Angles:", t.Angles())
        }
    }
    

    using t, ok := v.(Triangle) we requested type assertions. meaning we asked from compiler try to convert v of type Shape to type Triangle, so if it is successful the ok will be true otherwise false, then if it is successful call t.Angles() to calculate triangle three angles.

    this is the output:

    Circle (Radius: 1)  Area: 3.141592653589793
    Square (Sides: 1.772453)    Area: 3.1415896372090004
    Rectangle (Sides: 5, 10)    Area: 50
    Triangle (Sides: 10, 4, 7)  Area: 10.928746497197197
    Angles: [128.68218745348943 18.194872338766785 33.12294020774379]
    

    And whole working sample code:

    package main
    
    import "fmt"
    import "math"
    
    func main() {
        shapes := []Shape{
            Circle{1.0},
            Square{1.772453},
            Rectangle{5, 10},
            Triangle{10, 4, 7},
        }
        for _, v := range shapes {
            fmt.Println(v, "\tArea:", v.Area())
            if t, ok := v.(Triangle); ok {
                fmt.Println("Angles:", t.Angles())
            }
        }
    }
    
    type Shape interface {
        Area() float64
    }
    type Circle struct {
        Radius float64
    }
    type Triangle struct {
        A, B, C float64 // lengths of the sides of a triangle.
    }
    type Rectangle struct {
        A, B float64
    }
    type Square struct {
        A float64
    }
    
    func (t Circle) Area() float64 {
        return math.Pi * t.Radius * t.Radius
    }
    
    // Heron's Formula for the area of a triangle
    func (t Triangle) Area() float64 {
        p := (t.A + t.B + t.C) / 2.0 // perimeter half
        return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
    }
    func (t Rectangle) Area() float64 {
        return t.A * t.B
    }
    
    func (t Square) Area() float64 {
        return t.A * t.A
    }
    
    func (t Circle) String() string {
        return fmt.Sprint("Circle (Radius: ", t.Radius, ")")
    }
    func (t Triangle) String() string {
        return fmt.Sprint("Triangle (Sides: ", t.A, ", ", t.B, ", ", t.C, ")")
    }
    func (t Rectangle) String() string {
        return fmt.Sprint("Rectangle (Sides: ", t.A, ", ", t.B, ")")
    }
    func (t Square) String() string {
        return fmt.Sprint("Square (Sides: ", t.A, ")")
    }
    
    func (t Triangle) Angles() []float64 {
        return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
    }
    func angle(a, b, c float64) float64 {
        return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
    }
    

    Also see:

    Type assertions

    For an expression x of interface type and a type T, the primary expression

    x.(T)  
    

    asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion.

    More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T.

    If the type assertion holds, the value of the expression is the value stored in x and its type is T. If the type assertion is false, a run-time panic occurs. In other words, even though the dynamic type of x is known only at run time, the type of x.(T) is known to be T in a correct program.

    var x interface{} = 7  // x has dynamic type int and value 7
    i := x.(int)           // i has type int and value 7
    
    type I interface { m() }
    var y I
    s := y.(string)        // illegal: string does not implement I (missing method m)
    r := y.(io.Reader)     // r has type io.Reader and y must implement both I and io.Reader
    

    A type assertion used in an assignment or initialization of the special form

    v, ok = x.(T)
    v, ok := x.(T)
    var v, ok = x.(T)
    

    yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.

    Edit:
    Question: What does the assertion x.(T) returns when T is an interface{} and not a concrete type?
    Answer:

    asserts that x is not nil and that the value stored in x is of type T.

    e.g. this panics (compile:Success, Run: panic: interface conversion: interface is nil, not interface {}):

    package main
    
    func main() {
        var i interface{} // nil
        var _ = i.(interface{})
    }
    

    And this works (Run: OK):

    package main
    
    import "fmt"
    
    func main() {
        var i interface{} // nil
        b, ok := i.(interface{})
        fmt.Println(b, ok) // <nil> false
    
        i = 2
        c, ok := i.(interface{})
        fmt.Println(c, ok) // 2 true
    
        //var j int = c // cannot use c (type interface {}) as type int in assignment: need type assertion
        //fmt.Println(j)
    }
    

    output:

    <nil> false
    2 true
    

    Note here: c is of type interface {} and not int.

    See this working sample code with commented outputs:

    package main
    
    import "fmt"
    
    func main() {
        const fm = "'%T'\t'%#[1]v'\t'%[1]v'\t%v\n"
        var i interface{}
        b, ok := i.(interface{})
        fmt.Printf(fm, b, ok) // '<nil>'    '<nil>' '<nil>' false
    
        i = 2
        b, ok = i.(interface{})
        fmt.Printf(fm, b, ok) // 'int'  '2' '2' true
    
        i = "Hi"
        b, ok = i.(interface{})
        fmt.Printf(fm, b, ok) // 'string'   '"Hi"'  'Hi'    true
    
        i = new(interface{})
        b, ok = i.(interface{})
        fmt.Printf(fm, b, ok) // '*interface {}'    '(*interface {})(0xc042004330)' '0xc042004330'  true
    
        i = struct{}{}
        b, ok = i.(interface{})
        fmt.Printf(fm, b, ok) // 'struct {}'    'struct {}{}'   '{}'    true
    
        i = fmt.Println
        b, ok = i.(interface{})
        fmt.Printf(fm, b, ok) // 'func(...interface {}) (int, error)'   '(func(...interface {}) (int, error))(0x456740)'    '0x456740'  true
    
        i = Shape.Area
        b, ok = i.(interface{})
        fmt.Printf(fm, b, ok) // 'func(main.Shape) float64' '(func(main.Shape) float64)(0x401910)'  '0x401910'  true
    }
    
    type Shape interface {
        Area() float64
    }
    

    这篇关于在Go中解释类型断言的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆