Skip to content

Законы рефлексии в Go

В этой теме мы рассмотрим:

  • Go Reflection, типы reflect.Type, reflect.Value и значения

  • Что такое рефлексия тэгов и дополнительные функции рефлексии

  • Как происходит рефлексия карт

  • Что умеет функция reflect.ValueOf

  • В чём заключается метод Canconvert

Законы рефлексии в Go

Как мы уже знаем, Go — статически типизированный язык с хорошей поддержкой рефлексии. Далее мы рассмотрим функции рефлексии, предоставляемые в reflect стандартном пакете.

Обзор Go Reflection

Рефлексия Go привносит множество динамических функций в программирование Go. Многие стандартные пакеты кода, такие как fmt и encoding, сильно зависят от функций рефлексии.

Мы можем проверять значения Go через значения типов Type и Value, определенные в reflect стандартном пакете.

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

Тип reflect.Type и его значения

В Go мы можем создать reflect.Type значение из произвольного неинтерфейсного значения, вызвав функцию reflect.TypeOf. Результирующее reflect.Type значение представляет собой тип неинтерфейсного значения. Конечно, мы также можем передать значение интерфейса вызову функции reflect.TypeOf, но вызов вернет reflect.Type значение, представляющее динамический тип значения интерфейса. На самом деле функция reflect.TypeOf имеет только один параметр типа interface{} и всегда возвращает reflect.Type значение, представляющее динамический тип единственного параметра интерфейса. Тогда как получить reflect.Type значение, представляющее тип интерфейса? Для достижения этой цели мы должны использовать косвенные пути, которые будут представлены далее.

Тип reflect.Type является типом интерфейса. Он определяет несколько методов. Мы можем вызывать эти методы для проверки информации о типе, представленном reflect.Type значением получателя. Некоторые из этих методов применимы ко всем видам типов, некоторые из них специфичны для одного или нескольких видов. Пожалуйста, прочитай документацию каждого метод, чтобы получить подробную информацию. Вызов одного из методов через неправильное reflect.Type значение приемника вызовет панику.

Пример:

В Go есть 26 видов типов.

В приведенном выше примере мы используем метод Elem для получения типов элементов некоторых типов контейнеров:

  • тип канала;

  • тип карты;

  • тип среза;

  • тип массива.

Фактически, мы также можем использовать этот метод для получения базового типа, являющийся типом указателя. Пример показан на скриншоте.

В приведенном выше примере также показано, как (косвенно) получить значение reflect.Type, представляющее тип интерфейса.

Мы можем получить все типы полей (типа структуры) и информацию о методе типа посредством рефлексии. Мы также можем получить информацию о параметре и типе результата типа функции посредством рефлексии.

Из приведенного выше примера мы понимаем, что:

  1. Для не интерфейсных типов reflect.Type.NumMethod возвращает только количество экспортированных методов типа, включая неявно объявленные. Мы не можем получить информацию о неэкспортированном методе с помощью reflect.Type.MethodByName метода. Для типов интерфейса ограничений не существует, этот факт не упоминался в документах двух методов до Go 1.16. Такая ситуация также относится к соответствующим методам reflect.Value типа.

  2. Хотя вызов метода reflect.Type.NumField возвращает количество всех полей типа структуры, включая неэкспортированные,, не рекомендуется использовать reflect.Type.FieldByName метод для получения информации о неэкспортированном поле.