耶耶耶耶耶 发表于 2024-7-23 08:48:23

golang 基础 泛型编程

(一) 示例1
package _case

import "fmt"

// 定义用户类型的结构体
type user struct {
        ID   int64
        Name string
        Ageuint8
}

// 定义地址类型的结构体
type address struct {
        ID       int
        Province string
        City   string
}

// 集合转列表函数,接受一个 map,返回一个切片
func mapToList(mp mapT) []T {
        // 创建切片,长度与 map 的长度相同
        list := make([]T, len(mp))
        var i int
        for _, data := range mp {
                list = data
                i++
        }
        return list
}

// 打印通道内容的函数,接受一个通道并打印通道中的每一个数据
func myPrintln(ch chan T) {
        for data := range ch {
                fmt.Println(data)
        }
}

// 主函数,执行类型转换和打印
func TTypeCase() {
        // 创建一个用户类型的 map
        userMp := make(mapuser, 0)
        // 向 map 中添加用户数据
        userMp = user{ID: 1, Name: "heheda", Age: 18}
        userMp = user{ID: 2, Name: "Jerry", Age: 20}
        userMp = user{ID: 3, Name: "Tom", Age: 22}
        // 将用户 map 转换为用户列表
        userList := mapToList(userMp)

        // 创建用户类型的通道,并启动 goroutine 打印通道数据
        ch := make(chan user)
        go myPrintln(ch)
        // 将用户列表中的数据发送到通道
        for _, u := range userList {
                ch <- u
        }
        close(ch)

        // 创建一个地址类型的 map
        addrMp := make(mapaddress, 0)
        // 向 map 中添加地址数据
        addrMp = address{ID: 1, Province: "湖南", City: "长沙"}
        addrMp = address{ID: 2, Province: "广东", City: "揭阳"}
        // 将地址 map 转换为地址列表
        addrList := mapToList(addrMp)

        // 创建地址类型的通道,并启动 goroutine 打印通道数据
        ch1 := make(chan address)
        go myPrintln(ch1)
        // 将地址列表中的数据发送到通道
        for _, addr := range addrList {
                ch1 <- addr
        }
        close(ch1)
}

// 泛型切片的定义
type List []T

// 泛型 map 的定义
// 声明两个泛型,分别为 k 、 v
type MapT mapv

// 泛型通道的定义
type Chan chan T

func TTypeCase1() {
        // mapuser -> MapT
        userMp := make(MapT, 0)
        userMp = user{ID: 1, Name: "heheda", Age: 18}
        userMp = user{ID: 2, Name: "Jerry", Age: 20}
        userMp = user{ID: 3, Name: "Tom", Age: 22}

        var userList List
        userList = mapToList(userMp)

        ch := make(Chan)
        go myPrintln(ch)
        for _, u := range userList {
                ch <- u
        }
        close(ch)

        // mapaddress -> MapT
        addrMp := make(MapT, 0)
        addrMp = address{ID: 1, Province: "湖南", City: "长沙"}
        addrMp = address{ID: 2, Province: "广东", City: "揭阳"}

        var addrList List
        addrList = mapToList(addrMp)

        ch1 := make(Chan)
        go myPrintln(ch1)
        for _, addr := range addrList {
                ch1 <- addr
        }
        close(ch1)
}
https://i-blog.csdnimg.cn/direct/0c145988d82440f289d8907cbb51b8c7.png
这段代码展示了如何使用 Go 语言中的泛型、结构体和通道举行数据处理和打印。以下是对代码各部分的解释:
1.包和导入
package _case

import "fmt"

[*] 导入 fmt 包用于格式化输入输出
 2.结构体定义


[*]user 结构体 有三个字段:ID(范例 int64)、Name(范例 string)和 Age(范例 uint8)
[*]address 结构体 有三个字段:ID(范例 int)、Province(范例 string)和 City(范例 string)
 3.泛型函数
func mapToList(mp mapT) []T {
    list := make([]T, len(mp))
    var i int
    for _, data := range mp {
      list = data
      i++
    }
    return list
}

[*]mapToList 是一个泛型函数,它担当一个 map 范例的参数 mp,并将其转换为切片(列表)
[*]这里使用了两个泛型范例参数:k(可比较范例)和 T(恣意范例)
4.打印通道内容的泛型函数
func myPrintln(ch chan T) {
    for data := range ch {
      fmt.Println(data)
    }
}

