Все мы сталкиваемся с ситуациями во время программирования, когда нам нужно определить типы переменных или функций. Иногда возникает случай использования, когда вы на самом деле не знаете тип переменной при написании программы (во время компиляции), но вам нужно определить его во время выполнения. Возможно, вы хотите отобразить сетевой запрос в переменную или создать инструмент, который работает с разными типами. Итак, размышление могло бы стать возможным решением таких проблем.

Отражение - это способность программы определять типы переменных или функций во время выполнения. Это форма метапрограммирования.

Частный вариант использования отражения, который обычно встречается при использовании go, - это демаршаллинг JSON в переменную. Для этого мы вызываем функцию json.Unmarshal (a, b). Он принимает два параметра:

  • Первый аргумент имеет тип [] байт.
  • Второй аргумент имеет тип interface {}, то есть тот, который мы хотим заполнить.

Если вы углубитесь в собственную библиотеку encoding / json go, вы попадете в закрытый для пакета метод под названием unmarshal. Соответствующая часть выглядит так:

func (d *decodeState) unmarshal(v interface{}) error {
  rv := reflect.ValueOf(v)
  if rv.Kind() != reflect.Ptr || rv.IsNil() {
    return &InvalidUnmarshalError{reflect.TypeOf(v)}
  }
  d.scan.reset()
  d.scanWhile(scanSkipSpace)
  // We decode rv not rv.Elem because the Unmarshaler interface
  // test must be applied at the top level of the value.
  err := d.value(rv)
  if err != nil {
    return d.addErrorContext(err)
  }
  return d.savedError
}

Он использует отражение для проверки того, что v - это правильный тип переменной, указатель. Если это так, то отраженная версия v, называемая rv, передается методу значения.

Reflect.TypeOf ()

Отражение обеспечивается пакетом отражения. Он предоставляет два важных типа: тип и значение. Функция reflection.Typeof () принимает интерфейс {} и возвращает динамический тип как reflection.Type.

fmt.Println(reflect.TypeOf(2)) // 2

Вызов функции TypeOf (2) присваивает значение 2 параметру интерфейса {}. Присвоение конкретного значения типу интерфейса выполняет неявное преобразование интерфейса {}, которое создает значение интерфейса, состоящее из двух компонентов: его динамический тип - это тип операнда (int), а его динамическое значение - это значение операнда (2).

interface value => dynamic operand's type + dynamic operand's value

Также обратите внимание, что функция fmt.Printf () внутренне использует Reflection.TypeOf для определения типа переменной.

x := 2
fmt.Printf("%T\n", x) // int

отразить.ValueOf ()

Другой важный тип отражающего пакета - это Value. Функция reflection.Valueof () принимает интерфейс {} и возвращает reflection.Value, содержащее динамическое значение интерфейса.

x := 2
fmt.Println(reflect.ValueOf(x)) // 2

Вызов метода Type для Value возвращает его тип как reflection.Type:

x := reflect.ValueOf(2) // a reflect.Value
fmt.Println(x) // 2
t := x.Type()
fmt.Println(t) // int
i := x.Interface() // an interface{}
v := x.(int)
fmt.Println(v) // 2

Reflect.Value и interface {} могут содержать произвольные / случайные значения. Разница заключается в том, что пустой интерфейс скрывает представление и внутренние операции значения, которое он содержит, и не предоставляет ни одного из его методов, поэтому, если мы не знаем его динамический тип и не используем утверждение типа для однорангового взаимодействия внутри него (как мы сделали выше), мы мало что можем сделать с внутренней ценностью. Напротив, у Value есть много методов для проверки своего содержимого, независимо от его типа.

Один из таких внутренних методов, используемых для value, - Kind ().

x := reflect.ValueOf(2)
fmt.Println(x.Kind()) // int

Разница между видом и типом пакета отражения

Давайте поймем разницу на примере, который проиллюстрирован ниже:

package main

import (  
    "fmt"
    "reflect"
)

type Transactions struct {  
    TransactionID string `json:"txn_id,omitempty"`
    Amount        float64 `json:"amount,omitempty"`
}

func printTypes(i interface{}) {  
    type := reflect.TypeOf(i)
    kind := t.Kind()
    fmt.Println("Type ", type)
    fmt.Println("Kind ", kind)
}
func main() {  
    structForTxn := Transactions{
        TransactionID: "abc-1231-cd",
        Amount: 100,
    }
    printTypes(structForTxn)
}

Результатом для вышеуказанной программы будет:

Type  main.Transactions
Kind  struct

Думаю, теперь вам будет ясно, в чем разница между ними. Тип представляет фактический тип интерфейса {}, в данном случае main.Transactions и Kind представляет конкретный тип типа. В данном случае это структура.

Это все, ребята. Это была моя первая попытка писать блоги на среде. Надеюсь, я смог кому-то помочь.

Ссылки: https://www.gopl.io/