go泛型实现方式遐想

anko · 2020-04-08 15:53:03 · 2557 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2020-04-08 15:53:03 的主题,其中的信息可能已经有所发展或是发生改变。

为了绕开反射的慢性能和go无泛型以及接口断言的慢性能,下面提供一种方式给大伙,我想这会开拓大伙的一些思维,转发需注明原著连接,谢谢!

主要是利用了组合的原理来实现的,下面直接给出代码,欢迎大伙评论。


package main

import "fmt"

type Person struct {
    name string
    age  int
}
type Animal struct {
    name string
    age  int
}

//如果无泛型,我们需要下面这些累赘
//func (p Person) getName() string {
//    return p.name
//}
//
//func (p Person) getAge() int {
//    return p.age
//}
//
//func (a Animal) getName() string {
//    return a.name
//}
//
//func (a Animal) getAge() int {
//    return a.age
//}

//下面是泛型,我们注释掉上面的几个方法
type Biological struct {
    _type string
    *Person
    *Animal
}
func (b Biological) getName() (name string) {
    switch b._type {
    //针对不同的类型做不同的事情
    case "Person":
        name= b.Person.name
    case "Animal":
        name= b.Animal.name
    }
    return
}

func (b Biological) getAge() (age int) {
    switch b._type {
    //针对不同的类型做不同的事情
    case "Person":
        age= b.Person.age
    case "Animal":
        age= b.Animal.age
    }
    return
}

//下面是接收泛型的处理函数
func testFun(b *Biological) {
    fmt.Println(b.getName())
    fmt.Println(b.getAge())
}

//下面是将具体的类型转成泛型类型实例的函数
func newGeneric(s string,p *Person,a *Animal) *Biological  {
    return &Biological{
        _type:  s,
        Person: p,
        Animal: a,
    }
}

func main() {
    var p = Person{
        name: "ppp",
        age:  10,
    }
    testFun(newGeneric("Person",&p,nil))

    fmt.Println("--------------")

    var a = Animal{
        name: "AAA",
        age:  20,
    }
    testFun(newGeneric("Animal",nil,&a))

}

下面给出代码截图方便查看:

image.png image.png image.png

这个应该是比反射的性能要好很多,特别是在并发函数里面,我们应该尽量避免使用反射或者 go库中使用到反射的函数!

为了再说明组合的好处,我们再举个例子如下:

package main

import "fmt"

type T struct {
    *string
    *int
}
//如果不建立泛型则需要像下面这样写,挺累赘的写法!
//func return_string(s string) string {
//    return s
//}
//func return_int(i int) int {
//    return i
//}

func echo(arg T) T {
    return arg
}

func doubleStrOrInt(arg T) T {

    switch arg.int {
    case nil:
        //为string
        s := *arg.string
        s += s
        arg.string = &s
    default:
        //为int
        i := (*arg.int) * 2
        arg.int = &i
    }
    return arg
}

func main() {
    s := "a"//基本类型
    arg := T{string: &s}//泛型
    str := echo(arg).string//调用接收泛型的函数且回去结果
    fmt.Println(*str)

    ii := 1
    arg1 := T{int: &ii}
    i := echo(arg1).int
    fmt.Println(*i)

    //------------
    str = doubleStrOrInt(arg).string
    fmt.Println(*str)

    i = doubleStrOrInt(arg1).int
    fmt.Println(*i)

}

代码截图如下:

image.png image.png

为了让读者意识到性能问题,我给出下面的测试:

//代码1
package main

import (
    "fmt"
    "sync"
    "time"
)

func doubleStrOrInt(arg interface{}) interface{} {

    switch s := arg.(type) {
    case string:
        //为string
        s += s
        arg = s
    case int:
        //为int
        i := s * 2
        arg = i
    }
    return arg
}

func main() {
    start := time.Now()

    s := "a" //基本类型
    ii := 1

    var wg sync.WaitGroup
    wg.Add(1)

    go func() {
        defer wg.Done()
        //故意循环100000次
        for k := 0; k < 100000; k++ {
            _ = doubleStrOrInt(s)
            //fmt.Println(str)

            _ = doubleStrOrInt(ii)
            //fmt.Println(i)
        }

    }()

    wg.Wait()
    end := time.Now()
    fmt.Println("耗时:", end.Sub(start))

}

截图:

image.png

//代码2

package main

import (
    "fmt"
    "sync"
    "time"
)

type T struct {
    *string
    *int
}

func doubleStrOrInt(arg T) T {

    switch arg.int {
    case nil:
        //为string
        s := *arg.string
        s += s
        arg.string = &s
    default:
        //为int
        i := (*arg.int) * 2
        arg.int = &i
    }
    return arg
}

func main() {
    start := time.Now()

    s := "a"             //基本类型
    arg := T{string: &s} //泛型

    ii := 1
    arg1 := T{int: &ii}

    var wg sync.WaitGroup
    wg.Add(1)

    go func() {
        defer wg.Done()
        //故意循环100000次
        for k := 0; k < 100000; k++ {
            _ = doubleStrOrInt(arg).string
            //fmt.Println(*str)

            _ = doubleStrOrInt(arg1).int
            //fmt.Println(*i)
        }

    }()

    wg.Wait()
    end := time.Now()
    fmt.Println("耗时:", end.Sub(start))

}

代码截图:

image.png image.png

下面是上面2段代码的执行结果:

采用反射与断言的结果是:

耗时: 22.4633ms

而不采用反射,自定义类似泛型的结构体的执行结果则是:

耗时: 6.8349ms


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2557 次点击  
加入收藏 微博
2 回复  |  直到 2020-04-08 19:02:16
stirlingx
stirlingx · #1 · 5年之前

这个还不如interface呢

anko
anko · #2 · 5年之前
stirlingxstirlingx #1 回复

这个还不如interface呢

折中一下,没法,性能的话肯定比定义接口高,你测试下就知道了,代码偏复杂点也是无奈之举,但是,我的目的是提高代码性能,抛开代码复杂这个缺点来说,这种方式确实值得参考

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传