10

我试过这样做:

package main

import (
    "fmt"
    "strings"
)

type String string


func (s *String) tolower() String {
    *s = String(strings.ToLower(string(*s)))
    return *s
}

func (s *String) toupper() String {
    *s = String(strings.ToUpper(string(*s)))
    return *s
}

func main() {
    var s String = "ASDF"
    (s.tolower()).toupper() // this fails
    // s.toupper();s.tolower(); // this works
    // s.tolower().toupper() // this fails too
    fmt.Println(s)
}

但我得到了这些错误:

prog.go:30: cannot call pointer method on s.tolower()
prog.go:30: cannot take the address of s.tolower()

Program exited.

为什么我不能让这个链条工作?

4

4 回答 4

16

这有效:

package main

import (
    "fmt"
    "strings"
)

type String string


func (s *String) tolower() *String {
    *s = String(strings.ToLower(string(*s)))
    return s
}

func (s *String) toupper() *String {
    *s = String(strings.ToUpper(string(*s)))
    return s
}

func main() {
    var s String = "ASDF"
    (s.tolower()).toupper()
    s.toupper();
    s.tolower();
    s.tolower().toupper()
    fmt.Println(s)
}

对于在指向 String 的指针上定义的函数,您的返回类型是 String。能够将它们链接起来是没有意义的。

于 2013-08-09T17:39:08.123 回答
4

tolower() 和 toupper() 具有指向字符串的指针作为接收者,但它们返回的是字符串(而不是指向字符串的指针)。

您可以通过更改其中一个来解决此问题。

例如,将函数的签名更改为:

func (s *String) toupper() *String

或者

func (s String) toupper() String

(见:http ://play.golang.org/p/FaCD8AQtIX )

于 2013-08-09T17:41:08.210 回答
3

当您在变量(在您的示例中)上使用指针接收器调用方法时s,将自动获取该值的地址。所以,你基本上是在打电话(&s).toupper()。此机制适用于所有可寻址的值。

除非您将函数的返回值存储在变量中(以便它们在当前堆栈帧或堆中具有永久位置),否则函数的返回值是不可寻址的。

我会推荐以下 API,因为看起来您的字符串类型的用户应该使用String而不是*String. String因此,设计一个也用于避免混淆的一致 API 是有意义的。无论如何,按值传递字符串非常快,因为它们在内部实现为指向不可变数组的指针:

func (s String) tolower() String {
    return String(strings.ToLower(string(s)))
}

此方法不需要指针接收器,因为它不会修改当前字符串。它改为返回一个新字符串。您还可以轻松地链接这些方法。

或者,您可以通过以下方式实现这些方法:

func (s *String) tolower() *String {
    *s = String(strings.ToLower(string(*s)))
    return s
}

在这种情况下,您会一直返回相同的指针。因此,为了调用(s.tolower()).toupper(),您需要能够获取s可能的地址,因为您已将其分配给变量。然后链中所有进一步的方法调用也是可能的,因为您使用指向初始变量的指针来调用它们。这与您尝试链接方法不同,因为每个方法调用都必须获取临时变量的地址才能对其进行修改(这不是很有用)。

于 2013-08-09T18:05:57.173 回答
0

也许你可以试试这个项目:https ://github.com/Laisky/go-chaining

import "github.com/Laisky/go-chaining"

func toLower(c *chaining.Chain) (interface{}, error) {
    v := c.GetString()
    return strings.ToLower(v), nil
}

func toUpper(c *chaining.Chain) (interface{}, error) {
    v := c.GetString()
    return strings.ToUpper(v), nil
}

func TestChainWithString(t *testing.T) {
    f := func() (string, error) { return "aBcD", nil }
    r := chaining.New(f()).
        Next(toLower).
        Next(toUpper)

    expectVal := "ABCD"
    if r.GetString() != expectVal {
        t.Errorf("expect %v, got %v", expectVal, r.GetString())
    }
}
于 2018-03-23T06:40:47.627 回答