douyun1972 2017-07-04 11:56
浏览 27
已采纳

如何通过引用设置interface {}参数?

I have a function that has a parameter with the type interface{}. This parameter represents my template data. So on each page it stores different data types (mostly structs). I want to append some data to this parameter's data, but it's an interface{} type and I can't do it.

This is what I tried:

func LoadTemplate(templateData interface) {
  appendCustomData(&templateData)
  ... //other functionality that is not relevant
}

func appendCustomData(dst interface{}) {
    // ValueOf to enter reflect-land
    dstPtrValue := reflect.ValueOf(dst)
    // need the type to create a value
    dstPtrType := dstPtrValue.Type()
    // *T -> T, crashes if not a ptr
    dstType := dstPtrType.Elem()
    // the *dst in *dst = zero
    dstValue := reflect.Indirect(dstPtrValue)
    // the zero in *dst = zero
    zeroValue := reflect.Zero(dstType)
    // the = in *dst = 0

    v := reflect.ValueOf(dst).Elem().Elem().FieldByName("HeaderCSS")
    if v.IsValid() {
        v = reflect.ValueOf("new header css value")
    }

    reflect.ValueOf(dst).Elem().Elem().FieldByName("HeaderCSS").Set(reflect.ValueOf(v))

    //dstValue.Set(zeroValue)
    fmt.Println("new dstValue: ", dstValue)
}

I can successfully get the "HeaderCSS" value. But I can't replace it with another value. What am I doing wrong?

My templateData looks like this:

I have a generic struct:

type TemplateData struct {
    FooterJS template.HTML
    HeaderJS template.HTML
    HeaderCSS template.HTML
    //and some more
}

and I have another struct, such as:

type pageStruct struct {
    TemplateData //extends the previous struct
    Form template.HTML
    // and some other maps/string
}

I send this second struct as templateData argument.

Right now I get this error:

"reflect.Value.Set using unaddressable value" at the line: reflect.ValueOf(dst).Elem().Elem().FieldByName("HeaderCSS").Set(reflect.ValueOf(v))

The code from above is inspired from this answer: https://stackoverflow.com/a/26824071/1564840

I want to be able to append/edit values from this interface. Any idea how can I do it? Thanks.

  • 写回答

1条回答 默认 最新

  • doucuoyan0426 2017-07-04 12:08
    关注

    Don't pass a pointer to interface. Instead the interface{} value should contain the pointer. And simply just hand over this interface{} value:

    func LoadTemplate(templateData interface) {
      appendCustomData(templateData)
      ... //other functionality that is not relevant
    }
    

    Even if you can't use a more concrete type than interface{} (because you must allow multiple types), you can still use type assertion, it will be "super" easy:

    func appendCustomData(d interface{}) {
        if ps, ok := d.(*pageStruct); ok {
            ps.TemplateData.HeaderCSS += "+new"
        }
    
    }
    

    Try this one on the Go Playground.

    If you must or want to use reflection, this is how appendCustomData() can be implemented:

    type Data struct {
        Name  string
        Age   int
        Marks []int
    }
    
    func appendCustomData(d interface{}) {
        v := reflect.ValueOf(d).Elem()
    
        if f := v.FieldByName("Name"); f.IsValid() {
            f.SetString(f.Interface().(string) + "2")
        }
    
        if f := v.FieldByName("Age"); f.IsValid() {
            f.SetInt(f.Int() + 2)
        }
    
        if f := v.FieldByName("Marks"); f.IsValid() {
            f.Set(reflect.ValueOf(append(f.Interface().([]int), 2)))
        }
    
        if f := v.FieldByName("Invalid"); f.IsValid() {
            f.Set(reflect.ValueOf(append(f.Interface().([]int), 2)))
        }
    }
    

    Testing it:

    d := &Data{
        Name:  "Bob",
        Age:   22,
        Marks: []int{5, 4, 3},
    }
    fmt.Printf("%+v
    ", d)
    appendCustomData(d)
    fmt.Printf("%+v
    ", d)
    

    Output (try it on the Go Playground):

    &{Name:Bob Age:22 Marks:[5 4 3]}
    &{Name:Bob2 Age:24 Marks:[5 4 3 2]}
    

    Update:

    To answer your edited question: there is no difference when the value passed is a struct that embeds another struct. But the value wrapped in the interface{} still must be a pointer.

    Example appendCustomData() that appends content to pageStruct.TemplateData.HeaderCSS:

    func appendCustomData(d interface{}) {
        v := reflect.ValueOf(d).Elem()
    
        if f := v.FieldByName("TemplateData"); f.IsValid() {
            if f = f.FieldByName("HeaderCSS"); f.IsValid() {
                f.Set(reflect.ValueOf(f.Interface().(template.HTML) + "+new"))
            }
        }
    }
    

    Testing it:

    ps := &pageStruct{
        TemplateData: TemplateData{
            HeaderCSS: template.HTML("old"),
        },
    }
    fmt.Printf("%+v
    ", ps)
    appendCustomData(ps)
    fmt.Printf("%+v
    ", ps)
    

    Output (try it on the Go Playground):

    &{TemplateData:{FooterJS: HeaderJS: HeaderCSS:old} Form:}
    &{TemplateData:{FooterJS: HeaderJS: HeaderCSS:old+new} Form:}
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 echarts动画效果失效的问题。官网下载的例子。
  • ¥60 许可证msc licensing软件报错显示已有相同版本软件,但是下一步显示无法读取日志目录。
  • ¥15 Attention is all you need 的代码运行
  • ¥15 一个服务器已经有一个系统了如果用usb再装一个系统,原来的系统会被覆盖掉吗
  • ¥15 使用esm_msa1_t12_100M_UR50S蛋白质语言模型进行零样本预测时,终端显示出了sequence handled的进度条,但是并不出结果就自动终止回到命令提示行了是怎么回事:
  • ¥15 前置放大电路与功率放大电路相连放大倍数出现问题
  • ¥30 关于<main>标签页面跳转的问题
  • ¥80 部署运行web自动化项目
  • ¥15 腾讯云如何建立同一个项目中物模型之间的联系
  • ¥30 VMware 云桌面水印如何添加