Kann Go optionale Parameter haben? Oder kann ich einfach zwei Funktionen mit demselben Namen und einer unterschiedlichen Anzahl von Argumenten definieren?
Kann Go optionale Parameter haben? Oder kann ich einfach zwei Funktionen mit demselben Namen und einer unterschiedlichen Anzahl von Argumenten definieren?
Antworten:
Go verfügt weder über optionale Parameter noch unterstützt es das Überladen von Methoden :
Der Methodenversand wird vereinfacht, wenn kein Typabgleich durchgeführt werden muss. Die Erfahrung mit anderen Sprachen hat gezeigt, dass es gelegentlich nützlich ist, verschiedene Methoden mit demselben Namen, aber unterschiedlichen Signaturen zu verwenden, aber dass dies in der Praxis auch verwirrend und fragil sein kann. Nur nach Namen zu suchen und Konsistenz in den Typen zu erfordern, war eine wichtige vereinfachende Entscheidung im Typensystem von Go.
make
ein Sonderfall? Oder ist es nicht einmal wirklich als Funktion implementiert ...
make
ist ein Sprachkonstrukt und die oben genannten Regeln gelten nicht. Siehe diese verwandte Frage .
range
ist der gleiche Fall wie make
in diesem Sinne
Ein guter Weg, um so etwas wie optionale Parameter zu erreichen, ist die Verwendung verschiedener Argumente. Die Funktion empfängt tatsächlich ein Slice des von Ihnen angegebenen Typs.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
ist ein Stück Ints
Sie können eine Struktur verwenden, die die folgenden Parameter enthält:
type Params struct {
a, b, c int
}
func doIt(p Params) int {
return p.a + p.b + p.c
}
// you can call it without specifying all parameters
doIt(Params{a: 1, c: 9})
Für eine beliebige, möglicherweise große Anzahl optionaler Parameter ist es eine gute Redewendung, funktionale Optionen zu verwenden .
Foobar
Schreiben Sie für Ihren Typ zunächst nur einen Konstruktor:
func NewFoobar(options ...func(*Foobar) error) (*Foobar, error){
fb := &Foobar{}
// ... (write initializations with default values)...
for _, op := range options{
err := op(fb)
if err != nil {
return nil, err
}
}
return fb, nil
}
Dabei ist jede Option eine Funktion, die die Foobar mutiert. Bieten Sie Ihrem Benutzer dann bequeme Möglichkeiten, Standardoptionen zu verwenden oder zu erstellen, zum Beispiel:
func OptionReadonlyFlag(fb *Foobar) error {
fb.mutable = false
return nil
}
func OptionTemperature(t Celsius) func(*Foobar) error {
return func(fb *Foobar) error {
fb.temperature = t
return nil
}
}
Aus Gründen der Übersichtlichkeit können Sie dem Typ der Optionen ( Spielplatz ) einen Namen geben :
type OptionFoobar func(*Foobar) error
Wenn Sie obligatorische Parameter benötigen, fügen Sie diese als erste Argumente des Konstruktors vor der Variadik hinzu options
.
Die Hauptvorteile der Funktionsoptionssprache sind:
Diese Technik wurde von Rob Pike geprägt und auch von Dave Cheney demonstriert .
func()
s sein könnten , als dass dies mein Gehirn um diesen Ansatz biegt. Immer wenn ich diesen Ansatz verwenden muss, beispielsweise bei der Echo-Bibliothek, gerät mein Gehirn in das Kaninchenloch der Abstraktionen. #fwiw
In Go werden weder optionale Parameter noch Funktionsüberladung unterstützt. Go unterstützt eine variable Anzahl von Parametern: Übergeben von Argumenten an ... Parameter
Nein auch nicht. Gemäß den Dokumenten für Go for C ++ - Programmierer ,
Go unterstützt keine Funktionsüberladung und keine benutzerdefinierten Operatoren.
Ich kann keine ebenso klare Aussage finden, dass optionale Parameter nicht unterstützt werden, aber sie werden auch nicht unterstützt.
Sie können dies ganz gut in einer Funktion zusammenfassen, die der folgenden ähnelt.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Println(prompt())
}
func prompt(params ...string) string {
prompt := ": "
if len(params) > 0 {
prompt = params[0]
}
reader := bufio.NewReader(os.Stdin)
fmt.Print(prompt)
text, _ := reader.ReadString('\n')
return text
}
In diesem Beispiel enthält die Eingabeaufforderung standardmäßig einen Doppelpunkt und ein Leerzeichen. . .
:
. . . Sie können dies jedoch überschreiben, indem Sie der Eingabeaufforderungsfunktion einen Parameter zuweisen.
prompt("Input here -> ")
Dies führt zu einer Eingabeaufforderung wie unten.
Input here ->
Am Ende habe ich eine Kombination aus einer Struktur von Parametern und verschiedenen Argumenten verwendet. Auf diese Weise musste ich die vorhandene Schnittstelle, die von mehreren Diensten verwendet wurde, nicht ändern, und mein Dienst konnte bei Bedarf zusätzliche Parameter übergeben. Beispielcode auf dem Golang-Spielplatz: https://play.golang.org/p/G668FA97Nu
Ich bin etwas spät dran, aber wenn Sie eine flüssige Benutzeroberfläche mögen, können Sie Ihre Setter für verkettete Anrufe wie folgt entwerfen:
type myType struct {
s string
a, b int
}
func New(s string, err *error) *myType {
if s == "" {
*err = errors.New(
"Mandatory argument `s` must not be empty!")
}
return &myType{s: s}
}
func (this *myType) setA (a int, err *error) *myType {
if *err == nil {
if a == 42 {
*err = errors.New("42 is not the answer!")
} else {
this.a = a
}
}
return this
}
func (this *myType) setB (b int, _ *error) *myType {
this.b = b
return this
}
Und dann nenne es so:
func main() {
var err error = nil
instance :=
New("hello", &err).
setA(1, &err).
setB(2, &err)
if err != nil {
fmt.Println("Failed: ", err)
} else {
fmt.Println(instance)
}
}
Dies ähnelt der in der Antwort von @Ripounet dargestellten Redewendung für funktionale Optionen und bietet dieselben Vorteile, weist jedoch einige Nachteile auf:
err
Variable zu deklarieren und auf Null zu setzen.Es gibt jedoch einen möglichen kleinen Vorteil: Diese Art von Funktionsaufrufen sollte für den Compiler einfacher zu inline sein, aber ich bin wirklich kein Spezialist.
Sie können beliebige benannte Parameter mit einer Karte übergeben.
type varArgs map[string]interface{}
func myFunc(args varArgs) {
arg1 := "default" // optional default value
if val, ok := args["arg1"]; ok {
// value override or other action
arg1 = val.(string) // runtime panic if wrong type
}
arg2 := 123 // optional default value
if val, ok := args["arg2"]; ok {
// value override or other action
arg2 = val.(int) // runtime panic if wrong type
}
fmt.Println(arg1, arg2)
}
func Test_test() {
myFunc(varArgs{"arg1": "value", "arg2": 1234})
}
Eine andere Möglichkeit wäre, eine Struktur zu verwenden, die mit einem Feld angibt, ob sie gültig ist. Die Nulltypen von SQL wie NullString sind praktisch. Es ist schön, keinen eigenen Typ definieren zu müssen, aber falls Sie einen benutzerdefinierten Datentyp benötigen, können Sie immer dem gleichen Muster folgen. Ich denke, die Optionalität ergibt sich aus der Funktionsdefinition und es gibt nur minimalen zusätzlichen Code oder Aufwand.
Als Beispiel:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}