Golang反射

文章目錄

  • 基本介紹
  • reflect包
    • reflect.Type
    • reflect.Value
    • reflect.Kind
    • 具體類型、空接口與reflect.Value的相互轉換
  • 反射應用場景
    • 修改變量的值
    • 訪問結構體的字段信息
    • 調用變量所綁定的方法
    • 實現函數適配器
    • 創建任意類型的變量

基本介紹

基本介紹

  • 在Go中,反射(reflection)是一種機制,其允許程序在運行時檢查并操作變量、類型和結構的信息,而不需要提前知道它們的具體定義,使得代碼更加靈活和通用。
  • 反射通常用于動態獲取獲取類型信息、動態創建對象、動態調用函數、動態修改對象等,在實現反射時需要用到reflect包。
  • 需要注意的是,雖然反射的功能強大,但由于其使用了運行時的類型檢查和動態調用,在性能上可能會有一定的開銷,因此在性能敏感的場景中,應該盡量避免過度依賴反射來實現常規的編程任務。

reflect包

reflect.Type

reflect.Type

  • reflect.Type是reflect包中的一個接口類型,用于表示任意變量的類型信息。
  • 通過reflect包中的TypeOf函數,可以獲取指定變量的類型信息。

reflect.TypeOf函數的函數原型如下:

func TypeOf(i interface{}) Type

reflect.Type接口中常用的方法如下:

方法名功能
Kind獲取該類型對應的Kind
Size獲取該類型的大小
Elem獲取該類型的元素的Type
NumField獲取結構體類型的字段數
NumMethod獲取該類型所綁定的方法數
Field獲取結構體類型的第i個字段的信息
Method獲取該類型所綁定的第i個方法的信息
FieldByName獲取結構體類型的字段中,指定字段名的字段信息
MethodByName獲取該類型所綁定的方法中,指定方法名的方法信息
NumIn獲取函數/方法類型的參數個數
In獲取函數/方法類型的第i個參數的Type
NumOut獲取函數/方法類型的返回值個數
Out獲取函數/方法類型的第i個返回值的Type

說明一下:

  • reflect.Type接口中的方法不需要用戶手動實現,這些方法由反射系統在運行時為每個類型自動生成。
  • reflect.Type接口中的方法不是對所有類型都能使用,每個方法都有其特定的適用范圍和前提條件,如果在不滿足調用條件的情況下調用了某個方法,則會觸發panic異常。比如Elem方法只適用于數組、channel、map、指針和切片類型,NumField和Field方法只適用于結構體類型,NumIn、In、NumOut和Out方法只適用于函數或方法類型。

字段信息

通過reflect.Type接口的Field或FieldByName方法,能夠獲取結構體中某個字段的字段信息,獲取到的字段信息通過StructField結構體進行描述。StructField結構體的定義如下:

type StructField struct {Name string    // field namePkgPath string // package pathType      Type      // field typeTag       StructTag // field tag stringOffset    uintptr   // offset within struct, in bytesIndex     []int     // index sequence for Type.FieldByIndexAnonymous bool      // is an embedded field
}

字段說明:

  • Name:表示該字段的名稱。
  • PkgPath:表示該字段所在的包路徑,對于可導出的字段,PkgPath為空字符串。
  • Type:表示該字段對應的reflect.Type。
  • Tag:表示該字段的Tag標簽信息。
  • Offset:表示該字段在結構體中的偏移量。
  • Index:表示該字段的索引序列。
  • Anonymous:表示該字段是否為匿名字段。

方法信息

通過reflect.Type接口的Method或MethodByName方法,能夠獲取對應類型所綁定的某個方法的方法信息,獲取到的方法信息通過Method結構體進行描述。Method結構體的定義如下:

type Method struct {Name string    // method namePkgPath string // package pathType  Type  // method typeFunc  Value // func with receiver as first argumentIndex int   // index for Type.Method
}

字段說明:

  • Name:表示該方法的名稱。
  • PkgPath:表示該方法所在的包路徑,對于可導出的方法,PkgPath為空字符串。
  • Type:表示該方法對應的reflect.Type。
  • Func:表示該方法對應的reflect.Value。
  • Index:表示該方法在對應類型的方法集中的索引。

