李迟2022年10月工作生活总结

·  阅读 11

本文为 2022 年 10 月工作生活总结。

研发编码

Go

判断interface{}是否为空

判断interface{}类型是否为空,不能直接使用==nil来判断(用之无效),需先用reflect.ValueOf判断为指针类型,再使用IsNil函数判断,如下:

func IsInterfaceNil(i interface{}) bool {
    vi := reflect.ValueOf(i)
    if vi.Kind() == reflect.Ptr {
        return vi.IsNil()
    }
    return false
}
复制代码

结构体字段对比

最近进行的一个工程,在集成测试中需要对比请求的结果和已知的结果,里面有大量的较多字段的结构体,幸好之前实现了结构体字段对比的接口,但不够完善,为了泛化,参数使用了interface{},再用反射获取字段及类型,而对比时,则要一一判断结构体字段类型,因为不知道有没有更优雅的方法实现,每种类型都要用一个case判断,不太简洁,所以偷懒把int8uint8等几个用到的做判断,这次把常见的基础类型都加了,应该够用了。

func CompareStruct(data, data1 interface{}, point int, skipname ...string) (info string) {
    ret := false
    info = ""
    // 预防为空
    if IsInterfaceNil(data) || IsInterfaceNil(data1) {
        return
    }
​
    var skipNames []string
    for _, item := range skipname {
        skipNames = append(skipNames, item)
    }
​
    check := func(a string, b []string) bool {
        for _, item := range b {
            if strings.EqualFold(item, a) {
                return true
            }
        }
        return false
    }
​
    object := reflect.ValueOf(data)
    myref := object.Elem()
    // fmt.Printf("myref: %v %v\n", data, object)
    //typeOfType := myref.Type()
    for i := 0; i < myref.NumField(); i++ {
        // 如果不需要对比,跳过
        if ok := check(myref.Type().Field(i).Name, skipNames); ok {
            continue
        }
​
        // 浮点数精度
        mypoint := 4
        if point != 0 {
            mypoint = point
        }
        field := myref.Field(i)
        field1 := reflect.ValueOf(data1).Elem().Field(i)
        switch field.Type().Name() {
        case "int8":
            ret = field.Interface().(int8) == field1.Interface().(int8)
        case "uint8":
            ret = field.Interface().(uint8) == field1.Interface().(uint8)
        case "int16":
            ret = field.Interface().(int16) == field1.Interface().(int16)
        case "uint16":
            ret = field.Interface().(uint16) == field1.Interface().(uint16)
        case "int":
            ret = field.Interface().(int) == field1.Interface().(int)
        case "uint":
            ret = field.Interface().(uint) == field1.Interface().(uint)
        case "int32":
            ret = field.Interface().(int32) == field1.Interface().(int32)
        case "uint32":
            ret = field.Interface().(uint32) == field1.Interface().(uint32)
        case "int64":
            ret = field.Interface().(int64) == field1.Interface().(int64)
        case "uint64":
            ret = field.Interface().(uint64) == field1.Interface().(uint64)
        case "string":
            ret = field.Interface().(string) == field1.Interface().(string)
        case "float64":
            ret = IsFloatEqual(field.Interface().(float64), field1.Interface().(float64), mypoint)
        case "float32":
            ret = IsFloatEqual(float64(field.Interface().(float32)), float64(field1.Interface().(float32)), mypoint)
        case "bool":
            ret = field.Interface().(bool) == field1.Interface().(bool)
        default:
            ret = false
        }
        if ret == false {
            info1 := fmt.Sprintf("%s: %v != %v ", myref.Type().Field(i).Name, field.Interface(), field1.Interface())
            info = info + info1
        }
    }
    return info
}
​
测试示例如下:
​
func TestStruct(t *testing.T) {
    object1 := TestObj{
        Name:  "James",
        Value: 128,
        Size:  256,
        Guard: 56.4,
    }
    object2 := TestObj{
        Name:  "James1",
        Value: 128,
        Size:  259,
        Guard: 56.4,
    }
​
    info := CompareStruct(&object1, &object2, 0, "Name")
    fmt.Println("compare: ", info)
​
}
​
复制代码

