Golang模板中如何使用私有结构体成员

Golang模板中如何使用私有结构体成员 是否有一种简单的方法可以获取结构体所有成员(包括私有和公有)的副本,以供只读访问?这样其他包(如模板)就可以使用它们。我不喜欢使用getter方法,而这只需要一个。

如果我能定义一个单独的get方法来暴露整个结构体,即从包成员调用时获取其私有值的副本,那就太好了。

据我所知,无法将导出的成员设置为不可变的只读状态。使用getter和setter方法会很糟糕。

6 回复

我认为你应该重新考虑你的需求。如果类型的作者决定将其某些成员设为私有,很可能是有原因的。不将它们设为公开以便在模板中使用的决定是经过深思熟虑的。

更多关于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,同时保持了封装性。反射方法虽然灵活,但会失去编译时类型检查,且性能较差。

回到顶部