>백엔드 개발 >Golang >Go 언어의 반성의 세 가지 법칙은 무엇입니까?

Go 언어의 반성의 세 가지 법칙은 무엇입니까?

青灯夜游
青灯夜游원래의
2023-01-18 16:29:451785검색

반영의 세 가지 법칙: 1. 리플렉션은 "인터페이스 유형 변수"를 "반사 유형 개체"로 변환할 수 있습니다. 여기서 리플렉션 유형은 "reflect.Type" 및 "reflect.Value"를 참조합니다. 2. 리플렉션은 "반사 유형"을 변환할 수 있습니다. 객체" "인터페이스 유형 변수"로 변환; 3. "반사 유형 객체"를 수정하려면 해당 값이 "쓰기 가능"해야 합니다.

Go 언어의 반성의 세 가지 법칙은 무엇입니까?

이 튜토리얼의 운영 환경: Windows 7 시스템, GO 버전 1.18, Dell G3 컴퓨터.

반영의 세계에서는 객체의 유형, 속성 및 메서드를 얻을 수 있는 능력이 있습니다.

Go 언어의 반성의 세 가지 법칙은 무엇입니까?

두 가지 유형: 유형과 값

Go 리플렉션의 세계에는 매우 중요하고 전체 리플렉션의 핵심인 두 가지 유형이 있습니다. 다음 두 가지 유형을 알아보세요.

  • reflect.Type

  • reflect.Value

각각 실제 세계의 유형과 값에 해당하지만 리플렉션 개체에는 더 많은 콘텐츠가 있습니다.

소스코드에서 보면, Reflect.Type은 인터페이스

type Type interface {
    Align() int
    FieldAlign() int
    Method(int) Method
    MethodByName(string) (Method, bool)
    NumMethod() int
    Name() string
    PkgPath() string
    Size() uintptr
    String() string
    Kind() Kind
    Implements(u Type) bool
    AssignableTo(u Type) bool
    ConvertibleTo(u Type) bool
    Comparable() bool
    Bits() int
    ChanDir() ChanDir
    IsVariadic() bool
    Elem() Type
    Field(i int) StructField
    FieldByIndex(index []int) StructField
    FieldByName(name string) (StructField, bool)
    FieldByNameFunc(match func(string) bool) (StructField, bool)
    In(i int) Type
    Key() Type
    Len() int
    NumField() int
    NumIn() int
    NumOut() int
    Out(i int) Type
    common() *rtype
    uncommon() *uncommonType
}

형태로 존재하고, Reflect.Value는 구조체

type Value struct {
    typ *rtype
    ptr unsafe.Pointer
    flag
}

형태로 존재함과 동시에 많은 메소드를 받습니다(표 참조) 아래), 지면의 제약으로 인해 여기서는 하나씩 소개할 방법이 없습니다.

Addr
Bool
Bytes
runes
CanAddr
CanSet
Call
CallSlice
call
Cap
Close
Complex
Elem
Field
FieldByIndex
FieldByName
FieldByNameFunc
Float
Index
Int
CanInterface
Interface
InterfaceData
IsNil
IsValid
IsZero
Kind
Len
MapIndex
MapKeys
MapRange
Method
NumMethod
MethodByName
NumField
OverflowComplex
OverflowFloat
OverflowInt
OverflowUint
Pointer
Recv
recv
Send
send
Set
SetBool
SetBytes
setRunes
SetComplex
SetFloat
SetInt
SetLen
SetCap
SetMapIndex
SetUint
SetPointer
SetString
Slice
Slice3
String
TryRecv
TrySend
Type
Uint
UnsafeAddr
assignTo
Convert

이전 섹션()의 내용을 통해 인터페이스 변수는 실제로는 한 쌍(타입과 데이터)으로 구성되며, 그 쌍은 실제 변수의 값과 타입을 기록한다는 것을 알 수 있습니다. 즉, 현실 세계에서는 유형과 값이 결합되어 인터페이스 변수를 형성합니다.

