Может ли Go иметь дополнительные параметры? Или я могу просто определить две функции с одинаковым именем и разным количеством аргументов?
Может ли Go иметь дополнительные параметры? Или я могу просто определить две функции с одинаковым именем и разным количеством аргументов?
Ответы:
Go не имеет необязательных параметров и не поддерживает перегрузку методов :
Диспетчеризация методов упрощается, если не требуется выполнять сопоставление типов. Опыт работы с другими языками показал нам, что наличие различных методов с одинаковыми именами, но разными сигнатурами иногда полезно, но на практике это также может быть запутанным и хрупким. Сопоставление только по имени и требование согласованности типов было основным упрощающим решением в системе типов Go.
make
особый случай? Или это даже не реализовано как функция ...
make
является языковой конструкцией, и упомянутые выше правила не применяются. Смотрите этот связанный вопрос .
range
это тот же случай make
, что и в этом смысле
Хороший способ получить что-то вроде необязательных параметров - это использовать переменные аргументы. Функция фактически получает фрагмент любого типа, который вы укажете.
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}
params
это кусочек целых
Вы можете использовать структуру, которая включает параметры:
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
}
}
Для краткости вы можете дать название типу опций ( Playground ):
type OptionFoobar func(*Foobar) error
Если вам нужны обязательные параметры, добавьте их в качестве первых аргументов конструктора перед переменным options
.
Основными преимуществами функциональных опций являются:
Эта техника была придумана Робом Пайком, а также продемонстрирована Дейвом Чейни .
func()
случае необходимости, чем это искажает мой разум вокруг этого подхода. Всякий раз, когда мне приходится использовать этот подход, например, с библиотекой Echo, я обнаруживаю, что мой мозг попадает в кроличью нору абстракций. #fwiw
Ни необязательные параметры, ни перегрузка функций не поддерживаются в Go. 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("Input here -> ")
Это приведет к подсказке, как показано ниже.
Input here ->
В итоге я использовал комбинацию структуры params и variadic args. Таким образом, мне не пришлось менять существующий интерфейс, который использовался несколькими сервисами, и мой сервис мог передавать дополнительные параметры по мере необходимости. Пример кода на игровой площадке Golang: 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
переменную и обнуляющую ее.Однако, есть небольшое преимущество: вызовы функций такого типа должны быть проще для встроенного компилятора, но я действительно не специалист.
Вы можете передать произвольные именованные параметры с картой.
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})
}
Другая возможность - использовать структуру с полем, чтобы указать, является ли она действительной. Нулевые типы из sql, такие как NullString , удобны. Приятно, что вам не нужно определять свой собственный тип, но в случае, если вам нужен собственный тип данных, вы всегда можете следовать одному и тому же шаблону. Я думаю, что необязательность понятна из определения функции и требует минимального дополнительного кода или усилий.
Например:
func Foo(bar string, baz sql.NullString){
if !baz.Valid {
baz.String = "defaultValue"
}
// the rest of the implementation
}