วิธีตั้งค่าเริ่มต้นใน Go structs


155

มีคำตอบ / เทคนิคหลายประการสำหรับคำถามด้านล่าง:

  1. จะตั้งค่าเริ่มต้นเป็นโครงสร้าง golang ได้อย่างไร?
  2. วิธีการเริ่มต้นโครงสร้างใน golang

ฉันมีคำตอบสองสามข้อ แต่จำเป็นต้องมีการอภิปรายเพิ่มเติม



1
@icza คำตอบของคุณให้วิธีการทำ แต่การดำเนินการตามหัวข้อคำถามจะไม่มีทางที่คล้ายกันหรือค้นหาได้เนื่องจากเป็นคำถามที่เฉพาะเจาะจงมาก ฉันจะเพิ่มลิงค์ในคำตอบของฉันแม้ว่า
Prateek

มีคำถาม 2 ข้อให้เลือก 1 ข้อ สมมติว่าคุณเลือกคำถามแรก (ตามชื่อคำถาม) โปรดระบุให้เจาะจงมากขึ้นเกี่ยวกับการวิจัยก่อนหน้าของคุณและคำตอบอื่น ๆ ของคุณต้องการความเข้าใจเพิ่มเติม
Duncan Jones

คำตอบ:


103

แนวคิดหนึ่งที่เป็นไปได้คือการเขียนฟังก์ชันตัวสร้างแยกต่างหาก

//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
}

7
ใช่นี่เป็นวิธีหนึ่งที่ฉันได้กล่าวไว้ในคำตอบของฉัน แต่ไม่มีทางที่เราจะบังคับให้ใครใช้ฟังก์ชันนี้เท่านั้น
Prateek

@Prateek เป็นสิ่งนี้หรือใช้อินเทอร์เฟซซึ่งจะน่าเกลียดและซับซ้อนเกินไป
OneOfOne

35
@Prateek ใช่คุณสามารถบังคับให้คนอื่นใช้ตัวสร้างนี้ได้ถ้าคุณทำให้ประเภทนั้นไม่ถูกส่งออก คุณสามารถส่งออกฟังก์ชั่นNewSomethingและแม้กระทั่งฟิลด์Textและแต่ก็ไม่ได้ส่งออกประเภทที่DefaultText struct something
Amit Kumar Gupta

1
ปัญหาแย่กว่านั้น ... หากใช้บุคคลที่สาม (ไลบรารีเป็นต้น) เพื่อสร้างอินสแตนซ์โครงสร้างของคุณ (ผ่านreflect.New()เป็นต้น) คาดว่าจะไม่ทราบเกี่ยวกับฟังก์ชันโรงงานที่มีชื่อพิเศษของคุณ ในกรณีนั้นและการเปลี่ยนภาษาสั้น ๆ เองฉันคิดว่ามีเพียงอินเทอร์เฟซ (ที่ไลบรารีตรวจสอบได้)
edam

1
เป็นการดีที่จะตั้งค่าเริ่มต้น แต่บางครั้งฉันอาจต้องการแทนที่ค่าเริ่มต้น ในกรณีนี้ฉันจะไม่สามารถเริ่มต้นโครงสร้างด้วยค่าที่ไม่ใช่ค่าเริ่มต้น น่ารำคาญเล็กน้อยสำหรับฉัน
Juliatzin

