Go peut-il avoir des paramètres facultatifs? Ou puis-je simplement définir deux fonctions avec le même nom et un nombre d'arguments différent?
Go peut-il avoir des paramètres facultatifs? Ou puis-je simplement définir deux fonctions avec le même nom et un nombre d'arguments différent?
Réponses:
Go n'a pas de paramètres facultatifs et ne prend pas en charge la surcharge de méthode :
L'envoi de méthode est simplifié s'il n'a pas besoin de faire de correspondance de type également. L'expérience d'autres langues nous a montré que le fait d'avoir une variété de méthodes portant le même nom mais des signatures différentes était parfois utile, mais cela pouvait aussi être déroutant et fragile dans la pratique. Faire correspondre uniquement par nom et nécessiter la cohérence des types était une décision de simplification majeure dans le système de types de Go.
make
un cas particulier? Ou n'est-il même pas vraiment implémenté en fonction…
make
est une construction de langage et les règles mentionnées ci-dessus ne s'appliquent pas. Voir cette question connexe .
range
est le même cas que make
, en ce sens
Une bonne façon de réaliser quelque chose comme des paramètres facultatifs est d'utiliser des arguments variadiques. La fonction reçoit en fait une tranche du type que vous spécifiez.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
est une tranche d'ints
Vous pouvez utiliser une structure qui inclut les paramètres:
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})
Pour un nombre arbitraire et potentiellement important de paramètres facultatifs, un idiome agréable consiste à utiliser les options fonctionnelles .
Pour votre type Foobar
, écrivez d'abord un seul constructeur:
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
}
où chaque option est une fonction qui mute le Foobar. Ensuite, fournissez des moyens pratiques à votre utilisateur d'utiliser ou de créer des options standard, par exemple:
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
}
}
Par souci de concision, vous pouvez donner un nom au type des options ( Playground ):
type OptionFoobar func(*Foobar) error
Si vous avez besoin de paramètres obligatoires, ajoutez-les comme premiers arguments du constructeur avant le variadic options
.
Les principaux avantages de l' idiome des options fonctionnelles sont les suivants:
Cette technique a été inventée par Rob Pike et également démontrée par Dave Cheney .
func()
s si besoin est, que de plier mon cerveau autour de cette approche. Chaque fois que je dois utiliser cette approche, comme avec la bibliothèque Echo, je trouve mon cerveau pris dans le trou du lapin des abstractions. #fwiw
Ni les paramètres facultatifs ni la surcharge de fonctions ne sont pris en charge dans Go. Go prend en charge un nombre variable de paramètres: Passer des arguments à ... paramètres
Non - non plus. Selon les documents des programmeurs Go for C ++ ,
Go ne prend pas en charge la surcharge de fonctions et ne prend pas en charge les opérateurs définis par l'utilisateur.
Je ne trouve pas de déclaration aussi claire que les paramètres facultatifs ne sont pas pris en charge, mais ils ne sont pas non plus pris en charge.
Vous pouvez l'encapsuler assez bien dans une fonction similaire à celle ci-dessous.
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
}
Dans cet exemple, l'invite par défaut a deux points et un espace devant elle. . .
:
. . . cependant, vous pouvez remplacer cela en fournissant un paramètre à la fonction d'invite.
prompt("Input here -> ")
Cela se traduira par une invite comme ci-dessous.
Input here ->
J'ai fini par utiliser une combinaison d'une structure de paramètres et d'arguments variadiques. De cette façon, je n'ai pas eu à changer l'interface existante qui était consommée par plusieurs services et mon service a pu passer des paramètres supplémentaires selon les besoins. Exemple de code dans la cour de récréation de Golang: https://play.golang.org/p/G668FA97Nu
Je suis un peu en retard, mais si vous aimez une interface fluide, vous pouvez concevoir vos setters pour des appels en chaîne comme celui-ci:
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
}
Et puis appelez-le comme ceci:
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)
}
}
Ceci est similaire à l' idiome des options fonctionnelles présenté sur la réponse @Ripounet et bénéficie des mêmes avantages mais présente certains inconvénients:
err
variable et à la mettre à zéro.Il y a cependant un petit avantage possible, ce type d'appels de fonction devrait être plus facile pour le compilateur à s'aligner mais je ne suis vraiment pas un spécialiste.
Vous pouvez passer des paramètres nommés arbitraires avec une carte.
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})
}
Une autre possibilité serait d'utiliser une structure qui avec un champ pour indiquer si elle est valide. Les types nuls de sql tels que NullString sont pratiques. C'est bien de ne pas avoir à définir votre propre type, mais si vous avez besoin d'un type de données personnalisé, vous pouvez toujours suivre le même modèle. Je pense que l'option-ness est claire à partir de la définition de la fonction et il y a un minimum de code ou d'effort supplémentaire.
Par exemple:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}