如何在不将其作为键引用的情况下,将一个结构字段用于另一个结构中?

huangapple go评论75阅读模式
英文:

How to use a struct field into another struct without referring to it as a key

问题

我想在另一个结构体中插入一个结构体字段,而不必使用结构体名称。

我知道可以这样做:

type Person struct {
  Name string
}

type User struct {
  Person
  Email, Password string
}

但这会导致以下结构体:

user := User{Person: Person{Name: ""}, Email: "", Password: ""}

我该如何做到像这样:

type Person struct {
  Name string
}

type User struct {
  Name Person.Name // 这里
  Email, Password string
}

以便像这样使用它:

user := User{Name: "", Email: "", Password: ""}

这种做法可行吗?

英文:

I want to insert a struct field into another struct without having to use the struct name.

I know that I can do this:

type Person struct {
  Name string
}

type User struct {
  Person
  Email, Password string
}

But It results in this struct:

user := User{Person: Person{Name: ""}, Email: "", Password: ""}

How can I do something like this:

type Person struct {
  Name string
}

type User struct {
  Name Person.Name // Here
  Email, Password string
}

To use it like this

user := User{Name: "", Email: "", Password: ""}

Is it possible?

答案1

得分: 1

简单来说,根据当前的语言实现,你无法以你期望的方式初始化内部的Person

当初始化一个字面量时,你需要明确指定(或者说:字面量!)。由于User包含一个Person,一个字面量的User必须包含一个字面量的Person,就像你所示例的那样:

    u := User{ 
        Person: Person{
            Name: "Bob",
        },
        Email: "bob@bobspot.com",
        Password: "you're kidding right?",
    } 

然而,一旦你有了一个类型为User的变量,你可以利用匿名字段来设置(或获取)匿名PersonName,如下所示:

    u := User{}
    u.Name = "Bob"
    u.Email = "bob@bobspot.com",
    u.Password = "you're kidding right?",

为什么Go要让我做这么多工作?

让我们想象一下,如果可以以你期望的方式初始化内部的Person

    u := User{ Name: "Bob" }

现在让我们进一步想象,我们稍后修改User结构并添加它自己的Name字段:

    type User struct {
        Person
        Name string
        Email string
        Password string
    }

现在你可以明显地初始化新的Name字段:

    u := User{ Name: "Bob" }

注意,这与之前初始化User.Person.Name的代码是相同的,但现在它初始化的是User.Name。这样不好。

更多陷阱

使用这样的代码还有其他潜在的问题。

首先,向User变量的未限定引用添加一个Name字段已经类似地“破坏”了对User变量上的Name的设置:

    u.Name = "Bob" // 用于设置User.Person.Name,现在设置的是User.Name

此外,只有一个匿名的Person字段时,默认情况下,User.Person.Name字段会被默认地转换为JSON的“Name”字段:

    {
        "Name": "",
        "Email": "",
        "Password": ""
    }

如果添加一个Name字段,那么这个字段将作为"Name"被转换为JSON,而User.Person.Name字段将根本不会被转换。

你可能会认为可以为User.Person.Name添加一个json标签,例如:

    type User struct {
        Person   `json:"PersonName"`
        Name     string
        Email    string
        Password string
    }

但是现在Person将被转换为一个带有Name字段的对象:

    {
        "PersonName": {
            "Name": ""
        },
        "Name": "",
        "Email": "",
        "Password": ""
    }

即使User没有Name字段,如果你尝试更改匿名Person的转换字段名,也会发生这种情况。

简而言之:在结构体中使用匿名结构体作为“添加字段”的方式可能存在问题,而且可能很脆弱,应该尽量避免使用。

英文:

Simply put, with the current language implementation you can't.

When initialising a literal you need to be explicit (or, put another way: literal! [sic]). Since a User contains a Person, a literal User must contain a literal Person, as you illustrate:

    u := User{ 
        Person: Person{
            Name: "Bob",
        },
        Email: "bob@bobspot.com",
        Password: "you're kidding right?",
    } 

However, once you have a variable of type User, you can then leverage the anonymous field to set (or get) the Name of the anonymous Person with the User:

    u := User{}
    u.Name = "Bob"
    u.Email = "bob@bobspot.com",
    u.Password = "you're kidding right?",

Why Does Go Make Me Do All This Work?

Let us imagine that it was possible to initialise the inner Person in the way you are looking for:

    u := User{ Name: "Bob" }

Now let us further imagine that we later modify the User struct and add its own Name field:

    type User struct {
        Person
        Name string
        Email string
        Password string
    }

And now you can obviously initialise the new Name field:

    u := User{ Name: "Bob" }

Notice that this is identical to the previous code that initialised User.Person.Name but now it is initialising User.Name. Not good.

More Gotchas

There are further traps lurking with code like this.

First, the addition of a Name field in User already similarly "breaks" unqualified references to Name on User variables:

    u.Name = "Bob" // used to set User.Person.Name, now sets User.Name

Also, with only an anonymous Person field, the User.Person.Name field is marshalled to JSON by default as a "Name" field:

    {
        "Name": "",
        "Email": "",
        "Password": ""
    }

If a Name field is added, then this is the field that is marshalled as "Name" and the User.Person.Name field is not marshalled at all.

You might think you can add a json tag for the User.Person.Name, e.g.

    type User struct {
        Person   `json:"PersonName"`
        Name     string
        Email    string
        Password string
    }

But now the Person is marshalled as an object with a Name field:

    {
        "PersonName": {
            "Name": ""
        },
        "Name": "",
        "Email": "",
        "Password": ""
    }

This also happens if you try to change the marshalled field name of the anonymous Person even if User does not have a Name field.

In short: using anonymous structs within structs as a way of "adding fields" is potentially problematic and fragile and should probably be avoided.

huangapple
  • 本文由 发表于 2022年12月8日 22:37:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/74731934.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定