Чи можуть мати додаткові параметри 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
}
}
Для стислість ви можете вказати назву типу параметрів ( дитячий майданчик ):
type OptionFoobar func(*Foobar) error
Якщо вам потрібні обов'язкові параметри, додайте їх як перші аргументи конструктора перед варіантом options
.
Основними перевагами ідіоми функціональних опцій є:
Цей прийом був винайдений Роб Пайком, а також його продемонстрував Дейв Чейні .
func()
необхідними, ніж те, що нав'язує мій мозок навколо цього підходу. Щоразу, коли мені доводиться використовувати такий підхід, наприклад, з бібліотекою Echo, я виявляю, що мій мозок потрапляє в кролячу нору абстракцій. #fwiw
Ні додаткові параметри, ні перевантаження функцій не підтримуються в Go. Go підтримує змінну кількість параметрів: передача аргументів до ... параметрів
Ні - ні. Per від 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 ->
Я в кінцевому підсумку використовував комбінацію структури парам і варіативних арг. Таким чином, мені не довелося змінювати існуючий інтерфейс, який споживав декілька сервісів, і моя служба змогла передавати додаткові параметри за потребою. Зразок коду на майданчику голанг: 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
}