一种基于类型的函数切换 vs 多个有类型的函数

huangapple go评论83阅读模式
英文:

One function switching on type vs Many typed functions

问题

给定以下结构体Foo和处理多种类型的目标(其中Handle可以是ReadWrite等)。我知道当我们使用空接口时,会失去编译时类型检查,但除此之外,每种方法的优缺点是什么?最后,最符合惯例的实现方式是什么?

package main

type Foo struct {
    A int
    B string
}

// 使用switch处理所有类型
func (f *Foo) Handle(obj interface{}) {

    switch obj := obj.(type) {
    case int:
        // 处理int类型...
        f.A + obj
    case string:
        // 处理string类型...
        f.B + obj
    default:
        panic("未知类型")
    }
}

// 分别处理每种类型
func (f *Foo) HandleInt(i int) {
    // 处理int类型...
    f.A + i
}
func (f *Foo) HandleString(s string) {
    // 处理string类型...
    f.B + s
}
英文:

Given the following struct Foo and goal of handling multiple types (where Handle could be Read, Write, etc.). I understand that we lose compile-time type checks when we use the empty interface, though aside from this, what are the pros and cons of each? Finally, what is the most idiomatic way to achieve this?

package main

type Foo struct {
	A int
	B string
}

//Handle all types with switch
func (f *Foo) Handle(obj interface{}) {

	switch obj := obj.(type) {
	case int:
		//do int stuff...
		f.A + obj
	case string:
		//do string stuff...
		f.B + obj
	default:
		panic("Unknown type")
	}
}

//Handle each type individually
func (f *Foo) HandleInt(i int) {
	//do int stuff...
	f.A + i
}
func (f *Foo) HandleString(s string) {
	//do string stuff...
	f.B + s
}

答案1

得分: 2

空接口在通过reflect处理用户定义类型时是必需的。这就是为什么fmt.Printfjson.Encodebinary.Write接受空接口的原因。在你之前提到的Merkle树场景中,当你对事物进行哈希时,如果你的Hash()函数有一个基于reflect的回退选项来哈希用户创建的结构体,你会使用空接口。

如果你只为一些关键类型([]bytestring等)提供方法,那么具体的方法可能更有意义。除了在编译时进行检查外,函数列表还作为可以哈希的内容的文档。另一方面,如果有十几种以上的类型需要进行哈希,比如所有的(无)符号整数类型和它们的切片,我认为我会使用interface{},只是为了一个清晰简洁的API,除非你绝对需要最佳性能,但我认为在这个问题上没有明确的共识。

英文:

The empty interface is necessary if you're going to handle user-defined types via reflect. That's fmt.Printf, json.Encode, and binary.Write's reason for accepting it. In terms of the Merkle tree scenario you posted about earlier, where you're hashing things, you'd use an empty interface if your Hash() had a reflect-based fallback for hashing user-created structs.

Specific methods probably make more sense if you're only going to provide methods for a few key types ([]byte, string, whatever). Besides compile-time checking, the function list serves as documentation of what you can hash. On the other hand, if there are, like, a dozen-plus types you want to hash--think all the (u)int types and slices of them--I think I'd use an interface{} just for the sake of an uncluttered API unless you absolutely needed the very best performance, but I don't think there's a clear consensus one way or the other.

答案2

得分: 1

一个很好的例子是package sort源代码在这里),它具有以下特点:

  • 实现了一种基本类型的方法(与你的第二种方法类似)
  • 提供了一个接口,要排序的类型必须实现该接口。
  • 可以对切片和用户定义的集合进行排序。

如果你对Handle(obj interface{})进行相同的处理,即Handle(obj Interface),那么对于处理更复杂的类型,你只需要定义一次处理程序,并使用在Interface中声明的方法。任何满足该Interface的类型都可以传递给通用的Handle()函数。你也可以处理切片(类似于这个sort的例子)或用户定义的集合。

英文:

One good example would be the package sort (sources here), which:

  • does implement one method for basic type (as in your second approach)
  • proposes an interface that the type to be sorted has to implement.
  • can sort slices and user-defined collection

If you do the same for Handle(obj interface{}) => Handle(obj Interface), that would help you handler (for more complex type) to be defined once, using the methods declared in 'Interface'.
Any type satisfying said Interface would be passed to the generic Handle() function.
And you could Handle slice (similar to this example for sort) or used-defined collection as well.

huangapple
  • 本文由 发表于 2014年8月30日 12:01:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/25578890.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定