英文:
template won't evaluate fields that are interface type as the underlying type
问题
使用golang的html/template
(与text/template
的行为相同)。如果我有一个结构体,其中一个成员是接口类型,我无法访问底层类型的成员(具体来说,我想访问实现接口InnerInterface
的结构体上的字段,但是通过InnerInterface
接口类型返回,而不是结构体类型)。
http://play.golang.org/p/ZH8wSK83oM
package main
import "fmt"
import "os"
import "html/template"
type InnerInterface interface{ InnerSomeMethod() }
type MyInnerStruct struct { Title string }
func (mis MyInnerStruct)InnerSomeMethod() { fmt.Println("Just to show we're satisfying the interface") }
type MyOuterStruct struct { Inner InnerInterface }
func main() {
fmt.Println("Starting")
arg := MyOuterStruct{Inner:MyInnerStruct{Title:"test1"}}
err := template.Must(template.New("testtmpl").Parse("{{.Inner.Title}}")).Execute(os.Stdout, arg)
if err != nil { panic(err) }
}
将type MyOuterStruct struct { Inner InnerInterface }
更改为完全通用的接口,即type MyOuterStruct struct { Inner interface{} }
可以正确渲染。这让我相信渲染引擎对interface{}
进行了特殊处理。
有没有比在需要动态评估字段时都使用interface{}
更好的方法?
英文:
Using golang html/template
(same behavior with text/template
). If I have a struct with a member that is of an interface type, I cannot access members of the underlying type (specifically trying to access fields that are on a struct that implements interface InnerInterface
but is return via the InnerInterface
interface type, not the struct type).
http://play.golang.org/p/ZH8wSK83oM
package main
import "fmt"
import "os"
import "html/template"
type InnerInterface interface{ InnerSomeMethod() }
type MyInnerStruct struct { Title string }
func (mis MyInnerStruct)InnerSomeMethod() { fmt.Println("Just to show we're satisfying the interface") }
type MyOuterStruct struct { Inner InnerInterface }
func main() {
fmt.Println("Starting")
arg := MyOuterStruct{Inner:MyInnerStruct{Title:"test1"}}
err := template.Must(template.New("testtmpl").Parse("{{.Inner.Title}}")).Execute(os.Stdout, arg)
if err != nil { panic(err) }
}
Changing: type MyOuterStruct struct { Inner InnerInterface }
to a totally generic interface, i.e. type MyOuterStruct struct { Inner interface{} }
makes it render properly. This leads me to believe that interface{}
is treated specially by the rendering engine.
Is there a better way to do this than to use interface{}
whenever I want to be able to dynamically evaluate fields like this?
答案1
得分: 5
你说得对,interface{}
在渲染引擎中的处理方式确实不同。只有interface{}
类型的值会被解包,具有方法集的接口值不会被解包。我想背后的原因是,如果你有一个接口类型,你特别限制了该类型的方法集。因此,你不希望模板引擎尝试访问可能在该接口后面的成员。
这个"问题"是由exec.go
中的indirect
函数引起的:
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
if v.IsNil() {
return v, true
}
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
break
}
}
return v, false
}
这个方法被调用来获取反射值的最深层值。假设你有一个指针指向一个指针指向一个指针,这个函数将返回最后一个指针的值。对于接口值也是一样的。关键在于,一旦一个接口值有多于0个方法,间接引用就会停止。这正是你描述的行为。
既然这似乎是预期的行为,你可以在你的接口中定义一个Title() string
方法,并让它返回字符串。
英文:
You're correct with saying that interface{}
is handled differently by the rendering
engine. Only interface{}
values are unpacked, interface values that have a method set are not.
I suppose the reasoning behind this is that if you have a interface type, you specifically limit the type to the method set. Therefore, you don't want the template engine trying to access members that may lie behind that interface.
The 'problem' is caused by the function indirect
in exec.go
:
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
if v.IsNil() {
return v, true
}
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
break
}
}
return v, false
}
This method is called to get to the deepest value of a reflected value.
Suppose you have a pointer on a pointer on a pointer, this function will get return the
last of these. The same goes for interface values. The crux is that as soon as a
interface value has more than 0 methods, the indirection stops there. Exactly the
behaviour you're describing.
As this seems to be intended behaviour, what you can do is to define a Title() string
method in your interface and let it return the string.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论