实际编码中,经常会出现类型转换的情况。

1.C#中的类型转换

C#
int a = 100;
double d = a;  //将int类型转换为double类型
float f = 3.14f;
d = f;    //将float类型转换为double类型

反之

int r ;
double rd=5.0;
r = rd;
Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?)	
doubleint
int r ;
double rd=5.0;
r = (int)rd;
(T)EET
ET

有关支持的显式数值转换的完整列表,请参阅?内置数值转换一文的?显式数值转换部分。

对于引用类型,从基类型转换为派生类型,则必须进行显式强制转换

// 创建派生类
Giraffe g = new Giraffe();

// 隐式转换为基类是安全的
Animal a = g;

// 需要显式转换才能强制把基类转换回派生类型
Giraffe g2 = (Giraffe)a; //如果a不是Giraffe,编译能通过,但在运行时会抛出异常

另外一种特殊的类型转换是?装箱和拆箱,这里就不做介绍了。

2.Go语言中的类型转换

Go语言没有隐式转换,只有显式转换,说白了,任何一种类型转换,都需要开发者进行手动操作。

简单转换

何谓简单转换?就是转换数据类型的方式很简单。

func main() {
 a := 10
 b := float32(a)

 c := 50.5
 d := int32(c)
 e := int64(c)
 fmt.Printf("%v %T\n", a, a)
 fmt.Printf("%v %T\n", b, b)
 fmt.Printf("%v %T\n", c, c)
 fmt.Printf("%v %T\n", d, d)
 fmt.Printf("%v %T\n", e, e)
}
输出
10 int
10 float32
50.5 float64
50 int32
50 int64

注意事项

cannot convert xxx (type string) to type int64strconv

3.strconv包

这一节就归纳一些在实际开发中,strconv包中经常用到的函数:

3.1 Itoa()

Itoa()
func Itoa(i int) string

实际开发中,组合生成redis的key,key值为int类型的id

func main() {
    var userid int
    KeyPostVotedZSetPF := "post:voted:"
    userid = 41654
    key := KeyPostVotedZSetPF + strconv.Itoa(userid)
    println(key)
}
输出
post:voted:41654

3.2 FormatInt()

Itoa()intint64Itoa
func FormatInt(i int64, base int) string
func main() { 
    var userid int64
    KeyPostVotedZSetPF := "post:voted:"
    userid = 41654489498
    key := KeyPostVotedZSetPF + strconv.FormatInt(userid, 10)
    println(key)
}
输出
post:voted:41654489498

Format系列函数

Format其实是有一系列函数,用于实现了将给定类型数据格式化为string类型数据的功能。

FormatBool()
func FormatBool(b bool) string
FormatInt()
func FormatInt(i int64, base int) string
FormatUint()
func FormatUint(i uint64, base int) string

是FormatInt的无符号整型版本。

FormatFloat()
func FormatFloat(f float64, fmt byte, prec, bitSize int) string

函数将浮点数表示为字符串并返回。

bitSize表示f的来源类型(32:float32、64:float64),会据此进行舍入。

fmt表示格式:’f’(-ddd.dddd)、’b’(-ddddp±ddd,指数为二进制)、’e’(-d.dddde±dd,十进制指数)、’E’(-d.ddddE±dd,十进制指数)、’g’(指数很大时用’e’格式,否则’f’格式)、’G’(指数很大时用’E’格式,否则’f’格式)。

prec控制精度(排除指数部分):对’f’、’e’、’E’,它表示小数点后的数字个数;对’g’、’G’,它控制总的数字个数。如果prec 为-1,则代表使用最少数量的、但又必需的数字来表示f。

3.3 Parse系列函数

有了Format系列函数把一些类型转换为string类型,那么反过来Parse系列函数就是用于将字符串类型转换为给定类型的值

ParseBool()
func ParseBool(str string) (value bool, err error)

返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。

ParseInt()
func ParseInt(s string, base int, bitSize int) (i int64, err error)

返回字符串表示的整数值,接受正负号。

base指定进制(2到36),如果base为0,则会从字符串前置判断,”0x”是16进制,”0”是8进制,否则是10进制;

bitSize指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64;

返回的err是*NumErr类型的,如果语法有误,err.Error = ErrSyntax;如果结果超出类型范围err.Error = ErrRange。

ParseUnit()
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
ParseUintParseInt
ParseFloat()
func ParseFloat(s string, bitSize int) (f float64, err error)

3.4 Atoi()

Atoi()
func Atoi(s string) (i int, err error)

如果传入的字符串参数无法转换为int类型,就会返回错误。

s1 := "100"
i1, err := strconv.Atoi(s1)
if err != nil {
 fmt.Println("can't convert to int")
} else {
 fmt.Printf("type:%T value:%#v\n", i1, i1) //type:int value:100
}
FormatParseAppend系列函数Format

4.接口类型断言

Go语言的空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?

在接口值上的操作,用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型

value, ok := x.(T)

x 表示一个接口类型的值(包括空接口),T 表示一个具体的类型(也可为接口类型)。

当变量是一个接口时,相比较具体类型时:

  • 具体类型→动态类型
  • 具体类型的值→动态值
func main() {
 var x interface{}
 x = "I'm Garfield"
 v, ok := x.(string)
 if ok {
  fmt.Println(v)
 } else {
  fmt.Println("Asserts Failed")
 }
}
ifswitchxtypetypeswitch casexcasecasei := x.(type)
func justifyType(x interface{}) {
 switch i := x.(type) {
 case int64:
  fmt.Printf("x is a int64, is %v\n", i)
 case string:
  fmt.Printf("x is a string,value is %v\n", i)
 case int:
  fmt.Printf("x is a int is %v\n", i)
 case bool, int32:
  fmt.Printf("x is a bool or int32 is %v\n", i)
 case nil:
  fmt.Printf("x is a interface{} is %v\n", i)
 default:
  fmt.Println("unsupport type!")
}

func main() {
    justifyType(nil)
    justifyType("I'm Garfield")
    justifyType(44)
    justifyType(int64(516165161616))
    justifyType(true)
    justifyType(int32(1105020))
}

5.接口类型检测

除此之外,开发者还可以像C#那样把实现了接口的实例赋值给接口变量,前面博文中介绍过利用编译器和匿名变量,判断结构体是否实现了接口,实质也就是利用这种方式做一个接口类型检测:

var _ IRouter = &Engine{}

类似的还有:

var _ IRouter = (*Engine)(nil)

nil的类型是 nil,地址值为0x0,利用强制类型转换成了 *Engine ,返回的变量就是类型为 *Engine 地址值为0x0,如果 *Engine 没有实现了 IRouter 接口,就会在编译时报错,上面两个看似不同的代码,其本质是一样的,目的也一致:实现在编译期间检测接口是否实现

参考链接

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/types/casting-and-type-conversions

https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/type-testing-and-cast#cast-expression

------------------- End -------------------