Reflection의 세계에서는 타입과 데이터가 분리되어 각각 Reflect.Type과 Reflect.Value로 표현됩니다.

Reflection of the three law of Reflection

Go 언어에는 세 가지 성찰 법칙이 있는데, 이는 성찰을 배울 때 매우 중요한 참고 자료입니다.

  • Reflection은 인터페이스 값에서 Reflection 객체로 이동합니다.

  • 반사는 반사 객체에서 인터페이스 값으로 진행됩니다.

  • 반사 객체를 수정하려면 값을 설정할 수 있어야 합니다. 유형 객체";

Reflection은 "반사 유형 객체"를 "인터페이스"로 변환할 수 있습니다. 유형 변수";

  • "반사 유형 개체"를 수정하려면 해당 유형이 "쓰기 가능"이어야 합니다.

  • 제1법칙
  • 반영은 인터페이스 값에서 반사 개체로 이동합니다.

인터페이스 변수를 리플렉션 개체로 변환하려면 Reflect 패키지에서 매우 중요한 두 가지 메서드를 언급해야 합니다.

reflect TypeOf(i): 인터페이스 값의 유형 가져오기

reflect .ValueOf(i): 인터페이스 값의 값을 가져옵니다.

  • 이 두 메서드에서 반환된 개체를 반사 개체라고 합니다: Type 개체 및 Value 개체.

  • 예를 들어 이 두 가지 방법이 어떻게 사용되는지 볼까요?
  • package main
    
    import (
    "fmt"
    "reflect"
    )
    
    func main() {
        var age interface{} = 25
    
        fmt.Printf("原始接口变量的类型为 %T,值为 %v \n", age, age)
    
        t := reflect.TypeOf(age)
        v := reflect.ValueOf(age)
    
        // 从接口变量到反射对象
        fmt.Printf("从接口变量到反射对象:Type对象的类型为 %T \n", t)
        fmt.Printf("从接口变量到反射对象:Value对象的类型为 %T \n", v)
    
    }
출력은 다음과 같습니다

原始接口变量的类型为 int,值为 25 从接口变量到反射对象:Type对象的类型为 *reflect.rtype 
从接口变量到反射对象:Value对象的类型为 reflect.Value 复制代码
이렇게 인터페이스 유형 변수에서 리플렉션 객체로의 변환이 완료되었습니다.

Go 언어의 반성의 세 가지 법칙은 무엇입니까?잠깐만요. 위에서 정의한 나이가 int 유형이 아닌가요? 첫 번째 규칙에서 인터페이스 유형이라고 말하는 이유는 무엇입니까?

이 점에 관해서는 사실 이전 섹션(

인터페이스에 대한 세 가지 "숨겨진 규칙"

)에서 언급한 바 있는데, TypeOf와 ValueOf 두 함수는 인터페이스{} 빈 인터페이스 유형을 수신하고 Go 언어 함수는 모두 값으로 전달되므로 Go 언어는 암시적으로 유형을 인터페이스 유형으로 변환합니다.

原始接口变量的类型为 int,值为 25 
从接口变量到反射对象:Type对象的类型为 *reflect.rtype 
从接口变量到反射对象:Value对象的类型为 reflect.Value

제2법칙

리플렉션은 리플렉션 객체에서 인터페이스 값으로 이동합니다.

제1법칙과 정반대인 제2법칙은 리플렉션 객체에서 인터페이스 변수로의 변환을 설명합니다.

reflect.Value의 구조가

유형의 변수를 수신한다는 것을 소스 코드에서 볼 수 있습니다. (
참고: Value만 역변환할 수 있지만 Type은 변환할 수 없습니다. 이것도 이해하기 쉽습니다. Type

)

// Interface returns v's current value as an interface{}.
// It is equivalent to:
//    var i interface{} = (v's underlying value)
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
    return valueInterface(v, true)
}

이 함수는 반사 객체를 인터페이스 변수로 변환하는 데 사용하는 브리지입니다. Go 언어의 반성의 세 가지 법칙은 무엇입니까?

