【问题标题】:Copy or New instance of an interface复制或新建接口实例
【发布时间】:2014-04-08 21:04:08
【问题描述】:

我有一个函数,它接受一个接口并返回一个接口。它想将结果初始化为源的副本,然后进行一些更改,然后返回结果。例如:

Playground

type Something interface {
    CopySomething() Something  // I'd like to get rid of this
    SetX(x int)
}

type RealThing struct {
    x int
}

func (t *RealThing) SetX(x int) {
    t.x = x
}

func (t *RealThing) CopySomething() Something {
    newT := *t
    return &newT
}

func Updated(original Something, newX int) Something {
    newThing := original.CopySomething()  // I'd like to make the copy without .CopySomething()
    newThing.SetX(newX)
    return newThing
}

func main() {
    a := &RealThing{x: 1}
    b := Updated(a, 5)
    fmt.Printf("a = %v\n", a)
    fmt.Printf("b = %v\n", b)
}

这行得通,但CopySomething() 方法似乎没有必要(对于每个需要复制内容的接口,我都需要另一种相同的方法)。有没有更好的方法在没有额外方法的情况下在Updated() 内复制original?有没有更惯用的方法来实现这一点?

在我正在处理的特定情况下,我可以只实例化一个与original 相同类型的新实例(我真的不需要副本)。这样问题会更简单吗?


根据 Evan 的评论,我想我会提供一些我已经尝试过的其他基于反射的东西:

newThing := reflect.New(reflect.TypeOf(original))

==> 编译错误:类型 reflect.Value 没有字段或方法 SetX

newThing := reflect.New(reflect.TypeOf(original)).Interface().(Something)

===> 运行时错误:接口转换:**main.RealThing is not main.Something

newThing := reflect.Indirect(reflect.New(reflect.TypeOf(original))).Interface().(Something)

===> 运行时错误:无效的内存地址或 nil 指针取消引用

此时,我觉得我的反射变得很傻,并停止了对它的打击。

【问题讨论】:

  • 有没有办法将其重构为不使用指针?然后你会自动在界面中得到一个浅拷贝。
  • 我不明白。你怎么会不使用指针,但仍然实现 SetX?
  • 啊,没关系。我很着急,没有考虑清楚。您必须在结构内使 X 成为指针,并且可能会使它更加复杂。

标签: go


【解决方案1】:

由于您只需要实例化一个新实例,您可以使用反射来获取存储在接口中的对象的类型并以这种方式实例化一个副本。 reflect.New(reflect.TypeOf(x)) 之类的东西,尽管您可能必须使用 reflect.Indirect() 才能分配新值而不是新指针。

全部记录在这里:http://golang.org/pkg/reflect/

还有一个可运行的版本:http://play.golang.org/p/z8VPzDKrSk

【讨论】:

  • 我沿着这条路走得很深,但没有成功。 “play with reflect.Indirect()”部分是众多变体之一:D 你知道如何真正到达那里吗? (这个特定版本给出了类型“reflect.Value has no field or method SetX”)
  • 这可行,但在我看来,界面上的Copy() 方法更有意义。您的具体实现可能比您仅通过接口不知道的 x int 更多。
  • @RobNapier,如果你决定走反射路线,看看这个:godoc.org/code.google.com/p/rog-go/exp/deepcopy
  • @VitorDeMario,如果您了解其他领域,这有什么关系? Evan 的解决方案只是从 New() 中返回一个零。虽然我发现也许我确实需要复制一份……
  • @RobNapier 我想你想要original 值的完整副本。假设有一个包含更多字段的接口的ExtraThing 实现。一个y float64 可能。如果 y 设置为 0.0 以外的任何值,您将丢失该信息。这在您的用例中可能没问题。
【解决方案2】:

newThing := reflect.New(reflect.TypeOf(original))

==> 编译错误:类型 reflect.Value 没有字段或方法 SetX

如果 original 是指针则需要获取元素:

elem := reflect.TypeOf(original).Elem()    
newThing := reflect.New(elem)

新事物:= reflect.New(reflect.TypeOf(original)).Interface().(Something)

===> 运行时错误:接口转换:**main.RealThing is not main.Something

正在修复并继续... 知道 newThing 是新 RealThing 元素的指针,然后通过 .Interface() 获取此指针,但在 interface{} 上。

elem := reflect.TypeOf(original).Elem()    
newThingValue := reflect.New(elem)
newThingInterf := newThingValue.Interface()

为了获取指向RealThing的指针,需要做断言,那么

newThing := newThingInterface.(*RealThing)

这样就可以正常更新newThing了

newThing.SetX(newX)//update new instance

查看在https://play.golang.org/p/N-pwH5J_el工作

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-30
    • 2014-08-17
    • 2017-03-17
    • 2018-04-25
    • 1970-01-01
    • 1970-01-01
    • 2011-06-03
    • 2016-08-13
    相关资源
    最近更新 更多