71
  1. บังคับเมธอดเพื่อรับโครงสร้าง (วิธีตัวสร้าง)

    จากโพสต์นี้ :

    การออกแบบที่ดีคือการทำให้ประเภทของคุณไม่ถูกส่งออก แต่ให้ฟังก์ชันตัวสร้างที่ส่งออกNewMyType()ซึ่งคุณสามารถเริ่มต้นโครงสร้าง / ประเภทของคุณได้อย่างถูกต้อง ส่งคืนประเภทอินเทอร์เฟซไม่ใช่ประเภทคอนกรีตและอินเทอร์เฟซควรมีทุกสิ่งที่คนอื่นต้องการทำกับคุณค่าของคุณ และประเภทคอนกรีตของคุณต้องใช้อินเทอร์เฟซนั้นแน่นอน

    ซึ่งสามารถทำได้โดยการทำให้ชนิดไม่ถูกส่งออก คุณสามารถส่งออกฟังก์ชัน NewSomething และแม้แต่ช่อง Text และ DefaultText ได้ แต่อย่าส่งออกโครงสร้างบางอย่าง

  2. อีกวิธีหนึ่งในการปรับแต่งสำหรับโมดูลของคุณเองคือการใช้Config struct เพื่อตั้งค่าเริ่มต้น (ตัวเลือกที่ 5 ในลิงค์) ไม่ใช่วิธีที่ดีแม้ว่า


7
ตอนนี้เป็นลิงค์เสีย (404): joneisen.tumblr.com/post/53695478114/golang-and-default-values
Victor Zamanian

3
มีอยู่ในเครื่องย้อนกลับ
n8henrie

FWIW ฉันคิดว่ามันเป็น 'ตัวเลือก 3' - อย่างน้อยก็ในลิงค์เครื่องย้อนกลับ (ไม่มี 'ตัวเลือก 5' อยู่ที่นั่น)
decimus phostle

38

ปัญหาหนึ่งของตัวเลือกที่ 1 ในคำตอบจาก Victor Zamanian คือหากไม่ได้ส่งออกประเภทดังกล่าวผู้ใช้แพ็คเกจของคุณจะไม่สามารถประกาศว่าเป็นประเภทสำหรับพารามิเตอร์ฟังก์ชันได้วิธีหนึ่งในการแก้ไขปัญหานี้คือการส่งออกอินเทอร์เฟซแทน โครงสร้างเช่น

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
}

ซึ่งช่วยให้เราสามารถประกาศประเภทพารามิเตอร์ของฟังก์ชันโดยใช้อินเทอร์เฟซผู้สมัครที่ส่งออก ข้อเสียเดียวที่ฉันเห็นจากโซลูชันนี้คือวิธีการทั้งหมดของเราจำเป็นต้องได้รับการประกาศในคำจำกัดความของอินเทอร์เฟซ แต่คุณสามารถโต้แย้งได้ว่านั่นเป็นแนวทางปฏิบัติที่ดี


19

มีวิธีการทำเช่นนี้กับแท็กซึ่งอนุญาตให้มีค่าเริ่มต้นหลายค่า

สมมติคุณมี struct ต่อไปนี้มี 2 เริ่มต้นแท็กdefault0และdefault1

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
Nordes

3

จากhttps://golang.org/doc/effective_go.html#composite_literals :

บางครั้งค่าศูนย์ไม่ดีพอและจำเป็นต้องมีการเตรียมใช้งานคอนสตรัคเตอร์เนื่องจากในตัวอย่างนี้ได้มาจากระบบปฏิบัติการแพคเกจ

    func NewFile(fd int, name string) *File {
      if fd < 0 {
        return nil
      }
      f := new(File)
      f.fd = fd
      f.name = name
      f.dirinfo = nil
      f.nepipe = 0
      return f
}

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

1
สิ่งนี้ไม่ถูกต้อง อย่างดีที่สุดคุณสามารถตั้งค่าแท็กในฟิลด์นั้นจากนั้นไปที่ค่าของมันด้วยการสะท้อนกลับ แต่ถึงแม้จะเป็นแบบนี้ไวยากรณ์ก็ไม่ถูกต้อง (ไม่มีเครื่องหมายขีดหลัง) และคุณจะสามารถตั้งค่าเริ่มต้นสำหรับประเภทสตริงได้เท่านั้น หากคุณมีข้อมูลเชิงลึกเกี่ยวกับสิ่งที่ตัวอย่างนี้อ้างถึงโดยเฉพาะโปรดเพิ่มลิงก์เพื่ออ้างอิง
markeissler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.