reflect.Value

reflect.Value

  • reflect.Value是reflect包中的一個類型,用于表示任意變量的值。
  • 通過reflect包中的ValueOf函數,可以獲取持有指定變量的Value。
  • 通過reflect包中的New函數,可以創建指定類型的變量,并獲取持有指向該變量的指針的Value。

reflect.ValueOf函數的函數原型如下:

func ValueOf(i interface{}) Value
func New(typ Type) Value

reflect.Value類型常用的方法如下:

方法名功能
Kind獲取所持有的值對應的Kind
Type獲取所持有的值對應的Type
Elem獲取所持有的接口保管的值的Value封裝,或獲取所持有的指針指向的值的Value封裝
Index獲取所持有的值的第i個元素的Value封裝
NumField獲取所持有的結構體類型值的字段數
NumMethod獲取所持有的值所綁定的方法數
Field獲取所持有的結構體類型值的第i個字段的Value封裝
Method獲取所持有的值所綁定的第i個方法的函數形式的Value封裝
FieldByName獲取所持有的結構體類型值的字段中,指定字段名的字段的Value封裝
MethodByName獲取所持有的值所綁定的方法中,指定方法名的方法的函數形式的Value封裝
Call指定參數調用所持有的函數,返回函數返回值的Value封裝
Interface返回所持有的值的interface{}類型值
Int、Float、Bool、String、Pointer返回所持有的值的對應類型值,如果所持有的值不是對應的類型,則會觸發panic異常
SetInt、SetFloat、SetBool、SetString、SetPointer設置所持有的值,如果所持有的值不是對應的類型,則會觸發panic異常
Set將所持有的值設置為指定Value所持有的值,指定Value所持有值的類型必須與當前所持有值的類型相同,否則會觸發panic異常

說明一下:

  • reflect.Value類型的方法不是對所有類型都能使用,每個方法都有其特定的適用范圍和前提條件,如果在不滿足調用條件的情況下調用了某個方法,則會觸發panic異常。比如Elem方法只適用于接口和指針類型,NumField和Field方法只適用于結構體類型,Index方法只適用于數組、channel、切片和字符串類型,Call方法只適用于函數或方法類型。
  • 通過Method或MethodByName方法,獲取v所持有的值所綁定的某個方法的函數形式的Value封裝時,返回值持有的函數總是使用v所持有的值作為receiver(即第一個參數),因此返回值在調用Call方法時不用手動傳入receiver參數。

reflect.Value與reflect.Type

reflect.Value是一個具體的類型,而reflect.Type被設計成了一個接口類型。其原因如下:

  • 類型的信息需要反射系統在運行時為每個類型自動生成,以適應各種未知的類型,將reflect.Type定義成接口類型的目的就是,指明運行時需要為每個類型生成哪些方法。
  • reflect.Value提供了各種訪問和操作所持有值的方法,在使用reflect.Value時,通常已經知道所持有值的具體類型,這時通過reflect.Type即可獲取到所持有值的類型信息,運行時不需要為其動態的生成任何方法,因此最終將reflect.Value定義成具體類型。

reflect.Kind

reflect.Kind

  • reflect.Kind是reflect包中的一個類型,用于表示類型的類別。
  • reflect.Type和reflect.Value都提供了對應的Kind方法,用于獲取類型的Kind。

reflect.Kind本質是一個常量枚舉類型。其定義如下:

type Kind uintconst (Invalid Kind = iotaBoolIntInt8Int16Int32Int64UintUint8Uint16Uint32Uint64UintptrFloat32Float64Complex64Complex128ArrayChanFuncInterfaceMapPointerSliceStringStructUnsafePointer
)

說明一下:

  • 類型和類別可能是一對一的,比如int類型對應的Kind是Int,float32類型對應的Kind是Float32。類型和類別也可能是一對多的,比如所有結構體類型對應的Kind都是Struct,所有指針類型對應的Kind都是Pointer。
  • 在獲取變量的reflect.Value時,ValueOf函數或提取出接口值中對應的動態類型和動態值,并返回具體類型的Value。如果需要創建一個Kind為Interface的Value,可以先通過ValueOf函數獲取一個指向接口的指針的Value,然后通過Value的Elem方法獲取Value持有的指針指向的值的Value封裝,這時獲取到的Value的Kind就是Interface。

