Golang模板中如何使用私有结构体成员
Golang模板中如何使用私有结构体成员 是否有一种简单的方法可以获取结构体所有成员(包括私有和公有)的副本,以供只读访问?这样其他包(如模板)就可以使用它们。我不喜欢使用getter方法,而这只需要一个。
如果我能定义一个单独的get方法来暴露整个结构体,即从包成员调用时获取其私有值的副本,那就太好了。
据我所知,无法将导出的成员设置为不可变的只读状态。使用getter和setter方法会很糟糕。
我认为你应该重新考虑你的需求。如果类型的作者决定将其某些成员设为私有,很可能是有原因的。不将它们设为公开以便在模板中使用的决定是经过深思熟虑的。
更多关于Golang模板中如何使用私有结构体成员的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使结构体成员只读的模式是不导出它,并添加一个返回它的funct。
顺便说一句,我想不出任何支持公共只读成员的编程语言。
顺便说一句,我想不出任何支持公共只读成员变量的编程语言。
我知道至少 C# 和 Haxe 有一些特殊的语法,可以在一个特殊的语句中定义字段和 setter/getter。像这样定义一个公共键实际上使其成为私有的,而 setter/getter 是公共的。如果你只以这种方式定义一个 getter,那么它就是只读的。
据我所知,setter 和 getter 是以一种透明的方式创建的,使得设置是通过赋值运算符完成的,而获取则无需使用括号,只需字段名即可。
我们公司正在将一些项目迁移到 Go 语言,并且我们控制着包和成员的可见性。
我们拥有非常庞大的数据模型,我们需要能够让所有包都具有读取权限,但除非我们添加设置器,否则无法修改它们。
在 Go 语言中,实现这一点的惯用方法是什么?我在 Stack Overflow 上找到的所有信息都表明,在 Go 中不应该使用 getter 和 setter,我同意这一点,因为对我们来说,样板代码会非常庞大。而且有人建议我不必担心,因为如果需要更改字段,重构很容易,但这完全没有道理。难道我必须编写工具来确保值永远不会在设置器之外被修改吗?
我觉得我可能遗漏了某个特性或设计模式。
utjuyhywhzxbvhryyfja: 是否有一种简单的方法可以获取结构体所有成员(包括私有和公有)的副本,用于只读访问?
不行……你无法从定义它们的包外部轻松读取未导出的结构体成员。
你可以做的是定义另一个成员为公开的结构体并返回它,例如 playground
package main
import (
"fmt"
)
type DontMessWithMe struct {
a int
b string
}
type DontMessWithMeData struct {
A int
B string
}
func (d *DontMessWithMe) Data() *DontMessWithMeData {
return &DontMessWithMeData{
A: d.a,
B: d.b,
}
}
func main() {
d := DontMessWithMe{a: 1, b: "potato"}
data := d.Data()
fmt.Printf("data = %#v\n", data)
}
你也可以在子结构中定义数据并返回其副本,例如 (playground)
package main
import (
"fmt"
)
type DontMessWithMeData struct {
A int
B string
}
type DontMessWithMe struct {
private DontMessWithMeData
}
func (d *DontMessWithMe) Data() *DontMessWithMeData {
privateCopy := d.private
return &privateCopy
}
func main() {
d := DontMessWithMe{private: DontMessWithMeData{A: 1, B: "potato"}}
data := d.Data()
fmt.Printf("data = %#v\n", data)
}
这种方法可能效率更高,但会更多地改变数据结构。
在Golang中,可以通过几种方式在模板中访问私有结构体成员。以下是几种实用的方法:
方法1:使用导出方法返回结构体副本
定义一个导出方法,返回包含私有字段的结构体副本:
// 在定义结构体的包中
package mypkg
type myStruct struct {
PublicField string
privateField string // 私有字段
}
// 导出方法返回包含私有字段的副本
func (s *myStruct) Copy() myStruct {
return myStruct{
PublicField: s.PublicField,
privateField: s.privateField,
}
}
// 在模板中使用
func (s *myStruct) TemplateData() interface{} {
return struct {
PublicField string
PrivateField string
}{
PublicField: s.PublicField,
PrivateField: s.privateField,
}
}
方法2:使用匿名结构体暴露字段
创建一个匿名结构体来暴露所有字段:
package mypkg
import "html/template"
type User struct {
Name string
email string // 私有字段
password string // 私有字段
}
// 为模板创建数据视图
func (u *User) TemplateView() interface{} {
return struct {
Name string
Email string
Password string
}{
Name: u.Name,
Email: u.email,
Password: u.password,
}
}
// 使用示例
func RenderUser() {
user := &User{
Name: "John",
email: "john@example.com",
password: "secret",
}
tmpl := template.Must(template.New("user").Parse(`
Name: {{.Name}}
Email: {{.Email}}
Password: {{.Password}}
`))
tmpl.Execute(os.Stdout, user.TemplateView())
}
方法3:使用接口和嵌入
通过接口暴露需要访问的字段:
package mypkg
type privateData struct {
secret string
token string
}
type PublicInterface interface {
GetData() interface{}
}
type MyStruct struct {
privateData
PublicField string
}
func (m *MyStruct) GetData() interface{} {
return struct {
PublicField string
Secret string
Token string
}{
PublicField: m.PublicField,
Secret: m.secret,
Token: m.token,
}
}
// 在模板中使用
tmpl := template.Must(template.New("test").Parse(`
Public: {{.PublicField}}
Secret: {{.Secret}}
Token: {{.Token}}
`))
data := myStructInstance.GetData()
tmpl.Execute(writer, data)
方法4:使用反射(不推荐用于生产)
虽然不推荐,但可以使用反射在运行时访问私有字段:
package main
import (
"fmt"
"html/template"
"reflect"
)
type PrivateStruct struct {
public string
private string
}
func ExposePrivateFields(s interface{}) map[string]interface{} {
v := reflect.ValueOf(s).Elem()
t := v.Type()
result := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
// 即使字段是私有的,反射也可以访问
result[field.Name] = value.Interface()
}
return result
}
func main() {
ps := &PrivateStruct{
public: "public data",
private: "private data",
}
data := ExposePrivateFields(ps)
tmpl := template.Must(template.New("test").Parse(`
Public: {{.public}}
Private: {{.private}}
`))
tmpl.Execute(os.Stdout, data)
}
推荐使用方法1或方法2,它们提供了类型安全且清晰的API,同时保持了封装性。反射方法虽然灵活,但会失去编译时类型检查,且性能较差。