예제는 다음과 같습니다

package main

import (
"fmt"
"reflect"
)

func main() {
    var age interface{} = 25

    fmt.Printf("原始接口变量的类型为 %T,值为 %v \n", age, age)

    t := reflect.TypeOf(age)
    v := reflect.ValueOf(age)

    // 从接口变量到反射对象
    fmt.Printf("从接口变量到反射对象:Type对象的类型为 %T \n", t)
    fmt.Printf("从接口变量到反射对象:Value对象的类型为 %T \n", v)

    // 从反射对象到接口变量
    i := v.Interface()
    fmt.Printf("从反射对象到接口变量:新对象的类型为 %T 值为 %v \n", i, i)

}

输出如下

原始接口变量的类型为 int,值为 25 
从接口变量到反射对象:Type对象的类型为 *reflect.rtype 
从接口变量到反射对象:Value对象的类型为 reflect.Value 
从反射对象到接口变量:新对象的类型为 int 值为 25

当然了,最后转换后的对象,静态类型为 interface{} ,如果要转成最初的原始类型,需要再类型断言转换一下,关于这点,我已经在上一节里讲解过了,你可以点此前往复习:()。

i := v.Interface().(int)

至此,我们已经学习了反射的两大定律,对这两个定律的理解,我画了一张图,你可以用下面这张图来加强理解,方便记忆。

Go 언어의 반성의 세 가지 법칙은 무엇입니까?

第三定律

To modify a reflection object, the value must be settable.

反射世界是真实世界的一个『映射』,是我的一个描述,但这并不严格,因为并不是你在反射世界里所做的事情都会还原到真实世界里。

第三定律引出了一个 settable (可设置性,或可写性)的概念。

其实早在以前的文章中,我们就一直在说,Go 语言里的函数都是值传递,只要你传递的不是变量的指针,你在函数内部对变量的修改是不会影响到原始的变量的。

回到反射上来,当你使用 reflect.Typeof 和 reflect.Valueof 的时候,如果传递的不是接口变量的指针,反射世界里的变量值始终将只是真实世界里的一个拷贝,你对该反射对象进行修改,并不能反映到真实世界里。

因此在反射的规则里

  • 不是接收变量指针创建的反射对象,是不具备『可写性』的
  • 是否具备『可写性』,可使用 CanSet() 来获取得知
  • 对不具备『可写性』的对象进行修改,是没有意义的,也认为是不合法的,因此会报错。
package main

import (
    "fmt"
    "reflect"
)

func main() {
    var name string = "Go编程时光"

    v := reflect.ValueOf(name)
    fmt.Println("可写性为:", v.CanSet())
}

输出如下

可写性为: false

要让反射对象具备可写性,需要注意两点

  • 创建反射对象时传入变量的指针

  • 使用 Elem()函数返回指针指向的数据

完整代码如下

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var name string = "Go编程时光"
    v1 := reflect.ValueOf(&name)
    fmt.Println("v1 可写性为:", v1.CanSet())

    v2 := v1.Elem()
    fmt.Println("v2 可写性为:", v2.CanSet())
}

输出如下

v1 可写性为: false
v2 可写性为: true

知道了如何使反射的世界里的对象具有可写性后,接下来是时候了解一下如何对修改更新它。

反射对象,都会有如下几个以 Set 单词开头的方法

Go 언어의 반성의 세 가지 법칙은 무엇입니까?

这些方法就是我们修改值的入口。

来举个例子

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var name string = "Go编程时光"
    fmt.Println("真实世界里 name 的原始值为:", name)

    v1 := reflect.ValueOf(&name)
    v2 := v1.Elem()

    v2.SetString("Python编程时光")
    fmt.Println("通过反射对象进行更新后,真实世界里 name 变为:", name)
}

输出如下

真实世界里 name 的原始值为: Go编程时光
通过反射对象进行更新后,真实世界里 name 变为: Python编程时光

【相关推荐:Go视频教程编程教学

위 내용은 Go 언어의 반성의 세 가지 법칙은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.