web-dev-qa-db-ja.com

オプションのパラメータ

Goはオプションのパラメータを持つことができますか?それとも、同じ名前で引数の数が異なる2つの関数を定義するだけでいいのですか?

365
devyn

Goはオプションのパラメータを持っていません メソッドのオーバーロードをサポートしていません

型のマッチングも必要ないのであれば、メソッドのディスパッチは単純化されます。他の言語での経験から、同じ名前で異なるシグネチャを持つさまざまな方法を使用すると便利な場合があることがわかりましたが、実際には混乱を招き、脆弱になる可能性もあります。名前だけで照合し、型の一貫性を要求することは、Goの型システムでは単純化された大きな決定でした。

359
Andrew Hare

オプションのパラメータのようなものを実現するための良い方法は、可変引数を使うことです。この関数は、実際には指定した型のスライスを受け取ります。

func foo(params ...int) {
    fmt.Println(len(params))
}

func main() {
    foo()
    foo(1)
    foo(1,2,3)
}
176
Ferguzz

パラメータを含む構造体を使うことができます。

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})
139
deamon

任意の、潜在的に多数のオプションパラメータに対して、Niceという慣用句は機能オプションを使うことです。

あなたの型Foobarには、まずコンストラクタを1つだけ書いてください。

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の前にコンストラクタの最初の引数として追加します。

機能オプションイディオムの主な利点は次のとおりです。

  • 新しいオプションが必要になってもコンストラクタのシグネチャは変わらないため、既存のコードを壊すことなくAPIを徐々に拡張できます。
  • デフォルトのユースケースを最も単純にすることができます。引数はまったくありません。
  • 複雑な値の初期化を細かく制御できます。

このテクニックは Rob Pike によって造られ、 Dave Cheney によっても実証されました。

100
Deleplace

Goでは、オプションのパラメータも関数のオーバーロードもサポートされていません。 Goは可変数のパラメータをサポートしています。 ...引数に引数を渡す

16
peterSO

いいえ - どちらでもない。によると C++プログラマのために行く ドキュメント、

Goは関数のオーバーロードをサポートしておらず、ユーザー定義演算子もサポートしていません。

オプションのパラメータはサポートされていないという同じように明確な文を見つけることはできませんが、それらもサポートされていません。

5
Alex Martelli

以下のような機能でこれを非常にうまくカプセル化できます。

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 ->
4
Jam Risser

Go言語はメソッドのオーバーロードをサポートしていませんが、オプションのパラメータと同じように可変引数を使用できます。また、パラメータとしてinterface {}を使用することもできますが、これは適切な選択ではありません。

2
BaSO4

結局、paramsとvariadic argsの構造の組み合わせを使うことになりました。この方法で、私はいくつかのサービスによって消費されていた既存のインターフェースを変更する必要はなく、私のサービスは必要に応じて追加のパラメータを渡すことができました。遊び場でのサンプルコード: https://play.golang.org/p/G668FA97N

2
Adriana

マップで任意の名前付きパラメーターを渡すことができます。

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})
}
1
nobar

私は少し遅れています、しかしあなたが流暢なインターフェースを好むならば、あなたはこのような連鎖された呼び出しのためにあなたのセッターを設計するかもしれません:

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の回答で示された機能オプションイディオムに似ており、同じ利点を享受しますが、いくつかの欠点があります。

  1. エラーが発生してもすぐには中止されません。そのため、コンストラクタがエラーを頻繁に報告することが予想される場合は、やや効率が低下します。
  2. err変数を宣言してゼロにする行を費やす必要があります。

ただし、小さな利点もあります。この種の関数呼び出しは、コンパイラーにとってインライン化するほうが簡単なはずですが、私は本当に専門家ではありません。

1
VinGarcia