Как очистить значения экземпляра структуры типа динамически

Возможно ли с помощью 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
}

Ответ 1

Вы не можете изменить исходные значения без указания на них указателя.

Намного проще и понятнее просто назначить новое нулевое значение в вашем коде. Если ваши типы более сложные, вы можете заменить значения конструктором или предоставить методы Reset() для ваших типов с приемником указателя.

Если вы действительно хотите увидеть, как это сделать с помощью отражения, ваша функция clear может выглядеть следующим образом: http://play.golang.org/p/g0zIzQA06b

func clear(v interface{}) {
    p := reflect.ValueOf(v).Elem()
    p.Set(reflect.Zero(p.Type()))
}

(это вызовет панику, если вы передадите значение без указателя)

Ответ 2

Хотя этот вопрос был впервые задан 3 года и 9 месяцев назад, у ИМО по-прежнему нет хорошего ответа, поэтому позвольте мне предложить один.

Нет необходимости размышлять или создавать новый экземпляр каждый раз, когда вы хотите очистить свою переменную. Вместо этого создайте один чистый новый экземпляр, который вы оставляете в его первоначальном обнуленном состоянии, который затем вы можете скопировать в память объекта, когда захотите Reset() его. (Сошлитесь на @DaveC, указав, что метод Reset() более идиоматичен.)

Вы просто копируете из своего обнуленного значения в то место, в котором ваш объект указывает на указатели. В следующем тексте говорится: "Скопируйте значение памяти, на которую ссылается zeroA, в ячейку памяти, на которую ссылается a.":

*a = *zeroA

Вот мой полный пример, который вы также можете попробовать на игровой площадке Go. (Обратите внимание, что поскольку ваш приемник для Reset() является указателем на тип A, то вызов метода Reset() позволяет обновить значение a, если обновление сохраняется после окончания вызов метода):

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}    

Вы также можете использовать эту технику для копирования значений структуры, которая содержит значения по умолчанию. Однако имейте в виду, что это поверхностная копия, а не глубокая копия. Если ваш struct содержит какие-либо свойства, которые являются указателями, этот подход также скопирует значения указателя и не выделит память для указания на новые копии указанных значений. Поэтому вам потребуется проделать дополнительную работу в Reset(), если вы хотите сбросить свою структуру к новым значениям по умолчанию, включая копии любых подструктур, объявленных с указателями.

Я надеюсь, что это помогает другим, поэтому им не нужно учиться этому трудному пути, как это потребовалось мне.

Ответ 3

Можно переназначить значение с помощью пустого объекта структуры, который будет сбрасывать значения.

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

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

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

https://play.golang.org/p/CJ6cx2TFytY

Ответ 4

Вы можете просто установить пустую структуру, например, так:

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

// Reset struct.
obj = myStruct{}

ссылка на игровую площадку: https://play.golang.org/p/Rf1BqfFe3IQ