Вопрос
Может ли Go иметь необязательные параметры?Или я могу просто определить две функции с одинаковым именем и разным количеством аргументов?
Решение
Go не имеет необязательных параметров он также не поддерживает перегрузку методов:
Отправка метода упрощается, если ему не нужно выполнять сопоставление типов, как хорошо.Опыт работы с другими языками подсказал нам, что наличие множества методов с одинаковым именем, но разными сигнатурами иногда бывает полезным, но это также может быть запутанным и хрупким на практике.Соответствие только по названию и требование согласованности в типах было основным упрощающим решением в системе типов Go .
Другие советы
Хороший способ добиться чего-то вроде необязательных параметров - это использовать переменные аргументы.Функция фактически получает фрагмент любого указанного вами типа.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
Вы можете использовать структуру, которая включает в себя параметры:
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})
Для произвольного, потенциально большого количества необязательных параметров хорошей идиомой является использование Функциональные возможности.
Для вашего типа Foobar
, сначала напишите только один конструктор:
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
}
где каждая опция - это функция, которая изменяет Foobar.Затем предоставьте вашему пользователю удобные способы использования или создания стандартных параметров, например :
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
}
}
Для краткости вы можете присвоить имя типу параметров (Игровая площадка) :
type OptionFoobar func(*Foobar) error
Если вам нужны обязательные параметры, добавьте их в качестве первых аргументов конструктора перед переменной options
.
Основные преимущества Функциональные возможности идиомы - это :
- ваш API может расти с течением времени, не нарушая существующий код, потому что сигнатура конструктора остается неизменной, когда требуются новые параметры.
- это позволяет использовать вариант использования по умолчанию как самый простой:вообще никаких аргументов!
- это обеспечивает точный контроль над инициализацией сложных значений.
Эта техника была придумана Роб Пайк а также продемонстрированный Дэйв Чейни.
В Go не поддерживаются ни необязательные параметры, ни перегрузка функций.Go поддерживает переменное количество параметров: Передача аргументов в ...параметры
Нет ... ни то, ни другое.В соответствии с Выбирайте программистов на C ++ Документы,
Go не поддерживает функцию перегрузку и не поддерживает пользовательские операторы определенные пользователем.
Я не могу найти столь же четкого утверждения о том, что необязательные параметры не поддерживаются, но они также не поддерживаются.
Вы можете довольно красиво инкапсулировать это в функцию, аналогичную приведенной ниже.
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
}
В этом примере приглашение по умолчанию содержит двоеточие и пробел перед ним ...
:
...однако вы можете переопределить это, предоставив параметр функции prompt.
prompt("Input here -> ")
Это приведет к появлению запроса, подобного приведенному ниже.
Input here ->
В итоге я использовал комбинацию структуры параметров и переменных аргументов.Таким образом, мне не пришлось изменять существующий интерфейс, который использовался несколькими сервисами, и мой сервис смог передавать дополнительные параметры по мере необходимости.Пример кода в golang playground: https://play.golang.org/p/G668FA97Nu
Я немного опоздал, но если вам нравится свободный интерфейс, вы могли бы разработать свои сеттеры для связанных вызовов, подобных этому:
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
}
А потом назовите это вот так:
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)
}
}
Это похоже на Функциональные возможности идиома представлена в ответе @Ripounet и обладает теми же преимуществами, но имеет некоторые недостатки:
- Если возникает ошибка, она не будет немедленно прервана, таким образом, было бы немного менее эффективно, если бы вы ожидали, что ваш конструктор будет часто сообщать об ошибках.
- Вам придется потратить строку, объявляющую
err
переменная и обнуление ее.
Однако есть возможное небольшое преимущество: этот тип вызовов функций должен быть проще встроить компилятору, но я действительно не специалист.
Go language не поддерживает перегрузку методов, но вы можете использовать переменные аргументы точно так же, как необязательные параметры, также вы можете использовать interface{} в качестве параметра, но это не лучший выбор.
Вы можете передавать произвольные именованные параметры вместе с картой.
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})
}