이 글에서는 당신에 대한 새로운 이해를 얻기 위해 GolangMedium Reflection에 대해 주로 이야기할 것입니다.
많은 사람들이 Go 언어를 일정 기간 사용해왔고, 심지어 1~2년 정도 사용한 사람도 있지만, 여전히 Go 언어에 대한 성찰에 대해 모호하고 자신감이 별로 없습니다. 그것을 사용할 때. [관련 권장사항: Go 비디오 튜토리얼, 프로그래밍 교육]
게다가 반사도 거의 사용하지 않습니다. 물론 직장에서 사용하는 것이 가장 간단하고 효율적입니다. 확장 가능하고 성능이 좋습니다. 일부 고급 사용법을 기계적으로 복사할 필요가 없습니다. 우리는 많은 것을 실험할 수 있습니다. 이번에는 성찰 놀이 방법에 대해 자세히 살펴보겠습니다
기사
간단히 말해서, Reflection은 프로그램이 실행되는 동안 프로그램 자체에 액세스하고 수정하는 기능입니다. 예를 들어 프로그램이 실행 중일 때 프로그램의 필드 이름과 필드 값을 수정할 수 있으며, 프로그램 정보 등에 대한 인터페이스 액세스를 제공할 수도 있습니다.
이것은 Go 언어에서 제공되는 메커니즘입니다. . Go 언어 공용 라이브러리에서 Reflect의 사용에 대해 많이 볼 수 있습니다.
예를 들어 일반적으로 사용되는 fmt 패키지, commonly Used json 시퀀스 물론 앞서 언급한 gorm 라이브러리에서도 Reflection을 사용합니다. 그런데 왜 우리는 일반적으로 리플렉션을 사용합니까?
반사 능력을 바탕으로 당연히 우리가 제공하는 인터페이스는 들어오는 데이터 유형이 무엇인지 모르기 때문에 특정 데이터 유형은 프로그램이 실행될 때만 알 수 있습니다
하지만 코딩할 때 예상합니다. 프로그램이 실행될 때 전달된 유형(예: json 직렬화)과 이 특정 데이터에 대해 작업을 수행하려면 이때 리플렉션 기능을 사용해야 합니다그래서 사용하려면
인터페이스를 볼 수 있다는 것이 놀랍지 않나요? {}어디에 반영되나요? 들어오는 데이터 유형이 무엇인지 확실하지 않기 때문에 이를 인터페이스로 설계했습니다{}. 인터페이스의 특성과 사용법에 대해 잘 모르는 경우 다음 기사를 확인하세요.
인터페이스{}에 관해 어떤 점에 주의해야 합니까?
인터페이스{}에 관해 어떤 점에 주의해야 합니까? 다음으로
We 만약 당신이 런타임에 리플렉션 유형 개체를 수정하려면 이 개체에 해당하는 값을 쓸 수 있어야 합니다. 위의 세 가지 규칙도 비교적 이해하기 쉽습니다.
우리는 일반적으로 사용하는 데이터 유형을 패키지(예: 안전하지 않은 패키지 또는 반영 패키지)의 지정된 데이터 유형으로 변환한 다음 패키지의 규칙에 따라 데이터를 수정합니다법칙 1의 경우 인터페이스 유형 변수를 반사 유형 객체로 변환합니다
실제로 여기에 언급된 인터페이스 유형 변수의 경우etc.
와 같은 모든 데이터 유형의 변수를 전달할 수 있습니다.Reflection 유형 객체는 여기서 제공되는 reflect.Type
和 reflect.Value
对象,可以通过 reflect 包中提供的 TypeOf 和 ValueOf 函数得到
其中 reflect.Type
实际上是一个 interface ,他里面包含了各种接口需要进行实现,它里面提供了关于类型相关的信息
其中如下图可以查看到 reflect.Type
的所有方法,其中
reflect.Value
实际上是一个 struct,根据这个 struct 还关联了一组方法,这里面存放了数据类型和具体的数据,通过查看其数据结构就可以看出
type Value struct { typ *rtype ptr unsafe.Pointer flag }
看到此处的 unsafe.Pointer 是不是很熟悉,底层自然就可以将 unsafe.Pointer
转换成 uintptr
,然后再修改其数据后,再转换回来,对于 Go 指针不太熟悉的可以查看这篇文章:
写一个简单的 demo 就可以简单的获取到变量的数据类型和值
func main() { var demoStr string = "now reflect" fmt.Println("type:", reflect.TypeOf(demoStr)) fmt.Println("value:", reflect.ValueOf(demoStr)) }
我们可以通过将 reflect.Value
类型转换成我们具体的数据类型,因为 reflect.Value
中有对应的 typ *rtype
以及 ptr unsafe.Pointer
例如我们可以 通过 reflect.Value
对象的 interface() 方法来处理
func main() { var demoStr string = "now reflect" fmt.Println("type:", reflect.TypeOf(demoStr)) fmt.Println("value:", reflect.ValueOf(demoStr)) var res string res = reflect.ValueOf(demoStr).Interface().(string) fmt.Println("res == ",res) }
首先我们看上书的 demo 代码,传入 TypeOf 和 ValueOf 的变量实际上也是一个拷贝,那么如果期望在反射类型的对象中修改其值,那么就需要拿到具体变量的地址然后再进行修改,前提是这个变量是可写的
举个例子你就能明白
func main() { var demoStr string = "now reflect" v := reflect.ValueOf(demoStr) fmt.Println("is canset ", v.CanSet()) //v.SetString("hello world") // 会panic }
可以先调用 reflect.Value
对象的 CanSet
查看是否可写,如果是可写的,我们再写,如果不可写就不要写了,否则会 panic
那么传入变量的地址就可以修改了??
传入地址的思路没有毛病,但是我们去设置值的方式有问题,因此也会出现上述的 panic 情况
此处仔细看能够明白,反射的对象 v 自然是不可修改的,我们应该找到 reflect.Value
里面具体具体的数据指针,那么才是可以修改的,可以使用 reflect.Value
TypeOf 및 를 통해 액세스할 수 있는 Reflect 패키지의 reflect.Type
및 Reflect.Value
객체로 이해될 수 있습니다. Reflect 패키지에서 ValueOf
여기서 reflect.Type
은 실제로 구현해야 하는 다양한 인터페이스가 포함된 인터페이스이며 볼 수 있는 유형
reflect.Type
의 모든 메소드에
green
은 모든 데이터 타입을 호출할 수 있으며 Reflect.Value
는 실제로 구조체입니다. 이 구조체에 따르면 데이터 유형과 특정 데이터를 저장하는 메서드 집합도 연결되어 있습니다. 데이터 구조를 보면 알 수 있습니다🎜type RDemo struct { Name string Age int Money float32 Hobby map[string][]string } func main() { tmp := &RDemo{ Name: "xiaomiong", Age: 18, Money: 25.6, Hobby: map[string][]string{ "sport": {"basketball", "football"}, "food": {"beef"}, }, } v := reflect.ValueOf(tmp).Elem() // 拿到结构体对象 h := v.FieldByName("Hobby") // 拿到 Hobby 对象 h1 := h.MapKeys()[0] // 拿到 Hobby 的第 0 个key fmt.Println("key1 name == ",h1.Interface().(string)) sli := h.MapIndex(h1) // 拿到 Hobby 的第 0 个key对应的对象 str := sli.Index(1) // 拿到切片的第 1 个对象 fmt.Println(str.CanSet()) str.SetString("helloworld") fmt.Println("tmp == ",tmp) }🎜여기를 참조하세요. unsafe.Pointer에 익숙하십니까? 맨 아래 레이어는 자연스럽게
unsafe.Pointer
를 uintptr
로 변환한 다음 해당 데이터를 수정하고 다시 변환할 수 있습니다. not 너무 익숙하다면 다음 기사를 확인해 보세요: 🎜🎜🎜GO의 포인터? 🎜🎜🎜변수의 데이터 유형과 값을 쉽게 얻을 수 있는 간단한 데모를 작성하세요🎜// rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { size uintptr ptrdata uintptr hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 equal func(unsafe.Pointer, unsafe.Pointer) bool gcdata *byte str nameOff ptrToThis typeOff }🎜🎜
reflect.Value
유형을 특정 데이터 유형에 입력하세요. reflect.Value
및 ptr unsafe에 해당 <code>typ *rtype
이 있기 때문입니다. 포인터🎜🎜예를 들어 reflect.Value
객체🎜🎜🎜// emptyInterface is the header for an interface{} value.type emptyInterface struct { typ *rtype word unsafe.Pointer }复制代码🎜🎜
reflect.Value
객체의 CanSet
를 호출하여 쓰기 가능한지 확인할 수 있습니다. 즉, 다시 쓰자. 쓸 수 없으면 쓰지 마세요. 그렇지 않으면 패닉 상태가 됩니다🎜🎜🎜🎜그럼 전달된 변수의 주소를 수정할 수 있다는 건가요? ? 🎜🎜🎜🎜들어오는 주소가 있습니다 아이디어에는 문제가 없지만 값을 설정하는 방식에 문제가 있으므로 위에서 언급한 패닉 상황도 발생하게 됩니다. 여기서 잘 살펴보면 반영된 객체 v가 당연히 수정 불가능하다는 것을 알 수 있습니다. reflect.Value
의 특정 데이터 포인터는 약간 더 복잡한 경우 reflect.Value
의 🎜Elem🎜 메서드를 사용할 수 있습니다. , 이렇게 단순한 경우라고 생각하실 수도 있고, 시연만 하면 괜찮겠지만, 직장에서 사용하면 바로 크래쉬가 납니다. 당연히 아직 완전히 이해되지는 않았습니다. 잘 소화되지 않았습니다. 작업에 대한 또 다른 예가 있습니다🎜🎜🎜구조에는 맵이 있고 맵의 키는 문자열이며 값은 []string🎜🎜🎜🎜입니다. 요구 사항은 🎜1🎜번째에 액세스하는 것입니다. 맵 키 🎜sport🎜를 사용하여 구조에서 취미 분야에 해당하는 슬라이스 요소를 추가하고 🎜 hellolworld🎜🎜🎜rrreee🎜🎜🎜로 수정합니다.可以看到上述案例运行之后有时可以运行成功,有时会出现 panic 的情况,相信细心的 xdm 就可以看出来,是因为 map 中的 key 是 无序的导致的,此处也提醒一波,使用 map 的时候要注意这一点
看上述代码,是不是就能够明白咱们使用反射去找到对应的数据类型,然后按照数据类型进行处理数据的过程了呢
有需要的话,可以慢慢的去熟练反射包中涉及的函数,重点是要了解其三个规则,对象转换方式,访问方式,以及数据修改方式
那么通过上述案例,可以知道关于反射中数据类型和数据指针对应的值是相当重要的,不同的数据类型能够用哪些函数这个需要注意,否则用错直接就会 panic
来看 TypeOf 的接口中涉及的数据结构
在 reflect 包中 rtype
是非常重要的,Go 中所有的类型都会包含这个结构,所以咱们反射可以应用起来,结构如下
// rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { size uintptr ptrdata uintptr hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 equal func(unsafe.Pointer, unsafe.Pointer) bool gcdata *byte str nameOff ptrToThis typeOff }
其中可以看到此处的 rtype
的结构保持和 runtime/type.go
一致 ,都是关于数据类型的表示,以及对应的指针,关于这一块的说明和演示可以查看文末的 interface{} 处的内容
从 ValueOf 的源码中,我们可以看到,重要的是 emptyInterface 结构
// emptyInterface is the header for an interface{} value.type emptyInterface struct { typ *rtype word unsafe.Pointer }复制代码
emptyInterface 结构中有 rtype
类型的指针, word 自然是对应的数据的地址了
reflect.Value
对象中的方法也是非常的多,用起来和上述说到的 reflect.Type
接口中的功能类似
关于源码中涉及到的方法,就不再过多的赘述了,更多的还是需要自己多多实践才能体会的更好
殊不知,此处的 reflect.Value
也是可以转换成 reflect.Type
,可以查看源码中 reflect\value.go
的 func (v Value) Type() Type {
其中 reflect.Value
,reflect.Type
,和任意数据类型
可以相互这样来转换
如下图:
至此,关于反射就聊到这里,一些关于源码的细节并没有详细说,更多的站在一个使用者的角度去看反射需要注意的点
关于反射,大多的人是建议少用,因为是会影响到性能,不过如果不太关注这一点,那么用起来还是非常方便的
高级功能自然也是双刃剑,你用不好就会 panic,如果你期望去使用他,那么就去更多的深入了解和一步一步的吃透他吧
大道至简,反射三定律,活学活用
更多编程相关知识,请访问:编程视频!!
위 내용은 Golang의 리플렉션을 자세히 설명하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!