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泛型类型转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用 pprof 进行性能分析的方法详解

    使用 pprof 进行性能分析的方法详解

    pprof 是 Go 语言中用于性能分析的一个强大工具,它可以帮助开发人员找到应用程序中的性能瓶颈,并提供详细的分析报告,本文将介绍如何使用 pprof 进行性能分析,需要的朋友可以参考下
    2023-05-05
  • Go中的go.mod使用详解

    Go中的go.mod使用详解

    这篇文章主要介绍了Go中的go.mod使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Go语言执行cmd命令库的方法实现

    Go语言执行cmd命令库的方法实现

    go语言用来执行一个系统的命令相对python来说还是有点复杂的,执行命令是一个非常常见的需求,本文主要介绍了Go语言执行cmd命令库的方法实现,感兴趣的可以了解一下
    2023-09-09
  • Golang 运算符及位运算详解

    Golang 运算符及位运算详解

    这篇文章主要介绍了Golang 运算符及位运算详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang中fsnotify包监听文件变化的原理详解

    Golang中fsnotify包监听文件变化的原理详解

    Golang提供了一个强大的fsnotify包,它能够帮助我们轻松实现文件系统的监控,本文将深入探讨fsnotify包的原理,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-12-12
  • 从go语言中找&和*区别详解

    从go语言中找&和*区别详解

    这篇文章主要介绍了从go语言中找&和*区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • vscode插件设置之Golang开发环境配置全过程

    vscode插件设置之Golang开发环境配置全过程

    go语言开发选择vscode作为IDE工具也是一个不错的选择,下面这篇文章主要给大家介绍了关于vscode插件设置之Golang开发环境配置的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • Golang操作Kafka的实现示例

    Golang操作Kafka的实现示例

    本文主要介绍了Golang操作Kafka的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • goland最新激活办法

    goland最新激活办法

    这篇文章主要介绍了goland最新激活办法,激活方法真的超级简单,只需要把脚本内容复制到对应文件,并运行该脚本就可以了,需要的朋友可以参考下
    2021-12-12
  • golang字符串本质与原理详解

    golang字符串本质与原理详解

    这篇文章主要介绍了golang字符串本质与原理详解,golang中的字符串指的是所有8比特位字节字符串的集合,通常是UTF-8编码的文本,更多相关内容需要的小伙伴可以参考一下
    2022-06-06

最新评论