go использует (*interface{})(nil) для передачи типов параметров

Go

Пожалуйста, указывайте источник при перепечатке оригинальной статьи

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

Сначала посмотрите на объявление типа инжектора:

type injector struct {       
  values map[reflect.Type]reflect.Value       
  parent Injector
}

отложить в сторонуparentне вижу,valuesэто таблица сопоставления, используемая для сохранения введенных параметров, этоreflect.Typeкогда ключ,reflect.Valueпредставляет собой карту ценностей.

parent InjectorКакого черта?

// Injector represents an interface for mapping and injecting dependencies into structs
// and function arguments.
type Injector interface {
    Applicator
    Invoker
    TypeMapper
    // SetParent sets the parent of the injector. If the injector cannot find a
    // dependency in its Type map it will check its parent before returning an
    // error.
    SetParent(Injector)
}

// Applicator represents an interface for mapping dependencies to a struct.
type Applicator interface {
    // Maps dependencies in the Type map to each field in the struct
    // that is tagged with 'inject'. Returns an error if the injection
    // fails.
    Apply(interface{}) error
}

// Invoker represents an interface for calling functions via reflection.
type Invoker interface {
    // Invoke attempts to call the interface{} provided as a function,
    // providing dependencies for function arguments based on Type. Returns
    // a slice of reflect.Value representing the returned values of the function.
    // Returns an error if the injection fails.
    Invoke(interface{}) ([]reflect.Value, error)
}

// TypeMapper represents an interface for mapping interface{} values based on type.
type TypeMapper interface {
    // Maps the interface{} value based on its immediate type from reflect.TypeOf.
    Map(interface{}) TypeMapper
    // Maps the interface{} value based on the pointer of an Interface provided.
    // This is really only useful for mapping a value as an interface, as interfaces
    // cannot at this time be referenced directly without a pointer.
    MapTo(interface{}, interface{}) TypeMapper
    // Provides a possibility to directly insert a mapping based on type and value.
    // This makes it possible to directly map type arguments not possible to instantiate
    // with reflect like unidirectional channels.
    Set(reflect.Type, reflect.Value) TypeMapper
    // Returns the Value that is mapped to the current type. Returns a zeroed Value if
    // the Type has not been mapped.
    Get(reflect.Type) reflect.Value
}

Injector представляет собой комбинацию объявлений интерфейса инъекции. Сначала мы сосредоточимся на интерфейсе TypeMapper. Из исходного кода мы можем знать, что Map и MapTo используются для сопоставления типов данных и данных сvalues map[reflect.Type]reflect.ValueМетоды.

Метод Map относительно прост и использует отражение для получения типа объекта.

func (i *injector) Map(val interface{}) TypeMapper {
    i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
    return i
}

Теперь предположим, что когда в параметре несколько строк,values map[reflect.Type]reflect.ValueЭта карта сохранит только отображение последней строки, так что же нам делать с ней, чтобы полностью сохранить все строковые параметры?

рассмотреть возможностьinterfaceБазовая реализация типа (тип, данные),injectВ библиотеке реализована функция получения типа из указателя на интерфейс.InterfaceOf,а такжеMapToзатем используйтеInterfaceOfчтобы получить тип входящих данных.

func InterfaceOf(value interface{}) reflect.Type {       
  t := reflect.TypeOf(value)       

  for t.Kind() == reflect.Ptr {              
    t = t.Elem()       
  }       

  if t.Kind() != reflect.Interface {              
    panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")       
  }       
  return t
}

func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
    i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
    return i
}

Это гениальный ход, давайте найдем чужой пример:

package main

import ( 
  "fmt" 
  "github.com/codegangsta/inject"
)

type SpecialString interface{}

func main() {    
  fmt.Println(inject.InterfaceOf((*interface{})(nil)))        
  fmt.Println(inject.InterfaceOf((*SpecialString)(nil)))
}

выход

interface {}
main.SpecialString

видеть это? нулевой указатель на интерфейс, хотяdataноль, но мы просто хотим этогоtype. Пошаговое объяснение:

//以(*SpecialString)(nil)为例
t := reflect.TypeOf(value) //t是*main.SpecialString,t.Kind()是ptr,t.Elem()是main.SpecialString
for t.Kind() == reflect.Ptr { //循环判断,也许是指向指针的指针
  t = t.Elem() //Elem returns a type's element type.
}
if t.Kind() != reflect.Interface {
  ... //如果不是Interface类型,报panic
}
return t //返回(*SpecialString)(nil)的元素原始类型

interface{}что вgoвinterface{}всемогущAny.injectИспользуя преимущества характеристик (*interface{})(nil), переносящих типы данных, для завершения передачи типов данных используется только нулевой указатель, а связывание одного и того же типа данных расширяется.

пойдемmartini.goДавайте посмотрим, как работает эта инъекция.

// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.
type Martini struct {       
  inject.Injector       
  handlers []Handler       
  action   Handler       
  logger   *log.Logger
}

// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used.
func New() *Martini {       
  m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}       
  m.Map(m.logger)       
  m.Map(defaultReturnHandler())       
  return m
}

func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
    c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0}
    c.SetParent(m)
    c.MapTo(c, (*Context)(nil))
    c.MapTo(c.rw, (*http.ResponseWriter)(nil))
    c.Map(req)
    return c
}

индивидуальныеMartiniСтруктура содержитinject.Injectorинтерфейс, поэтому его можно легко ввестиlogger. следовать заInvokeПри использовании промежуточного программного обеспечения вы, естественно, можете передатьInjectorизGetспособ получитьloggerобъект.contextЗатем используйте метод MapTo для внедренияContextа такжеhttp.ResponseWriterЭти два типа интерфейса.

ТакInvokeКак вы вызываете функцию и вводите параметры, когда это так? пожалуйста, переместите«Как Invoke динамически передает параметры»

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