如何在Go结构中设置默认值


143

以下问题有多种答案/技术:

  1. 如何为golang结构设置默认值?
  2. 如何在Golang中初始化结构

我有几个答案,但需要进一步讨论。



@icza您的回答的确提供了一种解决方法,但是按问题标题逐个进行,由于它是一个非常具体的问题,因此绝非相似或不可搜索。我将在我的答案中添加链接。
普拉泰克

这里有两个问题,请选择一个。假设您选择第一个问题(按问题标题),请更详细地说明您以前的研究以及其他答案需要更多讨论的地方。
邓肯·琼斯

Answers:


96

一种可能的想法是编写单独的构造函数

//Something is the structure we work with
type Something struct {
     Text string 
     DefaultText string 
} 
// NewSomething create new instance of Something
func NewSomething(text string) Something {
   something := Something{}
   something.Text = text
   something.DefaultText = "default text"
   return something
}

6
是的,这是我在回答中也提到的一种方式,但是我们无法强迫任何人仅使用此功能。
Prateek

@Prateek要么是这个,要么使用一个接口,这将是丑陋且过于复杂的。
OneOfOne 2016年

31
@Prateek是的,如果您只是简单地使类型本身不导出,则可以强迫人们使用此构造函数。您可以导出功能NewSomething,甚至田野TextDefaultText,但就是不出口的结构类型something
阿米特·库玛·古普塔

1
问题更严重...如果使用第三方(例如,库)实例化您的结构(reflect.New()例如,通过),则不能期望它知道您的特殊命名的工厂功能。我认为,在那种情况下,只要不更改语言本身,就只能使用一个接口(库可以检查)。
edam '18

1
最好设置默认值,但有时我可能想覆盖默认值。在这种情况下,我将无法使用非默认值初始化结构。对我来说有点烦
Juliatzin

68
  1. 强制方法获取结构(构造方法)。

    一个好的设计是使您的类型不导出,但提供一个类似NewMyType()的导出构造函数,您可以在其中正确初始化您的struct / type。还返回一个接口类型,而不是具体类型,并且该接口应包含其他人希望对您的值进行的操作。当然,您的具体类型必须实现该接口。

    这可以通过简单地使类型本身不导出来完成。您可以导出函数NewSomething,甚至可以导出字段Text和DefaultText,但不要导出结构类型

  2. 为自己的模块自定义它的另一种方法是使用Config结构设置默认值(链接中的选项5),但这不是一个好方法。



3
它可以在Wayback机器中使用
n8henrie

FWIW,我认为它是“选项3”-至少在回溯机器链接中。(那里没有“选项5”)。
decimus phostle

@ m90可以使golint静音,您可以将函数声明为返回公共接口类型
Thomas Grainger

@ThomasGrainger我的评论似乎是指向此答案的先前版本,它不再具有这样的意义:)我将其删除。
m90

32

Victor Zamanian回答中选项1的一个问题是,如果未导出类型,则包的用户无法将其声明为函数参数等的类型。解决此问题的一种方法是导出接口而不是结构例如

package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
    Name string
    Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
    return candidate{name, 0}  // enforce the default value here
}

这使我们可以使用导出的Candidate接口声明函数参数类型。从该解决方案中可以看到的唯一缺点是,我们所有的方法都需要在接口定义中声明,但是您可能会认为这是一种好习惯。


可以在调用New函数后更改Name和Votes变量?
morteza khadem '19

不错的简单示例。

小错别字:Votes unit32应该是Votes uint32
PartyLich

@PartyLich很好发现。应该是固定的。
wolfson109 '19

13

有一种使用标记执行此操作的方法,它允许多个默认值。

假设您具有以下结构,带有2个默认标记default0default1

type A struct {
   I int    `default0:"3" default1:"42"`
   S string `default0:"Some String..." default1:"Some Other String..."`
}

现在可以设置默认值。

func main() {

ptr := &A{}

Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...

Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}

是操场上的完整节目

如果您对更复杂的示例感兴趣,例如使用切片和地图,那么请看一下creasty / defaultse


非常感谢!我开始编写与库建议的代码相同的代码,并遇到了这篇文章。它完全符合您的期望(github.com/creasty/defaults)。如果没有值,它将设置默认值,但是如果您为变量分配了一个值,那么它将不分配默认值。它与yaml.v2库一起很好地工作。
诺德


-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}

1
这是不正确的。充其量,您可以在该字段上设置标签值,然后通过反射获得其值,但是即使这样,语法也不正确(缺少反斜杠),并且您只能为字符串类型设置默认值。如果您对本示例具体指的是什么有所了解,请添加一个链接以供参考。
markeissler '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.