Golang泛型实现类型转换的方法实例

 更新时间:2022年12月30日 11:45:25   作者:恋喵大鲤鱼  
将一个值从一种类型转换到另一种类型,便发生了类型转换,下面这篇文章主要给大家介绍了关于Golang泛型实现类型转换的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

1.前言

Golang 标准库提供了很多类型转换的函数,如 strconv 包可完成 string 与基本数据类型之间的转换。

比如将 int 与 string 之间的互转。

// int to string
s := strconv.Itoa(i)

// string to int
i, err := strconv.ParseInt(i, 0, 64)

如果我们想完成任意类型到某一具体类型的转换,该如何实现呢?

2.To String

以 string 为,我们可以这样实现。

// ToStringE casts any type to a string type.
func ToStringE(i any) (string, error) {
	i = indirectToStringerOrError(i)

	switch s := i.(type) {
	case string:
		return s, nil
	case bool:
		return strconv.FormatBool(s), nil
	case float64:
		return strconv.FormatFloat(s, 'f', -1, 64), nil
	case float32:
		return strconv.FormatFloat(float64(s), 'f', -1, 32), nil
	case int:
		return strconv.Itoa(s), nil
	case int64:
		return strconv.FormatInt(s, 10), nil
	case int32:
		return strconv.Itoa(int(s)), nil
	case int16:
		return strconv.FormatInt(int64(s), 10), nil
	case int8:
		return strconv.FormatInt(int64(s), 10), nil
	case uint:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint64:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint32:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint16:
		return strconv.FormatUint(uint64(s), 10), nil
	case uint8:
		return strconv.FormatUint(uint64(s), 10), nil
	case json.Number:
		return s.String(), nil
	case []byte:
		return string(s), nil
	case template.HTML:
		return string(s), nil
	case template.URL:
		return string(s), nil
	case template.JS:
		return string(s), nil
	case template.CSS:
		return string(s), nil
	case template.HTMLAttr:
		return string(s), nil
	case nil:
		return "", nil
	case fmt.Stringer:
		return s.String(), nil
	case error:
		return s.Error(), nil
	default:
		return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i)
	}
}

其中 indirectToStringerOrError 是对指针类型的解引用,从标准库 html/template/content.go 获取。

var (
	errorType       = reflect.TypeOf((*error)(nil)).Elem()
	fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
)

// Copied from html/template/content.go.
// indirectToStringerOrError returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
// or error,
func indirectToStringerOrError(a any) any {
	if a == nil {
		return nil
	}
	v := reflect.ValueOf(a)
	for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Pointer && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}

3.To Other Type

那么对其他类型我们也都要实现对应的转换函数。

// ToBoolE casts any type to a bool type.
func ToBoolE(i any) (bool, error) {
	i = indirect(i)

	switch b := i.(type) {
	case bool:
		return b, nil
	case nil:
		return false, nil
	case int:
		if i.(int) != 0 {
			return true, nil
		}
		return false, nil
	case string:
		return strconv.ParseBool(i.(string))
	default:
		return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
	}
}

// ToIntE, ToInt8E, ToInt16E...

3.泛型

最终,我们可以通过泛型完成对上面多个具体类型转换函数的封装。这样我们只需要调用一个函数,便可完成对所有类型的转换。

