Any er ikke Any, men Any er Any
Any
I Go-sproget er any
-typen et alias for interface{}
, en magisk grænseflade, der kan indeholde enhver type. Det betyder, at enhver type kan tildeles en any
-type variabel.
1var a any
2a = 42 // int
3a = "hello" // string
4a = true // bool
5a = 3.14 // float64
Som vist kan any
-typen indeholde værdier af forskellige typer, hvilket muliggør skrivning af fleksibel kode. Dog er der visse overvejelser, når man anvender any
-typen.
Type Assertion
For at anvende den faktiske værdi fra en any
-type variabel skal man benytte type assertion. Type assertion informerer kompilatoren om, at en any
-type variabel er af en specifik type.
1var a any = "hello"
2str, ok := a.(string)
3if ok {
4 fmt.Println("String value:", str)
5} else {
6 fmt.Println("Not a string")
7}
I ovenstående eksempel bekræfter a.(string)
, at a
er af typen string. Hvis a
ikke er af typen string, vil ok
være false, og programmet undgår at gå i panik.
Type Switch
Type switch muliggør udførelse af forskellige handlinger baseret på den faktiske type af en any
-type variabel. Dette er nyttigt, når flere typer skal håndteres.
1var a any = 42
2switch v := a.(type) {
3case int:
4 fmt.Println("Integer:", v)
5case string:
6 fmt.Println("String:", v)
7case bool:
8 fmt.Println("Boolean:", v)
9default:
10 fmt.Println("Unknown type")
11}
I ovenstående eksempel kontrollerer a.(type)
den faktiske type af a
og udfører den tilsvarende case.
Reflection
Ved brug af Go-sprogets reflect
-pakke er det muligt dynamisk at inspicere og manipulere typen og værdien af en any
-type variabel under kørsel. Dette er fordelagtigt ved håndtering af komplekse datastrukturer.
1import (
2 "fmt"
3 "reflect"
4)
5
6var a any = 3.14
7v := reflect.ValueOf(a)
8fmt.Println("Type:", v.Type())
9fmt.Println("Value:", v.Float())
I eksemplet herover kan typen og værdien af a
verificeres under kørsel ved brug af reflect
-pakken.
EFace
I Go-sproget er grænseflader opdelt i to hovedformer: EFace (Empty Interface) og IFace (Interface with Methods). Blandt disse er EFace det samme koncept som den any
-type, vi hidtil har behandlet.
Struktur
EFace er en speciel struct, der kun eksisterer inden for Go-sprogets runtime, så her vil vi skabe en efterligning (mimic).
1type runtimeTypeMimic struct {
2 Size_ uintptr
3 PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers
4 Hash uint32 // hash of type; avoids computation in hash tables
5 TFlag uint8 // extra type information flags
6 Align_ uint8 // alignment of variable with this type
7 FieldAlign_ uint8 // alignment of struct field with this type
8 Kind_ uint8 // enumeration for C
9 Equal func(unsafe.Pointer, unsafe.Pointer) bool
10 GCData *byte
11 Str int32 // string form
12 PtrToThis int32 // type for pointer to this type, may be zero
13}
14
15type eFaceMimic struct {
16 Type *runtimeTypeMimic
17 Data unsafe.Pointer
18}
EFace består af to felter:
Type
: En pointer, der indeholder metadata om den faktiske type af værdien.Data
: En pointer, der indeholder den faktiske værdi.
EFace administrerer internt typeinformation gennem runtimeType
-struct'en. Denne struct inkluderer forskellige metadata såsom typens størrelse, justering og hash-værdi.
De felter, vi skal fokusere på her, er Kind_
, Hash
og Equal
. Kind_
angiver typen, mens Hash
og Equal
anvendes til at sammenligne typer og beregne hash-værdier i hash-tabeller.
Indpakning
Lad os nu indpakke denne EFace for at gøre det lettere at udføre sort magi.
1type TypeInfo struct {
2 Hash uint32
3 TFlag uint8
4 Kind uint8
5 Equal func(unsafe.Pointer, unsafe.Pointer) bool
6 This unsafe.Pointer
7}
8
9func GetTypeInfo(v any) TypeInfo {
10 eface := *(*eFaceMimic)(unsafe.Pointer(&v))
11 return TypeInfo{
12 Hash: eface.Type.Hash,
13 TFlag: eface.Type.TFlag,
14 Kind: eface.Type.Kind_,
15 Equal: eface.Type.Equal,
16 This: eface.Data,
17 }
18}
Ovenstående funktion modtager en any
-type variabel, udtrækker den interne EFace-struktur og henter typeinformation derfra for at returnere en TypeInfo
-struktur. Med denne TypeInfo
kan man nemt tilgå typens hash-værdi, art, sammenligningsfunktioner og mere.
Anvendelse
Anvendelsen er yderst ligetil. Man skal blot videregive en any
-type variabel til GetTypeInfo
-funktionen.
1func main() {
2 i := 42
3 eface := GetTypeInfo(i)
4 fmt.Printf("%+v\n", eface)
5
6 f := 3.14
7 eface2 := GetTypeInfo(f)
8 fmt.Printf("%+v\n", eface2)
9
10 // Compare the two values using the Equal function from the type info
11 log.Println(eface.Equal(eface.This, eface.This))
12 log.Println(eface2.Equal(eface2.This, eface2.This))
13 log.Println(eface2.Equal(eface.This, eface2.This))
14 log.Println(eface.Equal(eface2.This, eface.This))
15}
I ovenstående eksempel overføres en integer- og en flydende-komma-værdi til GetTypeInfo
-funktionen for at udskrive deres respektive typeinformation, og værdierne sammenlignes ved hjælp af Equal
-funktionen. Dette muliggør en hurtigere hentning af typeinformation og typesammenligning.
Desuden kan denne metode anvendes identisk på structs, pointers og interfaces, og den kan også løse problemet med "typed nil", et ofte forekommende problem. Dette skyldes, at Hash
vil være tildelt, hvis typen eksisterer, men This
vil være 0, hvis den er nil.
Konklusion
EFace spiller i øjeblikket en væsentlig rolle i håndteringen af any
-typen i Go-sproget. Gennem EFace kan værdier af forskellige typer behandles fleksibelt og hurtigt, og detaljeret typeinformation kan opnås og udnyttes. Dog skal man være opmærksom på, at dette er en intern runtime-funktion, som kan blive påvirket af ændringer i sprogspecifikationen.