具體類型、空接口與reflect.Value的相互轉換

具體類型、空接口與reflect.Value的相互轉換

在反射過程中,變量的類型經常需要在具體類型、空接口類型和reflect.Value類型之間進行轉換。其轉換的方式如下:

  • 將變量由具體類型轉換為空接口類型時,直接通過變量賦值的方式即可。
  • 將變量由空接口類型轉換為reflect.Value類型時,通過調用reflect.ValueOf函數即可。
  • 將變量由reflect.Value類型轉換為空接口類型時,通過調用reflect.Value的Interface方法即可。
  • 將變量由空接口類型轉為具體類型時,需要借助類型斷言。

轉換示意圖如下:

在這里插入圖片描述

轉換案例如下:

package mainimport ("fmt""reflect"
)type Student struct {Name stringAge  int
}func Reflect(iVal interface{}) { // 具體類型->interface{}rVal := reflect.ValueOf(iVal) // interface{}->reflect.ValueiVal2 := rVal.Interface() // reflect.Value->interface{}switch val := iVal2.(type) { // interface{}->具體類型case Student:fmt.Printf("type = %T, value = %v\n", val, val)case int:fmt.Printf("type = %T, value = %v\n", val, val)case float64:fmt.Printf("type = %T, value = %v\n", val, val)default:fmt.Printf("unknown type: %T\n", val)}
}func main() {var stu = Student{"Alice", 14}Reflect(stu) // type = main.Student, value = {Alice 14}Reflect(1)   // type = int, value = 1Reflect(1.2) // type = float64, value = 1.2
}

反射應用場景

修改變量的值

修改變量的值

通過反射可以修改變量的值,具體步驟如下:

  1. 通過reflect.ValueOf函數,獲取指向該值的指針的Value封裝v1。
  2. 通過Value的Elem方法,獲取v1所持有的指針指向的值的Value封裝v2。
  3. 通過Value的Set系列方法,設置v2所持有的值,完成對變量的修改。

案例如下:

package mainimport ("fmt""reflect"
)func Reflect(iVal interface{}) {rVal := reflect.ValueOf(iVal) // 獲取指向該值的指針的Value封裝switch val := iVal.(type) {case *int:rVal.Elem().SetInt(20) // 獲取所持有的指針指向的值的Value封裝,并設置所持有的值default:fmt.Printf("unknown type: %T\n", val)}
}func main() {var a = 10Reflect(&a) // 傳入的是指向變量的指針fmt.Printf("a = %d\n", a) // a = 20
}

說明一下:

  • 通過反射修改變量的值時,需要通過指向對應變量的指針來修改,這樣反射內部才能找到需要被修改的變量并對其進行修改。在修改變量的值時,需要先通過Value的Elem方法獲取所持有的指針指向的值的Value封裝(可以理解成對指針解引用),然后再調用Set系列方法修改變量的值。
  • 除了通過Set系列方法修改變量的值外,也可以使用Set方法將當前Value所持有的值設置為另一個Value所持有的值。

訪問結構體的字段信息

訪問結構體的字段信息

通過反射可以訪問結構體的字段信息,具體步驟如下:

  1. 通過reflect.ValueOf和reflect.TypeOf函數,分別獲取結構體變量的Value和Type。
  2. 通過Value或Type的NumField方法,獲取結構體的字段數。
  3. 通過Value的Field方法,獲取指定索引字段的Value。
  4. 通過Type的Field方法,獲取指定索引字段的各種信息。

案例如下:

