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


    本文为 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    结构体字段对比

    最近进行的一个工程,在集成测试中需要对比请求的结果和已知的结果,里面有大量的较多字段的结构体,幸好之前实现了结构体字段对比的接口,但不够完善,为了泛化,参数使用了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)
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

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

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

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    函数返回值为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 }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    错误较奇怪,到 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个小时,时间是有的,家庭工作两不误。一来练练脑子防止老化,二来教大锤背。当然,对此事既不炫耀沾沾自喜,也不强求背多少。当登高远眺时,能想到“山原旷其盈视”、想到“天高地迥,觉宇宙之无穷”,想到“仰观宇宙之大,俯察品类之盛”,感受一下古人的情怀。当意不平,遇到坎时,能用些话抚平内心,激励自己。这就够了。
    后来罗师傅说他买了套李白集校注,我不甘人后,作为价格敏感型消费者,趁着提前开始的双十一,买了史记、三国志,还有古文观止等书装门面。后来又和罗师傅相约去古玩市场淘些古籍。当然这些书并不会专门抽时间阅读,可能兴之所至,偶尔翻翻。毕竟这玩意不能当饭吃,写代码才有饭吃。

  • 相关阅读:
    word 毕业论文格式调整
    艾美捷细胞低密度脂肪酸(LDL)摄取试剂盒的功能&应用
    讨论面向对象及方法学的特点?为什么要用面向对象方法开发软件?
    LabVIEW如何使用热键去触发自定义的事件
    Linux与Shell学习--shell系列5--Shell运算符1(算数运算符和关系运算符)
    CAS号:2353409-93-3|PC生物素-PEG3-NHS碳酸酯 可光裂解生物素试剂
    【Java面试】这也太卷了~面试竟然问,HTTP协议和RPC协议的区别
    Chrome Extensions v3 迁移清单
    fiddler证书下载(模拟器)
    华为云耀云服务器HECS安装Docker
  • 原文地址:https://blog.csdn.net/subfate/article/details/127621325