[Перевод] Понимание отражения Go

Go

оригинал:medium.com/better-pro Страна…

Go — строго типизированный статический язык программирования. Однако некоторые особенности Go снова заставляют его казаться динамическим языком. Например, если вы не уверены в типе получаемого параметра, вы можете использоватьinterfaceпринимать все типы передачи параметров.

Помните, что атрибут отражения есть только у интерфейса..

Отметим, что интерфейсы позволяют Go реализовать полиморфизм. Ни один тип особо не выделяется. Может быть строка int64 float32 или даже коллекция (массив/карта). Но когда компьютер запускает код, отражение помогает изучить и изменить его собственную структуру и поведение. Этот процесс позволяет нам узнать тип объекта и то, как существует память во время выполнения.

Зачем нам нужно отражать?

  • Позволяет заранее определять типы параметров (обычно это происходит в открытых API)
  • Функции могут выполняться динамически в соответствии с переданными параметрами.

Недостатки отражения

  • влияет на читаемость кода
  • Проверка ошибок при компиляции кода замаскирована. Как динамический язык, компилятор Go может обнаруживать ошибки типов данных заранее, во время компиляции. Когда данные не имеют определенного типа в интерфейсе, сервер рискует запаниковать при выполнении кода
  • Снижение общей производительности. Использование отражения требует от сервера дополнительной работы для получения значения и определенного типа параметра, поэтому старайтесь избегать использования интерфейса для некоторых важных параметров.

Две основные функции Reflect

Две основные функции отражения:reflect.Typeтак же какreflect.Value.

Проще говоряreflect.TypeУкажите фактический тип параметров, когдаreflect.Valueкомбинировать_type dataПри совместном использовании он позволяет разработчику читать или перезаписывать значение параметра.

func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value

Затем вы можете использоватьfmt.Printf()а также%Tформатировать параметры в илиreflect.TypeOfРезультат выглядит следующим образом:

fmt.Printf("%T", 3) //int

существуетreflect.TypeВнизtoTypeэто метод, который изменяет тип данных:

func toType(t * rtype) Type {
    if t == nil {
    return nil
}
 
return t
}

другими словами,reflect.Valueвозвращает сохраненный вinterface{}переменные в . Уже существует множество методов, в том числеSetLen(n int),SetMapIndex(key, val Value),Int(),TrySend(x refelect.Value)и т.п. С полной версией документа можно ознакомиться по ссылкеsrc/reflect/value.go

Три сценария с использованием Reflect

Используйте сценарий с официального сайта GO:

  1. От интерфейса к отражению объекта
  2. От отражения объекта к интерфейсу
  3. Измените интерфейс, но его значение должно быть изменяемым

Классический пример выглядит следующим образом:

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic

Сервер паникует при выполнении этого кода, потому что v — это не x, а обратная сторона x. Поэтому все модификации v запрещены.

Итак, нам нужно использовать указатели для решения этой проблемы

var x float64 = 3.4
y := reflect.ValueOf(&x)

fmt.Println(“type of y”, y.Type()) // *float64
fmt.Println(“settability of y:”, y.CanSet()) // false

В это время y еще не может заменить x, вы можете передатьy.Elem()модифицировать

z := y.Elem()
z.SetFloat(7.1)

fmt.Println(z.Interface()) // 7.1
fmt.Println(x) // 7.1

Можно заметить, что указатель изменит значение, на которое указывает, то есть x

Применение отражения

reflectшироко используется в сериализации объектов,fmtСвязанные функции и ORM и так далее.

Сериализация JSON

В Go есть две функции для сериализации и десериализации.

func Marshal(v interface{})([]byte, error)
func Unmarshal(data []byte, v interface{}) error

Обе функции получают тип интерфейса в качестве параметра, поэтому, когда нам нужно знать значение и тип параметра при запуске функции внутри,reflectизget setметод будет работать

Функция DeepEqual

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

func DeepEqual(x, y interface{}) bool

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

подожди минутку как это называетсяdeeplyРавно, посмотрите на следующий пример

type FirstInt int
type SecondInt int

func main() {
    m := FirstInt(1)
    n := SecondInt(1)
    fmt.Println(reflect.DeepEqual(m, n)) // false
}

В приведенном выше примере, хотя m и n оба равны 1, их типы данных различны: один имеет тип FirstIn, а другой — тип SecondInt. Так что они не равны.

Суммировать

Как статический язык, нам совершенно ясно, что Go имеет ограничения в гибкости написания языка по сравнению с динамическими языками, такими как Python. Но с помощьюreflectЭто также позволяет нам иметь некоторые особенности динамических языков, вы можете легко получить тип и значение параметра при его использовании.