package mainimport ("fmt""reflect"
)type Student struct {Name string `json:"name"`Age  int    `json:"age"`
}func Reflect(iVal interface{}) {rVal := reflect.ValueOf(iVal)rType := reflect.TypeOf(iVal)rKind := rType.Kind()if rKind != reflect.Struct { // 確保傳入的變量是結構體類型return}// 訪問結構體的字段信息num := rType.NumField() // 獲取結構體的字段數for i := 0; i < num; i++ {fieldInfo := rType.Field(i) // 獲取結構體第i個字段的信息filedValue := rVal.Field(i) // 獲取結構體第i個字段的Value封裝fmt.Printf("field[%d] name = %s\ttype = %v\ttag = %s\tvalue = %v\n",i, fieldInfo.Name, fieldInfo.Type, fieldInfo.Tag, filedValue)}
}func main() {var stu = Student{"Alice", 14}Reflect(stu)
}

程序的運行結果如下:

在這里插入圖片描述

說明一下:

  • 上述代碼中通過對變量的Kind進行判斷,以確保傳入的變量是結構體類型。
  • json.Marshal函數在對結構體變量進行JSON序列化時,在函數內部就是通過反射來獲取結構體字段的Tag標簽的。

調用變量所綁定的方法

調用變量所綁定的方法

通過反射可以調用變量所綁定的方法,具體步驟如下:

  1. 通過reflect.ValueOf和reflect.TypeOf函數,分別獲取變量的Value和Type。
  2. 通過Value或Type的NumMethod方法,獲取變量對應的類型所綁定的方法數。
  3. 通過Value的Method方法,獲取指定索引方法的Value。
  4. 通過Type的Method方法,獲取指定索引方法的各種信息。
  5. 通過Value的Call方法,調用所持有的方法。

案例如下:

package mainimport ("fmt""reflect"
)type Student struct {Name string `json:"name"`Age  int    `json:"age"`
}func (stu Student) Study() {fmt.Printf("Study: student %s is studying...\n", stu.Name)
}func (stu *Student) UpdateAge(age int) {stu.Age = agefmt.Printf("UpdateAge: update %s age = %d...\n", stu.Name, stu.Age)
}func (stu Student) StuInfo() {fmt.Printf("StuInfo: name = %s, age = %d...\n", stu.Name, stu.Age)
}func Reflect(iVal interface{}) {rVal := reflect.ValueOf(iVal)rType := reflect.TypeOf(iVal)switch val := iVal.(type) {case *Student, Student:fmt.Printf("------type = %v------\n", rType)// 調用變量對應的類型所綁定的方法num := rType.NumMethod() // 獲取該類型所綁定的方法數fmt.Printf("method num = %d\n", num)for i := 0; i < num; i++ {methodVal := rVal.Method(i)         // 獲取該類型所綁定的第i個方法的Value封裝methodInfo := rType.Method(i)       // 獲取該類型所綁定的第i個方法的信息if methodInfo.Name == "UpdateAge" { // 調用時需要傳參var args []reflect.Valueargs = append(args, reflect.ValueOf(18))methodVal.Call(args) // 調用方法} else {methodVal.Call(nil) // 調用方法}}default:fmt.Printf("unknown type: %T\n", val)}
}func main() {var stu1 = Student{"Alice", 14}Reflect(&stu1)fmt.Printf("stu1 = %v\n", stu1) // stu1 = {Alice 18}var stu2 = Student{"Bob", 14}Reflect(stu2)fmt.Printf("stu2 = %v\n", stu2) // stu2 = {Bob 14}
}

程序的運行結果如下:

在這里插入圖片描述

說明一下:

  • 通過反射獲取變量對應的類型所綁定的方法數,以及獲取指定索引方法的Value封裝或方法信息時,如果變量的類型是type,則只能訪問到receiver為type的方法,如果變量的類型是*type,則能同時訪問到receiver為type*type的方法。
  • 因為receiver為*type的方法中可能會對變量的值進行修改,為了讓反射內部能夠找到需要被修改的變量并對其進行修改,這就要求變量的類型必須是*type,因此如果變量的類型是type,那就無法訪問到receiver為*type的方法。
  • Value的Call方法接收一個類型為[]Value的參數,表示在調用Value所持有的函數或方法時,需要傳入的各個參數的Value封裝,如果被調用的函數或方法無需傳入任何參數,則調用Call方法時傳入nil即可。同時Call方法會返回一個[]Value類型的返回值,表示調用Value所持有的函數或方法得到的各個返回值的Value封裝。

實現函數適配器

實現函數適配器

通過反射可以實現函數適配器,具體步驟如下:

  1. 通過reflect.ValueOf函數,獲取函數的Value。
  2. 對用戶傳入的用于調用函數的參數進行Value封裝,并放到Value切片中。
  3. 通過Value的Call方法,指定參數調用所持有的函數,并返回函數調用的返回值。

案例如下:

package mainimport ("errors""fmt""reflect"
)func AddTwo(num1 int, num2 int) int {return num1 + num2
}func AddThree(num1 int, num2 int, num3 int) int {return num1 + num2 + num3
}func Bridge(f interface{}, args ...interface{}) (ret int, err error) {rVal := reflect.ValueOf(f)rKind := rVal.Kind()if rKind != reflect.Func {err = errors.New("the first arg is not a function")return}// 對傳入的參數進行Value封裝,并放到Value切片中num := len(args)argVals := make([]reflect.Value, num)for i := 0; i < num; i++ {argVals[i] = reflect.ValueOf(args[i])}retVals := rVal.Call(argVals) // 調用函數ret = int(retVals[0].Int())return
}func main() {ret, err := Bridge(AddTwo, 10, 20)if err != nil {fmt.Printf("err = %v\n", err)} else {fmt.Printf("ret = %d\n", ret) // ret = 30}ret, err = Bridge(AddThree, 10, 20, 30)if err != nil {fmt.Printf("err = %v\n", err)} else {fmt.Printf("ret = %d\n", ret) // ret = 60}
}

創建任意類型的變量

創建任意類型變量

通過反射可以創建任意類型的變量,具體步驟如下:

  1. 通過reflect.ValueOf和reflect.TypeOf函數,分別獲取二級指針的Value和Type,并繼續通過Value和Type的Elem方法,分別獲取二級指針指向的一級指針的Value和Type。
  2. 再次通過Type的Elem方法,繼續獲取一級指針指向的元素的Type,即需要創建的變量的類型。
  3. 通過reflect.New函數,創建指定Type的變量,并獲取持有指向該變量的指針的Value封裝elemVal。
  4. 通過Value的Set方法,將一級指針所持有的值設置為elemVal所持有的值,讓一級指針指向創建的變量。

案例如下:

package mainimport ("fmt""reflect"
)type Student struct {Name stringAge  int
}func CreateObj(iVal interface{}) {rType := reflect.TypeOf(iVal).Elem() // 獲取二級指針指向的一級指針的TyperVal := reflect.ValueOf(iVal).Elem() // 獲取二級指針指向的一級指針的ValuerKind := rType.Kind()if rKind != reflect.Ptr { // 確保傳入的是二級指針(該類型指向的是一個指針類型)return}elemType := rType.Elem()         // 獲取一級指針指向的元素的TypeelemVal := reflect.New(elemType) // 創建elemType類型的變量,并獲取持有指向該變量的指針的ValuerVal.Set(elemVal)                // 將一級指針所持有的值設置為elemVal所持有的值
}func main() {var p1 *Studentfmt.Printf("p1 = %v\n", p1) // p1 = <nil>CreateObj(&p1)fmt.Printf("p1 = %v\n", p1) // p1 = &{ 0}var p2 *intfmt.Printf("p2 = %v\n", p2) // p2 = <nil>CreateObj(&p2)fmt.Printf("p2 = %v\n", p2) // p2 = 0xc00000e0f8
}

說明一下:

  • 在創建變量時需要提供一個*type類型的指針,然后根據指針的類型創建一個type類型的變量,并讓該指針指向這個變量,完成變量的創建。由于最終需要修改所給指針變量的指向,因此在調用CreateObj函數時需要傳入該指針的地址(二級指針)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19792.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19792.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19792.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

錯誤 0x80070570:文件或目錄損壞且無法讀取/無法訪問[拒絕訪問]-解決方法

1.起因&#xff1a;在挪動&#xff35;盤文件時&#xff0c;出現無法移動的報錯提示&#xff1a; and無法訪問[拒絕訪問]: 2.原因&#xff3b;大多是胡亂拔出&#xff35;盤&#xff3d; &#xff3b;來自0x80070570 文件或目錄損壞且無法讀取 CHKDSK 修復方法-CSDN博客&#…

iOS AVFoundation 音視頻源碼分享

引言 在現代移動開發中&#xff0c;音視頻處理是一個不可忽視的重要領域。iOS 提供了強大的 AVFoundation 框架&#xff0c;使開發者能夠輕松實現音視頻錄制、播放、編輯等功能。無論是創建高效的視頻播放器&#xff0c;還是實現復雜的音頻處理&#xff0c;AVFoundation 都能提…

Leecode---買賣股票最大利潤問題

121—題目&#xff08;只能買賣一次&#xff09;&#xff1a; 給定一個數組 prices &#xff0c;它的第 i 個元素 prices[i] 表示一支給定股票第 i 天的價格。 你只能選擇 某一天 買入這只股票&#xff0c;并選擇在 未來的某一個不同的日子 賣出該股票。設計一個算法來計算你所…

python-pytorch編寫transformer模型實現問答0.5.00--訓練和預測

python-pytorch編寫transformer模型實現問答0.5.00--訓練和預測 背景代碼訓練預測效果背景 代碼寫不了這么長,接上一篇 https://blog.csdn.net/m0_60688978/article/details/139360270 代碼 # 定義解碼器類 n_layers = 6 # 設置 Decoder 的層數 class Decoder(nn.Module)…

【JavaEE進階】——帶你詳細了解Spring日志以及配置日志

目錄 &#x1f6a9;Spring日志的認識 &#x1f6a9;Spring日志的作用 &#x1f6a9;觀察日志 &#x1f6a9;使用日志 &#x1f388;在程序中得到日志對象 &#x1f388;使??志對象輸出要打印的內容 &#x1f6a9;日志框架的介紹 &#x1f388;門面模式(外觀模式&…

Unity實現簡單的第一人稱控制

先看效果 實現方式 1.首先創建一個腳本 2.編輯腳本內容 付上腳本代碼 private float RotationX 0;public float speed 2f;//移動速度// Use this for initializationvoid Start(){Cursor.lockState CursorLockMode.Locked;//鎖定鼠標到中心點Cursor.visible false;//隱藏鼠…

(CPU/GPU)粒子繼承貼圖顏色發射

GetRandomInfo節點(復制貼進scratch pad Scripts) Begin Object Class/Script/NiagaraEditor.NiagaraClipboardContent Name"NiagaraClipboardContent_22" ExportPath/Script/NiagaraEditor.NiagaraClipboardContent"/Engine/Transient.NiagaraClipboardConten…

uni-app+php 生成微信二維碼 分銷海報

主要代碼如下&#xff0c;可直接復制調試參數&#xff1a; //查詢當前用戶是否有分銷海報public function user_poster(){$this->checkAuth();//查詢會員信息$user $this->getUserInfoById($this->user_id);if(!empty($user[distribution_img])){$result[data] $use…

深入解析力扣170題:兩數之和 III - 數據結構設計(哈希表與雙指針法詳解及模擬面試問答)

在本篇文章中&#xff0c;我們將詳細解讀力扣第170題“兩數之和 III - 數據結構設計”。通過學習本篇文章&#xff0c;讀者將掌握如何設計一個數據結構來支持兩種操作&#xff0c;并了解相關的復雜度分析和模擬面試問答。每種方法都將配以詳細的解釋和ASCII圖解&#xff0c;以便…

頭歌數據結構與算法課程設計易 - 青蛙跳臺階

從前有一只青蛙想跳臺階去等峰&#xff0c;若該青蛙一次可以跳上1級臺階、也可以跳上2級、還可以跳3級。那么改青蛙從第0級臺階出發&#xff0c;在跳上第n級臺階且在第m級臺階停留過時有多少種跳法。 輸入描述&#xff1a; 第一行兩個正整數&#xff0c;n和m m<n 輸出描述&a…

kubernetes鏡像下載頁,離線安裝k8s的資源

kubernetes-apt-pool安裝包下載_開源鏡像站-阿里云 (aliyun.com) 【Kubernetes】Kubernetes各大版本的最新版本下載地址_kubet軟件下載-CSDN博客

單位職員尤其女性,若你有文才那將前途無量!

單位職員尤其女性&#xff0c;若你有文才那將前途無量&#xff01; 公司職員尤其女性&#xff0c;若文才出眾&#xff0c;恭喜你&#xff1a;提拔重用你是早晚的事&#xff01;不信看我給你分析-- 再說機關、企事業單位的職員&#xff0c;尤其是體制內職工&#xff0c;你若會寫…

C# List

C# List 創建 List:添加元素:使用 AddRange 方法添加多個元素&#xff1a;插入元素:訪問元素:移除元素:使用 Remove 方法移除一個元素&#xff1a;使用 RemoveAt 方法移除指定索引的元素&#xff1a;使用 RemoveAll 方法移除滿足條件的所有元素&#xff1a; 查找元素:使用 Cont…

Goby 漏洞發布|萬戶ezEIP企業管理系統 /member/success.aspx 命令執行漏洞

漏洞名稱&#xff1a;萬戶ezEIP企業管理系統 /member/success.aspx 命令執行漏洞 English Name&#xff1a;Wanhu-ez-EIP /member/success.aspx Command Execution Vulnerability CVSS core: 9.0 影響資產數&#xff1a;6175 漏洞描述&#xff1a; 萬戶ezEIP是一種企業資源…

在CentOS7下構建TeamSpeak服務器并增加網易云點歌插件

文章目錄 部署TeamSpeak創建一個新用戶下載并解壓服務端下載解壓 啟動服務端同意許可協議啟動與配置開放端口設置開機自啟 客戶端連接 部署TS3AudioBot并添加網易云插件安裝ffmpeg下載TS3AudioBot本體與插件并解壓配置TS3AudioBot啟動設置開機自啟 部署網易云API安裝git安裝Nod…

解讀vue3源碼-2

提示&#xff1a;看到我 請讓滾去學習 vue3編譯模版的提升 文章目錄 vue3編譯模版的提升靜態節點提升補丁標志和block的使用附錄&#xff1a; template explorer可以將我們的源模版轉化成渲染函數代碼&#xff0c;vue2中就有&#xff0c;而Vue3 template explorer 功能更加豐富…

外匯天眼:ESMA發布針對在投資服務中使用人工智能的公司的指導意見

歐洲證券和市場管理局&#xff08;ESMA&#xff09;&#xff0c;歐盟的金融市場監管機構和監督機構&#xff0c;發布了一份聲明&#xff0c;為在向零售客戶提供投資服務時使用人工智能技術&#xff08;AI&#xff09;的公司提供初步指導。 盡管人工智能的普及仍處于初期階段&am…

請描述Vue常用的修飾符

在 Vue 中&#xff0c;修飾符&#xff08;Modifiers&#xff09;常用于自定義指令&#xff08;Directives&#xff09;和事件監聽&#xff08;Event Listeners&#xff09;中&#xff0c;以改變指令或事件監聽器的默認行為。以下是一些 Vue 中常用的修飾符&#xff1a; 1. 事件…

你認識nginx嗎,nginx是做什么的,nginx可以做什么 --2)nginx配置

hello大家今天教大家如何用nginx實驗tomcat的負載均衡&#xff0c;同理其他的也可以&#xff0c;如httpd等 首先需要準備一個nginx和tomcat包&#xff0c;這里用到的是版本號為 然后需要準備最少三臺linux虛擬機&#xff0c;然后我們開始吧 1.安裝tomcat 解包 tar zxf /mnt/…

學習 SSH Key 生成方法

SSH Key 是用于身份驗證的一對密鑰&#xff0c;包括公鑰和私鑰。公鑰可以放在需要訪問的服務器上&#xff0c;私鑰則保留在本地。當你使用SSH連接到支持SSH Key認證的服務器時&#xff0c;服務器會用公鑰來加密一個隨機生成的字符串發送給客戶端&#xff0c;客戶端用私鑰解密并…