2017-07-04 18 views
1

我有一個參數類型爲interface{}的函數。這個參數代表我的模板數據。所以在每個頁面上它存儲不同的數據類型(主要是結構)。我想附加一些數據到這個參數的數據,但它是一個interface{}類型,我不能這樣做。如何通過引用設置接口{}參數?

這是我的嘗試:

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) 
} 

我能順利拿到"HeaderCSS"值。但我無法用另一個價值取代它。我究竟做錯了什麼?

我templateData看起來是這樣的:

我有一個通用的結構:

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

,我有另一種結構,如:

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

我送這第二個結構作爲templateData論據。

現在我得到這個錯誤:

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

的代碼從上面從這個答案的啓發:https://stackoverflow.com/a/26824071/1564840

我希望能夠從這個界面添加/編輯值。任何想法我怎麼能做到這一點?謝謝。

+1

看來,如果你知道實際類型:只需鍵入assert到該類型並執行此類型支持的任何操作。如果不是:您必須顯示完整的代碼。順便說一句:擺脫空的'interface {}',你的代碼會更簡單。 – Volker

+0

我不能只刪除接口{},因爲在每個頁面上參數類型會有所不同。 interface {}是允許我接受所有類型參數的選項。 – Pascut

+1

這已經得到了很好的回答,但我想建議你可能應該添加一個'SetHeaderCSS'方法並傳入指定該方法的HeaderCSSSetter接口。這將通過簡單的更改消除所有反射*,從而爲您提供更好的性能,更好的可讀性和編譯時類型安全性。 – Adrian

回答

2

不要將指針傳遞給接口。相反,interface{}值應該包含指針。而只是簡單地交出該interface{}值:

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

即使你不能用一個更具體的類型比interface{}(因爲你必須允許多種類型的),你仍然可以使用type assertion,這將是「超級」易:

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

} 

試試這一個在Go Playground

如果你必須或者想使用反射,這是appendCustomData()如何實現:

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))) 
    } 
} 

測試它:

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

輸出(嘗試在Go Playground):

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

更新:

要回答你編輯的問題:當傳遞的值是嵌入另一個結構的結構時,沒有區別。但是包含在interface{}中的值仍然必須是指針。

appendCustomData()是追加內容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")) 
     } 
    } 
} 

測試它:

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

輸出(嘗試在Go Playground):

&{TemplateData:{FooterJS: HeaderJS: HeaderCSS:old} Form:} 
&{TemplateData:{FooterJS: HeaderJS: HeaderCSS:old+new} Form:} 
+0

Goog示例。我編輯了我的問題,我添加了結構細節。在我的情況下,我有一個擴展另一個結構的結構。你能想出一個方法來使它在這個例子上也有效嗎? – Pascut

+0

@Pascut查看編輯答案。 – icza

+0

感謝您的好例子! – Pascut