// ToAnyE converts one type to another and returns an error if occurred.
func ToAnyE[T any](a any) (T, error) {
	var t T
	switch any(t).(type) {
	case bool:
		v, err := ToBoolE(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case int:
		v, err := ToIntE(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case int8:
		v, err := ToInt8E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case int16:
		v, err := ToInt16E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case int32:
		v, err := ToInt32E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case int64:
		v, err := ToInt64E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case uint:
		v, err := ToUintE(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case uint8:
		v, err := ToUint8E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case uint16:
		v, err := ToUint16E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case uint32:
		v, err := ToUint32E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case uint64:
		v, err := ToUint64E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case float32:
		v, err := ToFloat32E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case float64:
		v, err := ToFloat64E(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	case string:
		v, err := ToStringE(a)
		if err != nil {
			return t, err
		}
		t = any(v).(T)
	default:
		return t, fmt.Errorf("the type %T is not supported", t)
	}
	return t, nil
}

如果不关心错误,可以再封装一下。

// ToAny converts one type to another type.
func ToAny[T any](a any) T {
	v, _ := ToAnyE[T](a)
	return v
}

4.使用示例

package main

import (
	"fmt"
)

func main() {
	fmt.Println(ToAny[string](1))    // "1"
	fmt.Println(ToAny[string](true)) // "true"
	fmt.Println(ToAny[string](1.1))  // "1.1"

	fmt.Println(ToAny[int]("1"))   	// 1
	fmt.Println(ToAny[int]("1.0")) 	// 1
	fmt.Println(ToAny[int](true))  	// 1

	fmt.Println(ToAny[bool]("true"))  // true
	fmt.Println(ToAny[bool]("false")) // false
	fmt.Println(ToAny[bool]("True"))  // true
	fmt.Println(ToAny[bool]("False")) // false
	fmt.Println(ToAny[bool](1))       // true
	fmt.Println(ToAny[bool](0))       // false
	fmt.Println(ToAny[bool](nil))     // false
}

5.go-huge-util

为了方便大家使用,以上相关代码已开源至 Github 工具库 go-huge-util,大家可使用 go mod 方式 import 然后使用。

import "github.com/dablelv/go-huge-util/conv"

conv.ToAny[string](1)    // "1"
conv.ToAny[string](true) // "true"
conv.ToAny[string](1.1)  // "1.1"

conv.ToAny[int]("1")   // 1
conv.ToAny[int]("1.0") // 1
conv.ToAny[int](true)  // 1

conv.ToAny[bool]("true")  // true
conv.ToAny[bool]("false") // false
conv.ToAny[bool]("True")  // true
conv.ToAny[bool]("False") // false
conv.ToAny[bool](1)       // true
conv.ToAny[bool](0)       // false
conv.ToAny[bool](nil)     // false

go-huge-util 除了类型转换,还有很多其他实用函数,如加解密、zip 等,欢迎大家使用、Star 和 Pull Request。

参考文献

github.com/dablelv/go-huge-util

总结

到此这篇关于Golang泛型实现类型转换的文章就介绍到这了,更多相关Golang泛型类型转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言读写锁RWMutex的源码分析

    Go语言读写锁RWMutex的源码分析

    本篇文章我们将一起来学习下Go语言中的读写锁sync.RWMutex。文中的示例讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-10-10
  • Golang中println和fmt.Println区别解析

    Golang中println和fmt.Println区别解析

    Golang 中打印数据通常使用 fmt.Println() 方法,也可以使用内置的 println() 方法。这两个方法大家可能都使用过,它们的区别是什么呢?本文给大家详细讲解,感兴趣的朋友跟随小编一起看看吧
    2023-03-03
  • golang中为什么不存在三元运算符详解

    golang中为什么不存在三元运算符详解

    这篇文章主要给大家介绍了关于golang中为什么不存在三元运算符的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 一文详解在Go中如何使用Viper来管理配置

    一文详解在Go中如何使用Viper来管理配置

    Viper 是一个功能齐全的 Go 应用程序配置库,支持很多场景。在本文中,我们将深入探讨 Viper 的各种用法和使用场景,以帮助读者更好地了解和使用 Viper 来管理应用程序配置,感兴趣的同学可以参考阅读
    2023-05-05
  • go语言的sql包原理与用法分析

    go语言的sql包原理与用法分析

    这篇文章主要介绍了go语言的sql包原理与用法,较为详细的分析了Go语言里sql包的结构、相关函数与使用方法,需要的朋友可以参考下
    2016-07-07
  • Golang Defer作用域及执行顺序使用案例

    Golang Defer作用域及执行顺序使用案例

    这篇文章主要为大家介绍了Golang Defer作用域及执行顺序使用案例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • go语言实现http服务端与客户端的例子

    go语言实现http服务端与客户端的例子

    今天小编就为大家分享一篇go语言实现http服务端与客户端的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • Golang监听日志文件并发送到kafka中

    Golang监听日志文件并发送到kafka中

    这篇文章主要介绍了Golang监听日志文件并发送到kafka中,日志收集项目的准备中,本文主要讲的是利用golang的tail库,监听日志文件的变动,将日志信息发送到kafka中 ,需要的朋友可以参考一下
    2022-04-04
  • 详解Go语言中调度器的原理与使用

    详解Go语言中调度器的原理与使用

    这篇文章主要介绍了Go语言运行时调度器的实现原理,其中包含调度器的设计与实现原理、演变过程以及与运行时调度相关的数据结构,希望对大家有所帮助
    2023-07-07
  • Golang连接Redis数据库的方法

    Golang连接Redis数据库的方法

    这篇文章主要介绍了Golang连接Redis数据库的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12

最新评论