为了不把问题复杂化,该函数不支持结构体嵌套,有此种情形的,需要过滤掉,再单独调用。

由上述接口启发,有些场合要检查结构体的字符串字段是否为空,因为结构体还可能有其它类型字段,所以要指定检查的字段名称,实现如下:

func CheckStructEmpty(data interface{}, checkname ...string) (ret int) {
    ret = 0// 预防为空
    if IsInterfaceNil(data) {
        ret = 1
        return
    }
​
    var checkNames []string
    for _, item := range checkname {
        checkNames = append(checkNames, item)
    }
​
    check := func(a string, b []string) bool {
        for _, item := range b {
            if strings.EqualFold(item, a) {
                return true
            }
        }
        return false
    }
​
    object := reflect.ValueOf(data)
    myref := object.Elem()
    // fmt.Printf("myref: %v %v\n", data, object)
    //typeOfType := myref.Type()
    for i := 0; i < myref.NumField(); i++ {
        // 如果不需要对比,跳过
        if ok := check(myref.Type().Field(i).Name, checkNames); !ok {
            continue
        }
​
        field := myref.Field(i)
        switch field.Type().Name() {
        case "string":
            ret1 := field.Interface().(string) == ""
            if ret1 {
                ret = 1
            }
        default:
            ret = 0
        }
    }
    return
}
复制代码

函数返回值为1表示为空,0表示非空。由于不支持嵌套结构体形式,所以复杂结构体需要调用多次函数,将返回值累加,如果不为0,则说明有一字段为空。上述代码仅判断string类型,可类推到其它类型,也可包含检查范围,但就变得复杂了。

go 版本导致的编译出错

# golang.org/x/exp/constraints
vendor/golang.org/x/exp/constraints/constraints.go:13:2: invalid character U+007E '~'
vendor/golang.org/x/exp/constraints/constraints.go:13:7: syntax error: unexpected |, expecting semicolon or newline or }
vendor/golang.org/x/exp/constraints/constraints.go:20:2: invalid character U+007E '~'
vendor/golang.org/x/exp/constraints/constraints.go:20:8: syntax error: unexpected |, expecting semicolon or newline or }
复制代码

错误较奇怪,到 github 仓库上查看源码,到能编译通过的机器查源码,都和出错的代码一样。后来才发现,出错所用的 golang 版本是 1.16,换成 1.18 即解决问题。

工作记录

这个月做了很多不属于自己而又花费时间的事,但又不得不做。因为自己的事务要测试,所以在服务器部署了 oracle 数据库,而后顺便部署了 mysql,再顺便帮忙创建各种数据表,再到帮忙开放端口,创建用户,最终负责维护服务器。一开始就是老好人的人设,不知怎么能改善这种状态。

这个月进行了一次 docker 入门的培训。由于是当天早上领导要求当天下午培训,于是用 Markdown 写了个文档,幸好以前有积累,到网上找了些图示,再整理下文字进行补充,然后导出pdf,就成了培训资料。不过,由于时间紧,而又新引入容器,在开发阶段,不是所有人都会接受,而事情只要有人做就行,所以还是由我代劳,但自身又有编码任务,不能占用过多的上班时间。在镜像中适配服务,涉及各种依赖库,很是麻烦,但得做。

月中公司挂牌了,阵仗比较大。但该怎么工作还是怎么工作,不知道后面会不会有新的饼。因为依旧25号发上个月工资,所以目前及未来一段时间应该没有变化(至少从这点能猜出一二)。

生活记录

不知何时起,才2岁半的大妞竟然能生气几个小时。小孩比较单纯,脑子想着什么,马上表现出来。正常情况下,大妞到处跑,逗大人,玩玩具,找零食吃。如果有什么事不顺她意,或大人语气重些,就会到墙角站着,或去阳台站着。如果是大锤,则是哭。最终结果都是不理人,不说话,怎么安慰也无济于事。不知他们长大一点,兄妹吵架,又会是怎样的情形。