[*]myPrintln 是一个泛型函数,担当一个通道范例的参数 ch,并打印通道中的每一个数据
5.主函数 TTypeCase
func TTypeCase() {
    userMp := make(mapuser, 0)
    userMp = user{ID: 1, Name: "heheda", Age: 18}
    userMp = user{ID: 2, Name: "Jerry", Age: 20}
    userMp = user{ID: 3, Name: "Tom", Age: 22}
    userList := mapToList(userMp)
   
    ch := make(chan user)
    go myPrintln(ch)
    for _, u := range userList {
      ch <- u
    }
    close(ch)
   
    addrMp := make(mapaddress, 0)
    addrMp = address{ID: 1, Province: "湖南", City: "长沙"}
    addrMp = address{ID: 2, Province: "广东", City: "揭阳"}
    addrList := mapToList(addrMp)
   
    ch1 := make(chan address)
    go myPrintln(ch1)
    for _, addr := range addrList {
      ch1 <- addr
    }
    close(ch1)
}

[*]这个函数首先创建两个 map 分别存储用户和地址数据
[*]使用 mapToList 将 map 转换为列表(切片)
[*]创建通道并启动 goroutine 打印通道数据
[*]将列表中的数据写入通道并关闭通道
 6.泛型范例


[*]定义三个新的泛型范例:List、MapT 和 Chan
type List []T
type MapT mapv
type Chan chan T 7.另一主函数 TTypeCase1
func TTypeCase1() {
    // mapuser -> MapT
    userMp := make(MapT, 0)
    userMp = user{ID: 1, Name: "heheda", Age: 18}
    userMp = user{ID: 2, Name: "Jerry", Age: 20}
    userMp = user{ID: 3, Name: "Tom", Age: 22}
   
    var userList List
    userList = mapToList(userMp)
   
    ch := make(Chan)
    go myPrintln(ch)
    for _, u := range userList {
      ch <- u
    }
    close(ch)
   
    // mapaddress -> MapT
    addrMp := make(MapT, 0)
    addrMp = address{ID: 1, Province: "湖南", City: "长沙"}
    addrMp = address{ID: 2, Province: "广东", City: "揭阳"}
   
    var addrList List
    addrList = mapToList(addrMp)
   
    ch1 := make(Chan)
    go myPrintln(ch1)
    for _, addr := range addrList {
      ch1 <- addr
    }
    close(ch1)
}

[*]TTypeCase1 函数与 TTypeCase 类似,但使用了自定义的泛型范例 MapT 和 List
相关知识点



[*]泛型 泛型允许函数和数据结构定义中使用范例参数,从而提拔代码的复用性
[*]结构体 结构体是 Go 中用于将多个字段组合成一个单一范例的数据结构
[*]通道(Channel)通道是在 goroutine 之间通报数据的管道,可以同步或者异步
[*]goroutine goroutine 是 Go 中轻量级的线程,用于并发编程
[*]map map 是一种内建的数据结构,用于存储键值对
可以实验运行和修改代码,进一步理解这些概念。
(二) 示例2
package _case

import "fmt"// 定义接口 ToString,有一个 String() 方法type ToString interface {        String() string}// user 结构体实现 ToString 接口func (u user) String() string {        return fmt.Sprintf("ID: %d,Name: %s,Age: %d", u.ID, u.Name, u.Age)}// address 结构体实现 ToString 接口func (addr address) String() string {        return fmt.Sprintf("ID: %d,Province: %s,City: %s", addr.ID, addr.Province, addr.City)}// 定义泛型接口 GetKey,要求实现 Get() 方法返回范例 Ttype GetKey interface {
        any        Get() T}// user 结构体实现 GetKey 接口,Get() 返回 IDfunc (u user) Get() int64 {        return u.ID}// address 结构体实现 GetKey 接口,Get() 返回 IDfunc (addr address) Get() int {        return addr.ID}// 泛型函数 listToMap,将列表转换为 mapfunc listToMap](list []T) mapT {        mp := make(mapT, len(list)) // 创建 map,长度为列表长度        for _, data := range list {                mp = data // 使用 Get() 方法获取键        }        return mp}// 主函数,演示列表转 map 的操纵func InterfaceCase() {        // 创建 user 列表,元素实现了 GetKey 接口        userList := []GetKey{                user{ID: 1, Name: "张三", Age: 18},                user{ID: 2, Name: "李四", Age: 19},        }        // 创建 address 列表,元素实现了 GetKey 接口        addrList := []GetKey{                address{ID: 1, Province: "广东", City: "揭阳"},                address{ID: 2, Province: "湖南", City: "长沙"},        }        // 将 user 列表转换为 map,并打印效果        userMp := listToMap](userList)        fmt.Println(userMp)        // 将 address 列表转换为 map,并打印效果        addrMp := listToMap](addrList)        fmt.Println(addrMp)} https://i-blog.csdnimg.cn/direct/ca1f7bb9655e478bbc199cb0df6bba31.png
 1.包和导入
