Golang如何在不同结构体之间进行切换

5

我是Golang的新手,正在尝试创建一个函数,根据它所用的结构体,将使用Sprintf返回格式化字符串。

type Name struct {
    Title string
    First string
    Last  string
}

type Location struct {
    Street string
    City   string
    State  string
    Zip    string
}

func Merge(m interface{}) string {
    switch m.(type) {
    case *Location:
        return fmt.Sprintf("%s \n %s, %s %s", m.(*Location).Street, m.(*Location).City, m.(*Location).State, m.(*Location).Zip)
    case *Name:
        return fmt.Sprintf("%s. %s %s", m.(*Name).Title, m.(*Name).First, m.(*Name).Last)
    }
    return "Not Applicable"
}

fmt.Println(Merge(Location))

我的PrintLn出现"不适用"的提示信息。在代码的另一个版本中,我认为提示信息是"索引超出范围"。


2
你没有将 *Location 传递给 Merge,这就是为什么会出现 "Not Applicable" 的原因。此外,你可能想考虑为你的类型实现 fmt.Stringer - user142162
谢谢你的建议,Tim。你能详细说明一下我该如何使用fmt.Stringer吗?println(merge()))这行代码只是我所拥有的内容的简化版本。我实际拥有的代码是fmt.Println(Merge(servicesA.Users[0].Location))。 - Scott S.
servicesA.Users[0].Location 的类型是什么:Location 还是 *Location?同时,您可能会发现在 Sprintf 中使用 %v%+v 很有用。 - kostya
感谢您的评论,kostya。它应该只是Location。当我尝试在函数中删除指针(*Location to Location)并运行它时,我得到了这个错误:panic: interface conversion: interface is main.Location not *main.Location。 - Scott S.
1个回答

9
在你的例子中,你试图将结构体本身传递给函数,而不是结构体的实例。当我运行你的代码时,它无法编译。虽然我同意上面的评论,但这个问题最好通过确保每个结构体都满足fmt.Stringer接口来处理。 fmt.Stringer接口 你的代码的修复版本:
package main

import "fmt"

type Name struct {
    Title string
    First string
    Last  string
}

type Location struct {
    Street string
    City   string
    State  string
    Zip    string
}

func Merge(m interface{}) string {
    switch m.(type) {
    case Location:
        return fmt.Sprintf("%s \n %s, %s %s", m.(Location).Street, m.(Location).City, m.(Location).State, m.(Location).Zip)
    case Name:
        return fmt.Sprintf("%s. %s %s", m.(Name).Title, m.(Name).First, m.(Name).Last)
    }
    return "Not Applicable"
}

func main() {
    l := Location{
        Street: "122 Broadway",
        City: "New York",
        State: "NY",
        Zip: "1000",
    }
    fmt.Println(Merge(l))
}

使用 fmt.String 的版本:

package main

import "fmt"

type Name struct {
    Title string
    First string
    Last  string
}

func (n *Name) String() string {
    return fmt.Sprintf("%s. %s %s", n.Title, n.First, n.Last)
}

type Location struct {
    Street string
    City   string
    State  string
    Zip    string
}

func (l *Location) String() string {
    return fmt.Sprintf("%s \n %s, %s %s", l.Street, l.City, l.State, l.Zip)
}

func main() {
    l := &Location{
        Street: "120 Broadway",
        City:   "New York",
        State:  "NY",
        Zip:    "1000",
    }
    fmt.Println(l)

    n := &Name{
        Title: "Mr",
        First: "Billy",
        Last:  "Bob",
    }
    fmt.Println(n)
}

谢谢你,汤姆!那是一个很好的解释!当我打出我的Println函数时,我也忘了在我的Merge函数中包含一个&引用到*Location。我的最终Println方法变成了这样:fmt.Println(Merge(&servicesA.Users[0].Location)) 在进行了这个更改并审查了你的代码之后,它按照我预期的方式工作了。再次感谢你,汤姆。 - Scott S.
1
如果您要使用类型开关,则不应在情况体中使用类型断言。例如:https://play.golang.org/p/Wh9mAr4xzi。 - Dave C
如果您有多个类型都想要具有共同的行为,那么使用接口是很常见的(如果这是整个类型的字符串表示,则fmt.Stringer才是合适的)。例如,可能会像https://play.golang.org/p/xq3a0RcbFa这样做一些事情。OP提到示例是简化的。 - Dave C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接