Go può avere parametri opzionali? O posso semplicemente definire due funzioni con lo stesso nome e un diverso numero di argomenti?
Go può avere parametri opzionali? O posso semplicemente definire due funzioni con lo stesso nome e un diverso numero di argomenti?
Risposte:
Go non ha parametri opzionali né supporta il sovraccarico del metodo :
L'invio del metodo è semplificato se non è necessario eseguire anche la corrispondenza del tipo. L'esperienza con altre lingue ci ha detto che avere una varietà di metodi con lo stesso nome ma firme diverse era occasionalmente utile ma che poteva anche essere confuso e fragile nella pratica. La corrispondenza solo per nome e la necessità di coerenza nei tipi è stata una decisione di semplificazione importante nel sistema di tipi di Go.
make
un caso speciale, allora? O non è nemmeno realmente implementato come funzione ...
make
è un costrutto di linguaggio e le regole sopra menzionate non si applicano. Vedi questa domanda correlata .
range
è lo stesso caso make
, in tal senso
Un buon modo per ottenere qualcosa come parametri opzionali è usare args variadici. La funzione riceve effettivamente una porzione di qualunque tipo specificato.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
è una fetta di
È possibile utilizzare una struttura che include i parametri:
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})
Per un numero arbitrario, potenzialmente elevato, di parametri opzionali, un buon linguaggio è usare le opzioni funzionali .
Per il tuo tipo Foobar
, prima scrivi un solo costruttore:
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
}
dove ogni opzione è una funzione che muta il Foobar. Quindi fornire modi convenienti per l'utente di utilizzare o creare opzioni standard, ad esempio:
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
}
}
Per semplicità, puoi dare un nome al tipo di opzioni ( Playground ):
type OptionFoobar func(*Foobar) error
Se hai bisogno di parametri obbligatori, aggiungili come primi argomenti del costruttore prima del variadic options
.
I principali vantaggi del linguaggio delle opzioni funzionali sono:
Questa tecnica è stata coniata da Rob Pike e dimostrata anche da Dave Cheney .
func()
s, se necessario, piuttosto che piegare il mio cervello attorno a questo approccio. Ogni volta che devo usare questo approccio, ad esempio con la libreria Echo, trovo il mio cervello intrappolato nella tana del coniglio delle astrazioni. #fwiw
Né i parametri opzionali né il sovraccarico delle funzioni sono supportati in Go. Go supporta un numero variabile di parametri: passare argomenti a ... parametri
No - nessuno dei due. Per i documenti dei programmatori Go for C ++ ,
Go non supporta il sovraccarico delle funzioni e non supporta gli operatori definiti dall'utente.
Non riesco a trovare un'affermazione altrettanto chiara del fatto che i parametri opzionali non sono supportati, ma non sono supportati neanche.
Puoi incapsularlo abbastanza bene in una funzione simile a quella che segue.
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 questo esempio, il prompt per impostazione predefinita ha due punti e uno spazio davanti. . .
:
. . . tuttavia è possibile sovrascriverlo fornendo un parametro alla funzione prompt.
prompt("Input here -> ")
Ciò si tradurrà in un prompt come di seguito.
Input here ->
Ho finito per usare una combinazione di una struttura di parametri e argomenti vari. In questo modo, non ho dovuto modificare l'interfaccia esistente utilizzata da numerosi servizi e il mio servizio è stato in grado di passare parametri aggiuntivi in base alle esigenze. Codice di esempio nel parco giochi Golang: https://play.golang.org/p/G668FA97Nu
Sono un po 'in ritardo, ma se ti piace un'interfaccia fluida potresti progettare i tuoi setter per chiamate concatenate come questa:
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
}
E poi chiamalo così:
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)
}
}
Questo è simile al linguaggio delle opzioni funzionali presentato sulla risposta di @Ripounet e gode degli stessi vantaggi ma presenta alcuni svantaggi:
err
variabile e azzerarla.Vi è, tuttavia, un possibile piccolo vantaggio, questo tipo di chiamate di funzione dovrebbe essere più facile da integrare per il compilatore, ma non sono davvero uno specialista.
È possibile passare parametri nominali arbitrari con una mappa.
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})
}
Un'altra possibilità sarebbe quella di utilizzare una struttura che con un campo per indicare se è valida. I tipi null da sql come NullString sono convenienti. È bello non dover definire il proprio tipo, ma nel caso in cui sia necessario un tipo di dati personalizzato è sempre possibile seguire lo stesso modello. Penso che l'opzione opzionale sia chiara dalla definizione della funzione e ci sia un minimo di codice o sforzo in più.
Come esempio:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}