Struct 如何动态清除类型结构实例的值

Struct 如何动态清除类型结构实例的值,struct,go,Struct,Go,Go是否可以创建一个动态清除结构实例值的方法 type A struct { Name string Level int } type B struct { Skill string } func main() { a := A{"Momo", 1} b := B{"Starfall"} // outputs // {"Momo", 1} // {"Starfall"} clear(a) clear(b)

Go是否可以创建一个动态清除结构实例值的方法

type A struct {
    Name string
    Level int
}

type B struct {
    Skill string
}

func main() {
    a := A{"Momo", 1}
    b := B{"Starfall"}

    // outputs
    // {"Momo", 1}
    // {"Starfall"}

    clear(a)
    clear(b)

    // outputs
    // { , 0}
    // { }
}

func clear(v interface{}) {
    // some code
}

如果不向原始值传递指针,则无法修改原始值

在代码中简单地分配一个新的零值更容易、更清晰。如果您的类型更复杂,则可以使用构造函数替换值,或者使用指针接收器为您的类型提供
Reset()
方法

如果您真的想通过反射查看如何执行此操作,您的
clear
函数可能如下所示:


(如果传入非指针值,将导致死机)

可以使用结构的空对象重新分配值,从而重置值

a := A{"Momo", 1}
b := B{"Starfall"}

fmt.Println(a)
fmt.Println(b)
// outputs
// {"Momo", 1}
// {"Starfall"}

a = A{}
b = B{}
// outputs
// { , 0}
// { }

尽管这个问题在3年零9个月前首次提出,但我仍然没有一个好的答案,所以让我提出一个

无需进行反射,也无需在每次清除变量时创建新实例。相反,创建一个原始的新实例,将其保留在初始归零状态,然后在需要重置对象时,可以将其复制到对象的内存中。(向@DaveC致敬,指出
Reset()
方法更为惯用。)

您只需使用指针将零值复制到对象指向的位置。以下内容为“将
zeroA
引用的内存的复制到
a
引用的内存位置。”

这是我的完整示例,您也可以尝试。(请注意,由于for
Reset()
是指向类型
a
的指针,因此调用
Reset()
方法可以更新
a
的值,其中更新在方法调用结束后仍然有效):

这是您的输出(请注意,我证明您的变量已调零,并且指向该变量的指针也已调零,但如果您制作了原始变量的*副本*,则不会调零):

还可以使用此技术复制包含默认值的结构的值。但是,请注意,这是一个浅拷贝,而不是深拷贝。如果您的
struct
包含任何作为指针的属性,这种方法也会复制指针值,并且不会分配内存指向指向的值的新副本。因此,如果要将结构重置为新的默认值,包括使用指针声明的任何子结构的副本,则需要在
Reset()
中进行额外的工作


我希望这对其他人有所帮助,这样他们就不必像我那样费力地学习了。

您可以将其设置为空结构,如下所示:

var obj myStruct
obj.field1 = "apple"
obj.field2 = 12

// Reset struct.
obj = myStruct{}

操场链接:

由于您没有传递指针,没有返回任何内容的函数无法清除a,因为它不知道a,只知道该值的一个副本。只需像这样指定一个零值
a=a{}
,它更简单、更可读。从技术上讲,接口值是一种引用类型。所以他写的clear函数可以清除一个结构。然而,他必须进行大量的反思。就像其他人说的那样,只使用零值要容易得多。@JeremyWall:那是不正确的。您无法通过接口获得对原始值的引用,即使是通过反射。@zmb,这是因为我的答案使用了指针。它与接口是“引用类型”(有点用词不当)无关,它只是从接口中提取指针。最初的问题不是传递指针,因为Go中的所有内容都是一个值,所有赋值都是一个副本,因此无法以这种方式修改a和b的值。仅供参考,在标准库中使用
Reset()
方法而不是
Clear()
方法是有先例的。例如,etc.似乎很清楚函数不适用于字符串类型:“reflect:call of reflect.Value.Elem on string Value”来自@美学的答案更为正确,因为该解决方案实际上失败了。myStructVar=myBaseStruct{}这很有效,如果有一个名为reset的struct方法就好了。@Cyberience:是的,如果实际使用的代码像提供的示例一样简单的话,最好是美学上的答案。虽然这应该是大多数代码的首选方法,但它不会试图回答张贴的问题;特别是因为OP实际上可能有一个用例,可以用它们的零值等价物替换各种任意值(这并非完全闻所未闻),建议这样做吗?我已经看到它用多种语言完成,而且很有效。没有理由不使用它。这就像重新分配对象的值。这种方法会进行新的分配,如果您的目标是重用对象,以便在快速创建大量对象并将其丢弃的程序中最小化分配,那么这将不会有帮助。@MikeSchinkel您从何处获得此想法?这不是真的。它仍然是相同的对象,具有不同的值。让我们比术语“对象”更精确一些,我将只讨论
A
,因为这同样适用于
B
。我们有1.)变量
A
,2.)存储
{Momo',1}
{,0}
,以及3.)存储
{Momo',1}
{,0}
的内存地址。正确的是,它仍然是变量
A
,但不正确的是,内存中差值的地址将是相同的。当您调用
A{}
时,它会分配新内存并将该地址分配给
A
,同时使内存
{“Momo”,1}
可供垃圾收集器使用。
*a = *zeroA
package main

import (
    "fmt"
)

type A struct {
    Name string
    Level int
}
var zeroA = &A{}
func (a *A) Reset() {
    *a = *zeroA
}

func main() {
    a1 := A{"Momo", 1}
    a2 := &a1
    a3 := a1

    fmt.Println(a1)
    fmt.Println(a2)
    fmt.Println(a3)

    a1.Reset()

    fmt.Println(a1)
    fmt.Println(a2)
    fmt.Println(a3)
}
{Momo 1}
&{Momo 1}
{Momo 1}
{ 0}
&{ 0}
{Momo 1}    
var obj myStruct
obj.field1 = "apple"
obj.field2 = 12

// Reset struct.
obj = myStruct{}