【问题标题】:Overwriting an interface pointer value覆盖接口指针值
【发布时间】:2017-07-20 20:24:52
【问题描述】:

我制作了一个接口,用于对结构值进行基本数学运算。接口的数学函数总是更新结构指针的值。

我的问题是,在某些时候,我想将值覆盖为初始值,但我只有接口可以使用。因为它是一个指针接口(不是 100% 确定这是否是人们所说的),所以我无法“克隆”结构的初始值以便以后覆盖。

还请注意,我正在尽力避免反思。

这是我的代码。可能比我试图解释它更有意义:

package main

import (
    "fmt"
)

type mather interface {
    add(mather) mather
    sub(mather) mather
}

type float struct {
    value float64
}

func (f *float) add(m mather) mather {
    f.value += m.(*float).value
    return f
}

func (f *float) sub(m mather) mather {
    f.value -= m.(*float).value
    return f
}

func (f *float) get() interface{} {
    return *f
}

func main() {
    var a, b, c mather

    a = &float{2} // this could be any time. approximitly 10 possible types
    b = &float{7} // this could be any time. approximitly 10 possible types

    // float can't be used again below, only mather

    c = a

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println()

    // a's math
    doMath(a)

    // now math is done, we need to reset the value from before the math was done
    // set *a equal to *c. (a == &float{2})
    resetMath(a, c)

    // b's math
    doMath(b)

    // now math is done, we need to reset the value from before the math was done
    // set *b equal to *c. (b == &float{7})
    resetMath(b, c)

    fmt.Println(a)
    fmt.Println(b)

}

func doMath(m mather) {
    m.add(&float{3})
}

func resetMath(m mather, r mather) {
    m = r
}

https://play.golang.org/p/3Szk8uQGy5

【问题讨论】:

  • 预期输出是多少? 6?接口是一种契约,与底层数据的交互(可以是指针、值、任何你喜欢的……)应该通过方法集来完成。所以,如果你想获取接口的克隆,应该提供一种clone() mather 方法。要修改界面,应该通过其他方法来完成,例如set(mather),那么底层数据的修改应该在这个set方法中完成(你可以使用类型断言等......)。
  • @putu yip 预期的输出应该是 6。我认为应该进行设置和克隆方法,但我正在努力定义这些返回 mather 接口的方法。

标签: pointers go struct interface


【解决方案1】:

需要在mather接口中定义cloneset方法,即:

type mather interface {
    add(mather) mather
    sub(mather) mather
    get() interface{}
    clone() mather
    set(v mather)
}

getsetclonefloat 类型中的实现如下所示:

func (f *float) get() interface{} {
    return f.value
}

func (f *float) clone() mather {
    return &float{f.value}
}

func (f *float) set(v mather) {
    switch v := v.(type) {
    case *float:
        f.value = v.value
    //handle other possible types...
    default:
        //handle unknown types
    }
}

working example in Playground。唯一复杂的部分是set,您应该在其中确定基础类型。在这里您可以使用type switch 获取底层类型,然后将其分配给float。需要在其他底层类型中添加类似的代码(您提到有 10 种可能的类型)。

【讨论】:

  • 没有类型断言就没有办法解决吗?对不起,我知道我有点难。我不想做类型断言的原因是因为开发人员将用他们自己的类型扩展这个系统。而且每次开发者添加实现mather的类型时更新这个系统会很麻烦。
  • 是的,那会很麻烦。取决于case,可能有更简单的解决方案。澄清一下,mather 类型只支持标量值?复杂的呢?向量/矩阵?
  • 是的,它会有复杂的类型。这是我希望它可扩展的主要原因。然而,我确实在我正在试验的东西上领先,到目前为止,早期的测试看起来很有希望。如果成功或现在将分享。
  • 这最终成为我工作解决方案的基础。我设法通过添加zero() 函数和add(mather) 函数来设置所需值来绕过类型开关。不是很自豪,但在我有时间返工之前一直工作。谢谢!
【解决方案2】:

你可以使用简单的寻址操作符来做到这一点如果你有一个底层类型的引用,你可以在必要时通过类型断言来获得它,而不必求助于反射。一旦你有了*float,就很容易:

f := &float{2}

var a, b *float
a = f
b = new(float)
*b = *f        // Assign the value pointed to by f to the value pointed to by b

在这种情况下,您的本地变量现在是 *float 而不是 mather,但至少在您的示例中这并不重要。如果它确实很重要,如上所述,您可以使用类型断言。

【讨论】:

  • 我没想到要提到本地变量必须是mather。我在哪里使用它不知道类型的代码,只是它实现了mather 接口。假设只有这两个选项,我可能会重新设计我的一些代码?
  • 我在答案中输入的行,即使本地 var 是接口类型,您也可以使用类型断言(或类型切换)来做到这一点。
  • 我稍微更新了我的代码。希望现在它可能更有意义......我之前忘记提到了一些限制。
猜你喜欢
  • 2020-08-23
  • 2015-12-23
  • 1970-01-01
  • 2020-03-23
  • 1970-01-01
  • 2021-04-13
  • 1970-01-01
  • 2012-11-10
  • 2019-07-26
相关资源
最近更新 更多