【问题标题】:How to modify function input parameters without specifying type?如何在不指定类型的情况下修改函数输入参数?
【发布时间】:2020-11-04 15:55:11
【问题描述】:

我正在尝试创建一个接受 2 个参数的函数,并将第二个参数的值设置为第一个,它可能看起来像(我不确定):

func set(a interface{}, b interface{}) {
  // do something to make a = b
}

它是这样工作的:

a := 0
b := 10
set(&a, b)
// here a should be 10

s1 := ""
s2 := "what"
set(&s1, s2)
// here s1 should be "what"

预计它适用于所有基本类型,如整数、浮点数、字符串、布尔值......

我怎样才能做到这一点?

【问题讨论】:

  • Golang 不支持泛型,所以你不应该尝试使用它。

标签: pointers go reflection interface


【解决方案1】:

这可以通过反射实现:

func set(a interface{}, b interface{}) {
    reflect.ValueOf(a).Elem().Set(reflect.ValueOf(b))
}

请注意,此代码不检查a 是否为指针,其元素类型是否与b 中值的类型匹配。另请注意,使用反射速度较慢,并且您会失去编译时类型安全性。上述代码比将单个 int 值分配给 int 变量或将 string 值分配给 string 变量要慢得多。

测试它:

a := 0
b := 10
set(&a, b)
fmt.Println(a)

s1 := ""
s2 := "what"
set(&s1, s2)
fmt.Println(s1)

输出(在Go Playground 上试试):

10
what

这是一个添加检查以避免运行时恐慌的版本:

func set(a interface{}, b interface{}) error {
    v1 := reflect.ValueOf(a)
    if v1.Kind() != reflect.Ptr {
        return errors.New("a must be pointer")
    }
    if v1.IsZero() {
        return errors.New("a must not be a nil pointer")
    }

    v1 = v1.Elem()
    v2 := reflect.ValueOf(b)
    if v1.Type() != v2.Type() {
        return errors.New("a's element type must match b's type")
    }

    if !v1.CanSet() {
        return errors.New("unsettable value in a")
    }

    v1.Set(v2)
    return nil
}

测试它:

a := 0
b := 10
err := set(a, b)
fmt.Printf("set(a, b): a=%v, err: %v\n", a, err)
err = set(&a, b)
fmt.Printf("set(&a, b): a=%v, err: %v\n", a, err)
err = set((*int)(nil), b)
fmt.Printf("set((*int)(nil), b): a=%v, err: %v\n", a, err)

s1 := ""
s2 := "what"
err = set(&s1, s2)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)
err = set(&s1, b)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)

输出(在Go Playground上试试):

set(a, b): a=0, err: a must be pointer
set(&a, b): a=10, err: <nil>
set((*int)(nil), b): a=10, err: a must not be a nil pointer
set(&s1, s2): s1=what, err: <nil>
set(&s1, s2): s1=what, err: a's element type must match b's type

【讨论】:

  • 如果我在 a 和 b 都是 int 的情况下调用 set(a, b),它会恐慌而不是报告编译错误,这让我很担心。有什么办法可以避免运行时错误?
  • @forrestJiang 添加了检查类型的版本,如果无法执行set(),则不会恐慌但返回error
  • 虽然它不会在编译时失败,但它确实避免了恐慌。非常感谢!
  • @forrestJiang 您不能创建在编译时失败的通用set()。这将需要 Go 尚不支持的泛型。
猜你喜欢
  • 2018-11-18
  • 2015-03-20
  • 1970-01-01
  • 2020-03-18
  • 1970-01-01
  • 1970-01-01
  • 2016-08-13
  • 2011-10-06
  • 1970-01-01
相关资源
最近更新 更多