从 C 到 golang

方法与指针

Go中没有类的概念,用结构体承担相应的工作

为任意类型声明方法 func (t Type) Abs() float64

package main

import (
    "fmt"
    "math"
)

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

func main() {
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}

输出
1.4142135623730951

为任意类型添加方法时,区分(t Type)和 (t *Type),前者为对象的拷贝,后者为对象的引用,需要修改时需要用后者,见下例

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale1(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}
func (v Vertex) Scale2(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}
func main() {
    v1 := Vertex{3, 4}
    v1.Scale1(10)
    
    v2 := Vertex{3, 4}
    v2.Scale2(10)
    fmt.Println(v1.Abs())
    fmt.Println(v2.Abs())
}

输出
50
5

区分go中的方法(methods)与函数(function)

package main

import (
    "fmt"
    "math"
)
type Vertex struct {
    X, Y float64
}
func Abs1(v Vertex) float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Abs2() float64{
    v.X = 1
    v.Y = 2
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
    v := Vertex{3, 4}
    fmt.Println(Abs1(v))
    fmt.Println(v.Abs2())
    fmt.Println(Abs1(v))
}

输出
5
2.23606797749979
2.23606797749979

使用指针来更有效地进行操作结构体

package main

import (
    "fmt"
)

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {
    v := &Vertex{3, 4}
    v.Scale(5)
    fmt.Println(v)
    fmt.Println(v.X,v.Y)
}

输出
&{15 20}
15 20

类型通过实现一个接口的所有方法来实现该接口

package main

import "fmt"

type I interface {
    M()
}

type T struct {
    S string
}

// 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。
func (t T) M() {
    fmt.Println(t.S)
}

func main() {
    var i I = T{"hello"}
    i.M()
}

输出
hello

空接口可以给任意值,所以常用空接口来处理未知类型

var i interface{} = "anyString"
package main

import "fmt"

func main() {
    var i interface{}
    describe(i)

    i = 42
    describe(i)

    i = "hello"
    describe(i)
}

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}

输出
(<nil>, <nil>)
(42, int)
(hello, string)

使用断言判断接口是否保存了某种类型,因为见上一条,接口可保存任意数据类型,格式如t := i.(T)

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s := i.(string)
    fmt.Println(s)

    s, ok := i.(string)
    fmt.Println(s, ok)

    f, ok := i.(float64)
    fmt.Println(f, ok)

    f = i.(float64) // 报错(panic)
    fmt.Println(f)
}

接口类型判断,接近于断言的语法(改T为关键字type),使用i.(type)

package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(21)
    do("hello")
    do(true)
}

输出
Twice 21 is 42
"hello" is 5 bytes long
I don't know about type bool!

fmt包中定义Stringer接口,通过实现该接口中的String()方法可以修改fmt.Println中的输出内容

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
    a := Person{"Arthur Dent", 42}
    z := Person{"Zaphod Beeblebrox", 9001}
    fmt.Println(a, z)
}

输出
Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)

类似于Stringer接口,同样内置一个error接口,如下

type error interface {
    Error() string
}

使用时需要做非空判断,注意其中的run方法,接口的犀利之处

package main

import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

//注意该方法因为error的返回值,即使return了一个MyError对象的指针,拿到的仍是error对象,因为MyError实现了error的接口,所以可以将Myerror对象指向error.类似于多态.
func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}

输出
at 2009-11-10 23:00:00 +0000 UTC m=+0.000000001, it didn't work

观察下面这个例子,Go 指南中关于错误的例子

实现一个Sqrt函数,使其接受负数参数时返回error

中文版链接

package main

import (
    "fmt"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprintf("cannot Sqrt negative number: %f", float64(e))
}
func Sqrt(x float64) (float64, error) {
    if x < 0 {
        return x, ErrNegativeSqrt(x)
    }
    return 0, nil
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}
  • return x, ErrNegativeSqrt(x)该行,其实就是将x转为实现了Error的ErrNegativeSqrt类型,指向error返回

使用Reader读取字节流

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Hello, Reader!")

    b := make([]byte, 8)
    for {
        n, err := r.Read(b)
        fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
        fmt.Printf("b[:n] = %q\n", b[:n])
        if err == io.EOF {
            break
        }
    }
}

输出
n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
b[:n] = "Hello, R"
n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
b[:n] = "eader!"
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""

实现一个Reader类型,它产生一个 ASCII 字符 'A' 的无限流

package main

import "golang.org/x/tour/reader"

type MyReader struct{}

// TODO: 给 MyReader 添加一个 Read([]byte) (int, error) 方法
func (m MyReader) Read(b []byte) (int, error) {
    for i := 0; i < len(b); i++ {
        b[i] = 'A'
    }
    return len(b), nil
}
func main() {
    reader.Validate(MyReader{})
}

标签: golang, c

添加新评论