Go 如何优雅地处理泛型问题

tinyfeng · 2020年04月16日 · 最后由 HDJ 回复于 2022年08月14日 · 3601 次阅读

把电子表格解析为对象,传入文件路径,需要解析为的类型

返回值为 interface{},如何优雅地访问返回值的成员/方法

type Student struct {
    Name string `xlsxCol:"student name"`
    Age string `xlsxCol:"student age"`
    Grade string `xlsxCol:"student grate"`
}

type Teacher struct {
    Name string `xlsxCol:"teacher name"`
    Age string `xlsxCol:"teacher age"`
}

func parse(path string, t reflect.Type) []interface{} {
... // 已经实现
}

在用的时候

result := parse("/tmp/xxx.xslx", reflect.TypeOf(Student{}))
if len(result) > 0 {
    result[0].Name // 怎么可以访问到这个属性
}

如果是 debug 模式,在result[0].Name 这一行打断点,已经可以看到result[0]{interface{} | Student}的类型,且 debug 模式可以使用 Evaluate Expression result[0].Name 访问到 Name,但是静态编译肯定过不了。

想知道通常情况应该怎么处理这一问题,我应该在每个用的地方使用类型断言吗?

我实在不想写这种代码

func parseToStudent(path string) []Student {
...
}
func parseToTeacher(path string) []Teacher {
...
}
...

此处应该祭出这个图了

go 就是这样,写起来很别扭,调试也麻烦

大致如下,一些边缘 case 检查(obj 不是指针)这边忽略了

func parse(ss string, obj interface{}) {
    var (
        v = reflect.ValueOf(obj).Elem()
        f = v.FieldByName("Age")
    )

    if f.CanSet() {
        f.SetString(ss)
    }
}

一种是比较传统的办法,用接口挡一下。但是字段太多的时候会很蠢……

type nameSetable interface{ SetName(string) }
type ageSetable interface{ SetAge(string) }
type gradeSetable interface{ SetGrade(string) }

type student struct {
    Name  string `xlsxCol:"student name"`
    Age   string `xlsxCol:"student age"`
    Grade string `xlsxCol:"student grate"`
}

var _ nameSetable = &student{}
var _ ageSetable = &student{}
var _ gradeSetable = &student{}

func (s *student) SetName(ss string)  { s.Name = ss }
func (s *student) SetAge(ss string)   { s.Age = ss }
func (s *student) SetGrade(ss string) { s.Grade = ss }

type teacher struct {
    Name string `xlsxCol:"teacher name"`
    Age  string `xlsxCol:"teacher age"`
}

var _ nameSetable = &teacher{}
var _ ageSetable = &teacher{}

func (s *teacher) SetName(ss string) { s.Name = ss }
func (s *teacher) SetAge(ss string)  { s.Age = ss }

func parse(ss string, obj interface{}) {
    if s, ok := obj.(nameSetable); ok {
        s.SetName(ss)
    }

    if s, ok := obj.(ageSetable); ok {
        s.SetAge(ss)
    }

    if s, ok := obj.(gradeSetable); ok {
        s.SetGrade(ss)
    }
}

🤮

我基本不用 go 的泛型,写着窝火。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号