package _case

import "fmt"  导入 fmt 包用于格式化输入输出。
2.定义基本接口
type ToString interface {
    String() string
} 3.实现 ToString 接口
func (u user) String() string {
    return fmt.Sprintf("ID: %d,Name: %s,Age: %d", u.ID, u.Name, u.Age)
}

func (addr address) String() string {
    return fmt.Sprintf("ID: %d,Province: %s,City: %s", addr.ID, addr.Province, addr.City)
}

[*]user 和 address 结构体实现了 ToString 接口的 String 方法,返回结构体的字符串体现。
 4.定义泛型接口
type GetKey interface {

    any
    Get() T
} 这是一个泛型接口声明。具体来说,GetKey 接口担当一个范例参数 T,这个范例参数必须是一个可比较的范例 (comparable)
(1)定义泛型接口
type GetKey interface {


[*]GetKey 定义了一个泛型接口。
[*]T 是一个范例参数,它必须是一个可比较的范例 (comparable)。可比较范例体现可以使用 == 和 != 运算符举行比较,常见的可比较范例包括整数、浮点数、字符串以及指针等。
(2)嵌入 any 接口


[*]Go 1.18 引入了范例聚集 any,它是 interface{} 的别名,体现恣意范例
[*]在接口中嵌入 any 意味着该接口可以担当任何范例的实现
(3)定义方法
Get() T

[*]定义了一个方法 Get,这个方法返回范例 T。
[*]因为 T 是一个范例参数,所以 Get 方法的返回值范例是泛型的,可以是恣意 T 范例
5.实现 GetKey 接口
func (u user) Get() int64 {
    return u.ID
}

func (addr address) Get() int {
    return addr.ID
}

[*]user 和 address 结构体实现了 GetKey 接口的 Get 方法,分别返回结构体的 ID 字段
[*]user 实现了 GetKey 接口,而 address 实现了 GetKey 接口
 6.列表转聚集函数
func listToMap](list []T) mapT {
    mp := make(MapT, len(list))
    for _, data := range list {
      mp = data
    }
    return mp
}

[*]listToMap 是一个泛型函数,将 list 转换为 map
[*]函数参数 k 为键的范例,T 为实现 GetKey 接口的范例
[*]data.Get() 返回键,作为 map 的键
 7.主函数 InterfaceCase
func InterfaceCase() {
    userList := []GetKey{
      user{ID: 1, Name: "张三", Age: 18},
      user{ID: 2, Name: "李四", Age: 19},
    }
    addrList := []GetKey{
      address{ID: 1, Province: "广东", City: "揭阳"},
      address{ID: 2, Province: "湖南", City: "长沙"},
    }
    userMp := listToMap](userList)
    fmt.Println(userMp)
    addrMp := listToMap](addrList)
    fmt.Println(addrMp)
}

[*]创建 userList 和 addrList,分别包罗 user 和 address 结构体
[*]调用 listToMap 将列表转换为 map,并打印效果
>>分解解读
(1)创建 user 结构体实例
user{ID: 1, Name: "张三", Age: 18},
user{ID: 2, Name: "李四", Age: 19},

[*]user{ID: 1, Name: "张三", Age: 18} 创建一个 user 实例,ID 为 1,名字为 "张三",年龄为 18。
[*]user{ID: 2, Name: "李四", Age: 19} 创建一个 user 实例,ID 为 2,名字为 "李四",年龄为 19。
 (2)实现 GetKey 接口


[*]上述 user 结构体实现了 GetKey 接口,因为 user 结构体定义了 Get() 方法,而且返回值范例为 int64
 (3)创建并初始化切片
userList := []GetKey{
    user{ID: 1, Name: "张三", Age: 18},
    user{ID: 2, Name: "李四", Age: 19},
}

[*]使用两个 user 实例来初始化一个切片 (slice)。
[*]该切片的范例是 []GetKey,体现这个切片包罗实现了 GetKey 接口的元素。
(4)作用


[*]创建并初始化一个包罗多个 user 实例的切片,让这个切片可以作为 GetKey 接口的实现来通报和使用。
[*]这样的设计允许切片中的每个 user 实例都具备 Get 方法,从而可以在后续的泛型函数 listToMap 中使用这些 Get 方法来获取唯一键。
总结



[*]GetKey 是一个泛型接口,带有范例参数 T,该参数必须是可比较范例。
[*]该接口要求实现者提供一个 Get 方法,返回范例为 T。
[*]这是 Go 1.18 引入的泛型特性,允许编写更通用、更范例安全的代码。
相关知识点
接口 


[*]接口定义了方法聚集,恣意范例只要实现了这些方法,就实现了该接口
泛型 


