So legen Sie Standardwerte in Go-Strukturen fest


143

Es gibt mehrere Antworten / Techniken auf die folgende Frage:

  1. Wie setze ich Standardwerte für Golang-Strukturen?
  2. So initialisieren Sie Strukturen in Golang

Ich habe ein paar Antworten, aber weitere Diskussionen sind erforderlich.


2
Verwandte Themen
icza

@icza Ihre Antwort gibt zwar eine Möglichkeit, dies zu tun, aber nach dem Fragentitel ist es in keiner Weise ähnlich oder durchsuchbar, da es sich um eine sehr spezifische Frage handelt. Ich werde den Link jedoch in meine Antwort einfügen.
Prateek

Hier gibt es zwei Fragen, wählen Sie eine aus. Angenommen, Sie entscheiden sich für die erste Frage (gemäß Fragetitel), geben Sie bitte Ihre frühere Forschung genauer an und wo Ihre anderen Antworten mehr Diskussion erfordern.
Duncan Jones

Antworten:


96

Eine mögliche Idee besteht darin, eine separate Konstruktorfunktion zu schreiben

//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
Ja, dies ist eine der Möglichkeiten, die ich auch in meiner Antwort erwähnt habe, aber wir können niemanden zwingen, nur diese Funktion zu verwenden.
Prateek

@Prateek ist es entweder dies oder verwenden Sie eine Schnittstelle, die hässlich und überkompliziert wäre.
OneOfOne

31
@Prateek Ja, Sie können Benutzer zwingen, diesen Konstruktor zu verwenden, wenn Sie den Typ selbst einfach nicht exportieren. Sie können die Funktion NewSomethingund sogar die Felder Textund DefaultTextexportieren, aber den Strukturtyp nicht exportieren something.
Amit Kumar Gupta

1
Das Problem ist schlimmer ... Wenn ein Dritter (z. B. eine Bibliothek) zum Instanziieren Ihrer Struktur verwendet wird ( reflect.New()z. B. über), kann nicht erwartet werden, dass er über Ihre speziell benannte Factory-Funktion Bescheid weiß. In diesem Fall und ohne die Sprache selbst zu ändern, würde meiner Meinung nach nur eine Schnittstelle (nach der die Bibliothek suchen könnte) ausreichen.
Edam

1
Es ist gut, die Standardeinstellung festzulegen, aber manchmal möchte ich die Standardeinstellung überschreiben. In diesem Fall kann ich eine Struktur nicht mit einem Wert initialisieren, der nicht der Standardwert ist. ein wenig nervig für mich
Juliatzin

68
  1. Erzwingen Sie eine Methode, um die Struktur abzurufen (den Konstruktorweg).

    Ein gutes Design besteht darin, Ihren Typ nicht zu exportieren, aber eine exportierte Konstruktorfunktion wie NewMyType () bereitzustellen, mit der Sie Ihre Struktur / Ihren Typ ordnungsgemäß initialisieren können. Geben Sie auch einen Schnittstellentyp und keinen konkreten Typ zurück, und die Schnittstelle sollte alles enthalten, was andere mit Ihrem Wert tun möchten. Und Ihr konkreter Typ muss diese Schnittstelle natürlich implementieren.

    Dies kann erreicht werden, indem der Typ selbst einfach nicht exportiert wird. Sie können die Funktion NewSomething und sogar die Felder Text und DefaultText exportieren, aber den Strukturtyp nicht exportieren

  2. Eine andere Möglichkeit, es für Ihr eigenes Modul anzupassen, ist die Verwendung einer Konfigurationsstruktur zum Festlegen von Standardwerten (Option 5 im Link). Dies ist jedoch keine gute Möglichkeit.



3
Es ist in der Wayback-Maschine verfügbar .
n8henrie

FWIW, ich denke es ist 'Option 3' - zumindest in der Wayback-Maschinenverbindung. (Dort gibt es keine 'Option 5').
Dezimus-Apostel

@ m90 um Golint zum Schweigen zu bringen, können Sie Ihre Funktion als Rückgabe des öffentlichen Schnittstellentyps deklarieren
Thomas Grainger

@ThomasGrainger Mein Kommentar scheint sich auf eine frühere Überarbeitung dieser Antwort zu beziehen, es macht so keinen Sinn mehr :) Ich werde es einfach löschen.
M90

32

Ein Problem mit Option 1 als Antwort von Victor Zamanian ist, dass Benutzer Ihres Pakets, wenn der Typ nicht exportiert wird, ihn nicht als Typ für Funktionsparameter usw. deklarieren können. Eine Möglichkeit, dies zu umgehen, besteht darin, eine Schnittstelle anstelle der zu exportieren Struktur zB

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
}

Damit können wir Funktionsparametertypen über die exportierte Kandidatenschnittstelle deklarieren. Der einzige Nachteil, den ich aus dieser Lösung ersehen kann, ist, dass alle unsere Methoden in der Schnittstellendefinition deklariert werden müssen, aber Sie könnten argumentieren, dass dies sowieso eine gute Praxis ist.


ist verfügbar, um die Variable Name und Stimmen nach dem Aufruf der neuen Funktion zu ändern?
Morteza Khadem

Schönes einfaches Beispiel.

kleiner Tippfehler: Votes unit32sollte wahrscheinlich seinVotes uint32
PartyLich

@PartyLich gut entdeckt. Sollte behoben werden.
Wolfson 109

13

Es gibt eine Möglichkeit, dies mit Tags zu tun, die mehrere Standardeinstellungen zulassen.

Angenommen, Sie haben die folgende Struktur mit 2 Standard-Tags default0 und default1 .

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

Jetzt ist es möglich, die Standardeinstellungen festzulegen.

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

Hier ist das komplette Programm auf einem Spielplatz .

Wenn Sie an einem komplexeren Beispiel interessiert sind, z. B. mit Slices und Maps, dann werfen Sie einen Blick auf creasty / defaultse


Vielen Dank! Ich fing an, den gleichen Code zu schreiben, den die Bibliothek vorgeschlagen hatte, und stieß auf diesen Beitrag. Es macht genau das, was Sie erwarten ( github.com/creasty/defaults ). Wenn Sie keinen Wert haben, wird der Standardwert festgelegt. Wenn Sie Ihrer Variablen jedoch einen Wert zugewiesen haben, wird der Standardwert nicht zugewiesen. Es funktioniert ziemlich gut mit der yaml.v2-Bibliothek.
Nordes

3

Von https://golang.org/doc/effective_go.html#composite_literals :

Manchmal ist der Nullwert nicht gut genug und ein initialisierender Konstruktor ist erforderlich, wie in diesem Beispiel aus dem Paket os abgeleitet.

    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
Das ist falsch. Im besten Fall können Sie einen Tag-Wert für dieses Feld festlegen und dann mit Reflektion zu seinem Wert gelangen. Trotzdem ist die Syntax falsch (fehlende Back Ticks) und Sie können nur einen Standardwert für einen Zeichenfolgentyp festlegen. Wenn Sie einen Einblick haben, worauf sich dieses Beispiel speziell bezieht, fügen Sie bitte einen Link hinzu, auf den Sie verweisen können.
markeissler
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.