055-实战 flag.Value 接口

如果你中间是跳跃着看的,希望你一定不要错过《参数解析》 一文。

我们已经学习过参数解析的 flag 包相关使用方法,所以这里不会再赘述,如果你还不会,建议你回去学习并掌握它。

1. 简单回顾

很久以前,我们就学习过 flag 包的使用方法,它可以解析常见的基础类型,比如 int, string 等。 flag 包使用起来非常简单,只要使用相关的方法,将要解析的标志加入到标志集合,最后调用 flag.Parse 就可以完成解析。下面是解析 int 类型的例子:

var age int
flag.IntVar(&age, "a", 15, "设置年龄") // 添加标志集合
flag.Parse() // 解析

2. flag.Value 接口

flag 包固然好用,但是默认情况下它只支持解析基础数据类型。有时候我们想解析用户自定义的类型怎么办?

在 Go 里,一切都好说。有了接口,这件事情就好办。只要你定义的类型实现了 flag.Value 接口,就可以使用 flag.Var 函数添加标志集合了。flag.Value 接口定义如下:

type Value interface {
    String() string
    Set(string) error
}

一旦你的类型实现了 String 方法和 Set 方法,你就可以调用 flag.Var 来解析你的专属类型了。来看一个具体实例。

3. 示例

下面是自定义的一个 Person 类型,有两个成员,一个 Name,一个 Age.

type Person struct {
    Name string
    Age  int
}

我们希望通过命令行参数来指定 Person 对象的值。比如我们希望输入以下命令,就能从命令行传递一个 Person 对象的参数了。

$ ./demo -p allen 19

在此之前,我们先实现 flag.Value 接口。

  • 实现 flag.Value 接口
// 实现 flag.Value 接口
func (p *Person) Set(s string) error {
    var name string
    var age int
    // Sscanf 对字符串 s 解析,提取出 name 和 age 并赋值给接收器对象。
    _, err := fmt.Sscanf(s, "%s %d", &name, &age)
    *p = Person{name, age}
    return err
}

func (p *Person) String() string {
    return fmt.Sprintf("name:%s age:%d", p.Name, p.Age)
}
  • 使用 flag.Var 解析
func main() {
    var p Person
    flag.Var(&p, "p", "设置 person") // 添加标志集合 p
    flag.Parse() // 解析命令行参数
    fmt.Printf("%v\n", &p) // 只有 *Person 实现了 String 方法,所以需要传递 &p
}

运行程序,结果如下:

$ go run demo.go -p "allen 19"
name:allen age:19

4. 总结

  • 掌握 flag.Value 接口
  • 掌握实现接口的方法
  • 理解接口的用处

通过对 flag.Value 的学习,你就能认识到接口的妙用了吧。从命令行解析参数本来就是一件重复性的劳动,你当然也可以手工去解析,不过自己实现一套参数解析功能的函数,这完全和 flag 包的功能重复,实在没有必要。

flag 包用更加智慧的办法解决了用户自定义类型的解析——使用接口。即便 flag 不知道你的类型长什么样也没关系,只要你的类型支持了 String 和 Set 这样的行为就够了。本质上这就是一种协议,一种约定。

猜你喜欢

转载自blog.csdn.net/q1007729991/article/details/80559892
今日推荐