[*]泛型允许定义通用的数据结构和函数,进步代码复用性。Go 1.18 开始引入泛型支持
[*]泛型接口和方法通过范例参数举行约束
结构体


[*]结构体是复合数据范例,将相关数据构造在一起
map


[*]map 在 Go 中用于存储键值对,是内建的数据结构
导入包


[*]fmt 包提供了格式化输入和输出功能,用于打印变量的值
(三)  示例3
package _receiver

import "fmt"

// 定义一个泛型结构体 MyStruct
// 该结构体接受一个类型参数 T,T 只能是 *int 或 *string
type MyStruct struct {
        Name string // 结构体的名字字段
        Data T      // 结构体的数据字段,类型为 T
}

// 定义 MyStruct 结构体的泛型方法接收器
// GetData 返回结构体中的 Data 字段
func (myStruct MyStruct) GetData() T {
        return myStruct.Data
}

// 主函数,演示 MyStruct 结构体和其方法的使用
func ReceiverCase() {
        // 定义一个整型变量,并创建 MyStruct 实例
        data := 18
        myStruct := MyStruct[*int]{
                Name: "heheda",
                Data: &data, // 将整型变量的指针分配给 Data 字段
        }
        // 调用 GetData 方法获取 Data 字段的值并打印
        data1 := myStruct.GetData()
        fmt.Println(*data1) // 解引用指针打印值,输出 18

        // 定义一个字符串变量,并创建 MyStruct 实例
        str := "abcdefg"
        myStruct1 := MyStruct[*string]{
                Name: "heheda",
                Data: &str, // 将字符串变量的指针分配给 Data 字段
        }
        // 调用 GetData 方法获取 Data 字段的值并打印
        str1 := myStruct1.GetData()
        fmt.Println(*str1) // 解引用指针打印值,输出 "abcdefg"
} 这段代码展示了如安在 Go 语言中使用泛型定义结构体和方法接收器,以下是对代码各部分的解释:
1.包和导入
package _receiver

import "fmt" 导入 fmt 包用于格式化输入输出
2.定义泛型结构体
type MyStruct struct {
    Name string
    Data T
}

[*]MyStruct 是一个泛型结构体,结构体内包罗两个字段:Name(范例为 string)和 Data(泛型范例 T)
[*]这里 T 被约束为 *int 或 *string 范例
 3.定义泛型方法接收器
func (myStruct MyStruct) GetData() T {
    return myStruct.Data
}

[*]GetData 是 MyStruct 的方法,这个方法担当一个泛型范例 T,返回 MyStruct 结构体中的 Data 字段
 4.主函数 ReceiverCase
func ReceiverCase() {
    data := 18
    myStruct := MyStruct[*int]{
      Name: "heheda",
      Data: &data,
    }
    data1 := myStruct.GetData()
    fmt.Println(*data1)

    str := "abcdefg"
    myStruct1 := MyStruct[*string]{
      Name: "heheda",
      Data: &str,
    }
    str1 := myStruct1.GetData()
    fmt.Println(*str1)
}

[*]创建一个整型变量 data 并取其指针赋值给 MyStruct 实例 myStruct 的 Data 字段
[*]调用 GetData 方法获取 Data 字段的值并打印
[*]创建一个字符串变量 str 并取其指针赋值给 MyStruct 实例 myStruct1 的 Data 字段
[*]调用 GetData 方法获取 Data 字段的值并打印
相关知识点

(1)泛型


[*]泛型允许定义通用的数据结构和函数,加强代码复用性。Go 1.18 开始引入泛型支持
[*]泛型范例参数通过范例约束举行限制,如本例中的 interface{ *int | *string }
(2)结构体


[*]结构体是一种复合数据范例,可以包罗多个字段,允许将相关的数据构造在一起
(3)方法接收器


[*]方法接收器是指与某个范例(如结构体或接口)关联的方法。在本例中,我们定义了 MyStruct 范例的泛型方法接收器 GetData
(4)指针


[*]指针保存了变量的内存地址,在 Go 中,使用 & 符号获取一个变量的指针,使用 * 符号解引用指针获取指针指向的值
(5)导入包


[*]fmt 包提供了格式化输入和输出功能,用于打印变量的值
可以实验运行和修改代码,进一步理解这些概念。
main.go
package main

import (
        "context"
        _case "gomod/genetic-T/case"
        "os"
        "os/signal"
)

func main() {
        _case.TTypeCase()
        _case.TTypeCase1()
        _case.InterfaceCase()
        _receiver.ReceiverCase()
        ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
        defer stop()
        <-ctx.Done()
}


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: golang 基础 泛型编程