57

次のコードがあるとします。

package main

import "fmt"

type Car struct{
    year int
    make string
}

func (c *Car)String() string{
    return fmt.Sprintf("{make:%s, year:%d}", c.make, c.year)
}

func main() {
    myCar := Car{year:1996, make:"Toyota"}
    fmt.Println(myCar)
}

fmt.Println(myCar) を呼び出し、問題のオブジェクトがポインターの場合、String() メソッドが適切に呼び出されます。ただし、オブジェクトが値の場合、出力は Go に組み込まれているデフォルトの書式設定を使用して書式設定され、そのオブジェクトを書式設定するコードは呼び出されません。

興味深いのは、どちらの場合でも myCar.String() を手動で呼び出すと、オブジェクトがポインターまたは値のいずれであっても適切に機能することです。

Println で使用する場合、オブジェクトが値ベースかポインターベースかに関係なく、オブジェクトを希望どおりにフォーマットするにはどうすればよいですか?

String に value メソッドを使用したくありません。これは、呼び出されるたびにオブジェクトがコピーされ、その継ぎ目が不合理であることを意味するためです。また、ダックタイピングシステムに機能させようとしているため、常に手動で .String() を呼び出す必要はありません。

4

6 に答える 6

76

を呼び出すとfmt.Println、関数のシグネチャからわかるようmyCarに、型の値に暗黙的に変換されます。次に、パッケージinterface{}のコードは、次のように、この値を出力する方法を理解するために型の切り替えを行います。fmt

switch v := v.(type) {
case string:
    os.Stdout.WriteString(v)
case fmt.Stringer:
    os.Stdout.WriteString(v.String())
// ...
}

ただし、 が実装されていないため( で定義されているfmt.Stringerため)、このケースは失敗します。手動での呼び出しは機能します。これは、コンパイラが が必要であることを認識し、自動的に に変換するためです。インターフェイスに関しては、手動で行う必要があります。したがって、に実装するか、常にへのポインターを渡す必要があります。CarString*CarStringString*CarmyCar.String()(&myCar).String()StringCarfmt.Println

fmt.Println(&myCar)
于 2013-06-07T07:32:24.917 に答える
27

メソッド

ポインターと値

レシーバーのポインターと値に関するルールは、値メソッドはポインターと値で呼び出すことができますが、ポインター メソッドはポインターでのみ呼び出すことができるということです。これは、ポインター メソッドがレシーバーを変更できるためです。値のコピーでそれらを呼び出すと、それらの変更が破棄されます。

したがって、Stringメソッドがポインターと値の両方で呼び出されたときに機能するようにするには、値レシーバーを使用します。例えば、

package main

import "fmt"

type Car struct {
    year int
    make string
}

func (c Car) String() string {
    return fmt.Sprintf("{make:%s, year:%d}", c.make, c.year)
}

func main() {
    myCar := Car{year: 1996, make: "Toyota"}
    fmt.Println(myCar)
    fmt.Println(&myCar)
}

出力:

{make:Toyota, year:1996}
{make:Toyota, year:1996}
于 2013-06-07T07:52:58.117 に答える
7

ポインター レシーバーで fmt.Stringer を定義します。

package main

import "fmt"

type Car struct {
        year int
        make string
}

func (c *Car) String() string {
        return fmt.Sprintf("{maker:%s, produced:%d}", c.make, c.year)
}

func main() {
        myCar := Car{year: 1996, make: "Toyota"}
        myOtherCar := &Car{year: 2013, make: "Honda"}
        fmt.Println(&myCar)
        fmt.Println(myOtherCar)
}

遊び場


出力:

{maker:Toyota, produced:1996}
{maker:Honda, produced:2013}    

次に、常に Car のインスタンスへのポインターを fmt.Println に渡します。このようにして、コストがかかる可能性のある値のコピーが制御下で回避されます。

于 2013-06-08T10:20:42.603 に答える
-2

一般的に言えば、静的イニシャライザを介して変数に値を代入することは避けるのが最善です。

f := Foo{bar:1,baz:"2"}

これは、まさにあなたが話している苦情を作成できるためです。foo経由でポインターとして渡すのを忘れた場合、&foo または値レシーバーを使用することにした場合、値の多くのクローンを作成することになります。

代わりに、デフォルトで静的イニシャライザにポインタを割り当ててみてください。

f := &Foo{bar:1,baz:"2"}

この方法fは常にポインターであり、値のコピーを取得するのは、値のレシーバーを明示的に使用する場合のみです。

(もちろん、静的イニシャライザからの値を格納したい場合もありますが、それらはエッジ ケースである必要があります)

于 2014-12-26T12:16:52.147 に答える