Использование отражения для обновления значения по ссылке, когда аргумент не является указателем в go - PullRequest
0 голосов
/ 01 сентября 2018

У меня были трудности с изучением основ рефлекса, указателей и интерфейса на ходу, поэтому вот еще один вопрос начального уровня, который я не могу понять.

Этот код выполняет то, что я хочу, - я использую отражение, чтобы добавить еще одну запись к фрагменту, который напечатан как интерфейс.

package main

import (
  "reflect"
  "log"
)
type Person struct {
  Name string
}
func Add(slice interface{}) {
  s := reflect.ValueOf(slice).Elem()
  // in my actual code, p is declared via the use of reflect.New([Type])
  p := Person{Name:"Sam"}

  s.Set(reflect.Append(s,reflect.ValueOf(p)))
}

func main() {
  p := []Person{}
  Add(&p)
  log.Println(p)
}

Если я изменил функцию Add и main на это, все будет работать не так, как я хочу.

func Add(slice interface{}) {
  s := reflect.ValueOf(&slice).Elem()
  p := Person{Name:"Sam"}

  s.Set(reflect.Append(reflect.ValueOf(slice),reflect.ValueOf(p)))
  log.Println(s)
}

func main() {
  p := []Person{}
  Add(p)
  log.Println(p)
}

То есть log.Println(p) в конце не показывает срез с записью Sam, как я и надеялся. Итак, мой вопрос: возможно ли для меня Add() получить фрагмент, который не является указателем, и для меня все еще написать некоторый код в Add(), который даст результат, показанный в моем первом сценарии?

Многие из моих недавних вопросов танцуют вокруг этого вида предмета, поэтому мне все еще нужно время, чтобы понять, как эффективно использовать пакет отражения.

1 Ответ

0 голосов
/ 01 сентября 2018

Нет, невозможно добавить к срезу в функции, не передав указатель на срез. Это связано не с отражением, а с тем, как переменные передаются в функции. Вот тот же код, модифицированный, чтобы не использовать отражение:

package main

import (
        "log"
)

type Person struct {
        Name string
}

func AddWithPtr(slicep interface{}) {
        sp := slicep.(*[]Person)

        // This modifies p1 itself, since *sp IS p1
        *sp = append(*sp, Person{"Sam"})
}

func Add(slice interface{}) {
        // s is now a copy of p2
        s := slice.([]Person)

        sp := &s

        // This modifies a copy of p2 (i.e. s), not p2 itself
        *sp = append(*sp, Person{"Sam"})
}

func main() {
        p1 := []Person{}
        // This passes a reference to p1
        AddWithPtr(&p1)
        log.Println("Add with pointer:   ", p1)

        p2 := []Person{}
        // This passes a copy of p2
        Add(p2)
        log.Println("Add without pointer:", p2)
}

(Выше, когда написано «копия» среза, это не означает копию базовых данных - только срез)

Когда вы передаете срез, функция эффективно получает новый срез, который ссылается на те же данные, что и оригинал. Добавление к срезу в функции увеличивает длину нового среза, но не меняет длину исходного среза, который был передан. Вот почему оригинальный срез остается неизменным.

...