某周日晚上睡前,大妞唱了句“世上只有爸爸好”,被妈妈教育,大妞生气,于是她妈妈加大教育力度,顺便从大锤出生到当前的不好的事情细数了一次,根据经验,我是不能也不敢插话的。于是小孩又是半夜才入睡,而我也因为这个事耽搁,又熬到2点多才基本完成工作,算是能暂时交差。
我知道小孩是有“秩序敏感期”,但不知道小孩某些无知的“创造”是否有理论支撑。人与人的生活经历不同,对某些事物的感受不同,因而不可避免会出现观点的差异,所以才要“求同存异”,只是不是所有的人都有这个想法,比如,大锤接到他妈妈给的东西,一定要说“谢谢”,而我是无所谓的,我甚至刻意让大锤记住家里所有的事都是他妈妈做的,他爸爸只会玩手机和玩游戏。 幸好在公司里面,部门研发氛围较好,至于我没感受到尔虞我诈,相互推诿(当然要抛开外包和正式员工的差别这个事实)。只是家里就一地鸡毛了。

月底前几天,看到小区大门门禁被折,一些健身器材被毁,垃圾满地,才知道是物业要走了,新的物业又没来。后来结合小区公告、法院文书、网上新闻,才知道一些事由。小区建成之初,开发商旗下的物业入住手续不合法,至今有十多年了,几年前开始官司,物业成了被告;上半年,业委会成了被执行人。
现在,物业被限期撤走,从公告上看,临走前要把属于他们的资产搬走,并保留追缴物业费的权利。后来小区挂起横幅,既有欢迎新物业的,又有新物业承诺的。后来新物业贴出公开信,表明了要稳定、要提升的态度和决心,同时提到由于旧物业没有留下资料,要重新登记云云。物业和业主本来就是对立的,虽说有业委会代表业主,但其只是代表而已。月底最后一天,看到广西较大的报社报道到这事,看来还是挺大的,不过还是看开点罢。 话说,工作以来,也换了很多家公司,但从不敢删库跑路,也和原同事保持着关系,因为南宁搞IT的圈子太小了,做人留一线,日后好相见。

思想方面

关于程序默认行为的一点思考。

之前实现的数据校验(后因事务中断,转由其它同事继续开发),需要设计校验的配置文件,一开始设计时,在每个数据表字段都加上need参数,值为1表示要校验,为0表示不要校验。后发觉此法繁琐,因为做这个 工作的程序本身就是校验,没必要画蛇添足人为加上限制,如果某字段不需校验,配置文件不出现此字段即可。即:配置文件中所列出的,都是要校验,无必要不列出。

golangswitch默认会退出case条件,如要继续执行,则显式加上fallthrough。而C/C++语言,则是相反,如退出case条件,需显式加break。两种语言行为表现截然不同,只能编码时留意,没有很好的理解方法。

无常

使用了十余年的域名明年到期,就不再使用了。以前注册了若干个云盘,也无法再使用了(现在维护着价格不菲的云主机,保存和共享资料)。2008年注册使用至今的CSDN,也有很大变化,我上传的0积分资源变成要积分了,有很多资源收费了,我拥有的1千多C币也没了。唯一不变的是变化本身,只能拥着变化,跟上时代步伐。不过,我还是比较怀旧,听歌要听二十年前的,看电影亦然。像我维护着的 golang 工程,也依然使用着 KubeEdge 的模块注册框架。

业余研究

这个月除了写代码外,没干啥事,而且代码还是业务代码,没有做研究提升技能。思考再三,决定拿些古文来背。反正一天通勤有2个小时,时间是有的,家庭工作两不误。一来练练脑子防止老化,二来教大锤背。当然,对此事既不炫耀沾沾自喜,也不强求背多少。当登高远眺时,能想到“山原旷其盈视”、想到“天高地迥,觉宇宙之无穷”,想到“仰观宇宙之大,俯察品类之盛”,感受一下古人的情怀。当意不平,遇到坎时,能用些话抚平内心,激励自己。这就够了。

后来罗师傅说他买了套李白集校注,我不甘人后,作为价格敏感型消费者,趁着提前开始的双十一,买了史记、三国志,还有古文观止等书装门面。后来又和罗师傅相约去古玩市场淘些古籍。当然这些书并不会专门抽时间阅读,可能兴之所至,偶尔翻翻。毕竟这玩意不能当饭吃,写代码才有饭吃。

分类:
代码人生
标签:
收藏成功!
已添加到「」, 点击更改