оригинал: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:
- От интерфейса к отражению объекта
- От отражения объекта к интерфейсу
- Измените интерфейс, но его значение должно быть изменяемым
Классический пример выглядит следующим образом:
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
Это также позволяет нам иметь некоторые особенности динамических языков, вы можете легко получить тип и значение параметра при его использовании.