Go 类型断言


原文链接: Go 类型断言

在Golang项目开发中我们常会使用到空接口interface{}与reflect。下面是作者在学习Golang过程中遇到的一些关于Reflect的用法,即使总结并记录。

将interface{T} 转为 interface{*T}:

我们在使用interface{}传递函参时,可能会碰到需要将传递的值类型T转变为指针类型*T,从而使他能够被赋值,可取址。思路: 根据T.type New()一个新的指针Type,并对其赋值。
func ifaceValue2Pointer(v interface{}) interface{}{

pv:=reflect.New(reflect.TypeOf(v))
pv.Elem().Set(reflect.ValueOf(v))
return pv.interface()

//fmt.Println(reflect.ValueOf(pv).Elem().CanAddr()) ----> true
}

将interface{T} 中T转为 []byte 字节流形式:
使用场景:例如使用socket建立长连接时,需要传输struct类型。思路:存储struct的指针地址,Len与Cap,通过unsafe包进行转换。
type SliceMock struct{

addr uintptr
len  int
cap  int

}

func struct2byte(t interface{}) []byte { //在此假设t的实例为值类型

var ts = &t
Len := unsafe.Sizeof(*ts)
p := reflect.New(reflect.TypeOf(t))
p.Elem().Set(reflect.ValueOf(t))
addr := p.Elem().UnsafeAddr()  //取址
tb := &SliceMock{
    addr: addr,
    cap:  int(Len),
    len:  int(Len),
}
return *(*[]byte)(unsafe.Pointer(tb))

}
//...........
func main() {

data := struct2byte(TestStruct{3,100})
var ptestStruct *TestStruct = *(**TestStruct)(unsafe.Pointer(&data))

}

the-way-to-go Go入门指南

	DnsRecords("hangruan.cn", nil, "")

func DnsRecords(domain, typekey, value interface{}) error {
    // 对interface{} 参数做nil判断,否则报panic错误
	if typekey != nil && typekey.(string) != "" {
		args.TypeKeyWord = typekey.(string)
	}
}

获取interface{}中存放的数据类型

interface{} 可以接受任何类型的对象值
获取interface{}队形的数据类型,可以使用断言,或者 switch type 来实现

// Assertion project main.go
package main

import (
    "fmt"
)

type Bag struct {
    Key string
}

type Bag2 struct {
    Key int
}

func main() {
    var b1 interface{}
    var b2 interface{}

    b1 = Bag{Key: "1"}
    b2 = Bag2{Key: 0}
	//获取interface{}中存放的数据类型
	
    //方法一:
    { //判断是否是Bag类型  若不是则置0
        b, ok := b1.(Bag)
        fmt.Println("Bag类型   :", ok, "数据", b)
    }
    { //判断是否是Bag2类型  若不是则置0
        b, ok := b2.(Bag2)
        fmt.Println("Bag2类型:", ok, "数据", b)
	}
	
    //方法二:
    switch v := b1.(type) { //v表示b1 接口转换成Bag对象的值
    case Bag:
        fmt.Println("b1.(type):", "Bag", v)
    case Bag2:
        fmt.Println("b1.(type):", "Bag2", v)
    default:
        fmt.Println("b1.(type):", "other", v)
    }
}

断言:一般使用于已知interface中的对象的数据类型,调用后自动将接口转换成相应的对象,语法结构 接口对象(obj),存放的数据类型(string) ,

v,ok := obj.(string),若是相应的对象ok则为真,v为相应对象及数据。
switch type: 已知或者未知的对象数据类型均可,b1.(type)必须配合switch来使用,不能单独执行此语句。

switch v:= b1.(type){//b1为interface对象 ,v为相应对象及数据
	case Bag: //类型为Bag时执行
		fmt.Println(“b1.(type):”, “Bag”, v)
	case Bag2://类型为Bag2时执行
		fmt.Println(“b1.(type):”, “Bag2”, v)
	default://类型为其他类型时执行
		fmt.Println(“b1.(type):”, “other”, v)
}
````

# 11.3 类型断言:如何检测和转换接口变量的类型

一个接口类型的变量 `varI` 中可以包含任何类型的值,必须有一种方式来检测它的 **动态** 类型,即运行时在变量中存储的值的实际类型。在执行过程中动态类型可能会有所不同,但是它总是可以分配给接口变量本身的类型。通常我们可以使用 **类型断言** 来测试在某个时刻 `varI` 是否包含类型 `T` 的值:

```go
v := varI.(T)       // unchecked type assertion

varI 必须是一个接口变量,否则编译器会报错:invalid type assertion: varI.(T) (non-interface type (type of varI) on left)

类型断言可能是无效的,虽然编译器会尽力检查转换是否有效,但是它不可能预见所有的可能性。如果转换在程序运行时失败会导致错误发生。更安全的方式是使用以下形式来进行类型断言:

if v, ok := varI.(T); ok {  // checked type assertion
    Process(v)
    return
}
// varI is not of type T

如果转换合法,vvarI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,okfalse,也没有运行时错误发生。

应该总是使用上面的方式来进行类型断言

多数情况下,我们可能只是想在 if 中测试一下 ok 的值,此时使用以下的方法会是最方便的:

if _, ok := varI.(T); ok {
    // ...
}

示例 11.4 type_interfaces.go

package main

import (
	"fmt"
	"math"
)

type Square struct {
	side float32
}

type Circle struct {
	radius float32
}

type Shaper interface {
	Area() float32
}

func main() {
	var areaIntf Shaper
	sq1 := new(Square)
	sq1.side = 5

	areaIntf = sq1
	// Is Square the type of areaIntf?
	if t, ok := areaIntf.(*Square); ok {
		fmt.Printf("The type of areaIntf is: %T\n", t)
	}
	if u, ok := areaIntf.(*Circle); ok {
		fmt.Printf("The type of areaIntf is: %T\n", u)
	} else {
		fmt.Println("areaIntf does not contain a variable of type Circle")
	}
}

func (sq *Square) Area() float32 {
	return sq.side * sq.side
}

func (ci *Circle) Area() float32 {
	return ci.radius * ci.radius * math.Pi
}

输出:

The type of areaIntf is: *main.Square
areaIntf does not contain a variable of type Circle

程序中定义了一个新类型 Circle,它也实现了 Shaper 接口。 if t, ok := areaIntf.(*Square); ok 测试 areaIntf 里是否有一个包含 *Square 类型的变量,结果是确定的;然后我们测试它是否包含一个 *Circle 类型的变量,结果是否定的。

备注

如果忽略 areaIntf.(*Square) 中的 * 号,会导致编译错误:impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)

`