GoSuda

Mikä tahansa ei ole Mikä tahansa, mutta Mikä tahansa on Mikä tahansa

By snowmerak
views ...

Any

Go-kielessä any-tyyppi on maagisen rajapinnan, interface{}:n, aliaskonstruktio, joka kykenee sisältämään minkä tahansa tyypin. Toisin sanoen, mikä tahansa tyyppi voidaan sijoittaa any-tyyppiseen muuttujaan.

1var a any
2a = 42          // kokonaisluku
3a = "hello"     // merkkijono
4a = true        // totuusarvo
5a = 3.14        // liukuluku

Kuten nähtävissä, any-tyyppi mahdollistaa joustavan koodin luomisen, sillä se voi sisältää erilaisten tyyppien arvoja. Kuitenkin any-tyypin käytössä on huomioitava useita seikkoja.

Type Assertion

Jotta any-tyyppisen muuttujan todellista arvoa voidaan käyttää, on hyödynnettävä tyyppivarmistusta (type assertion). Tyyppivarmistus toimii ilmoituksena kääntäjälle siitä, että any-tyyppinen muuttuja edustaa tiettyä tyyppiä.

1var a any = "hello"
2str, ok := a.(string)
3if ok {
4    fmt.Println("Merkkijonoarvo:", str)
5} else {
6    fmt.Println("Ei ole merkkijono")
7}

Edellä olevassa esimerkissään lauseke a.(string) varmistaa, että a on tyyppiä string. Mikäli a ei ole tyyppiä string, muuttujan ok arvoksi tulee false, mikä estää ohjelman kaatumisen (panic).

Type Switch

Tyyppikytkin (type switch) mahdollistaa erilaisten toimintojen suorittamisen any-tyyppisen muuttujan todellisen tyypin perusteella. Tämä on hyödyllistä tilanteissa, joissa täytyy käsitellä useita eri tyyppejä.

 1var a any = 42
 2switch v := a.(type) {
 3case int:
 4    fmt.Println("Kokonaisluku:", v)
 5case string:
 6    fmt.Println("Merkkijono:", v)
 7case bool:
 8    fmt.Println("Totuusarvo:", v)
 9default:
10    fmt.Println("Tuntematon tyyppi")
11}

Edellä olevassa esimerkissään lauseke a.(type) tarkistaa muuttujan a todellisen tyypin ja suorittaa vastaavan case-lohkon.

Reflection

Go-kielen reflect-paketin avulla voidaan dynaamisesti tarkastella ja manipuloida any-tyyppisen muuttujan tyyppiä ja arvoa ajonaikaisesti (runtime). Tämä on hyödyllistä käsiteltäessä monimutkaisia tietorakenteita.

1import (
2    "fmt"
3    "reflect"
4)
5
6var a any = 3.14
7v := reflect.ValueOf(a)
8fmt.Println("Tyyppi:", v.Type())
9fmt.Println("Arvo:", v.Float())

Edellä olevassa esimerkissään reflect-paketin avulla voidaan ajonaikaisesti todentaa muuttujan a tyyppi ja arvo.

EFace

Go-kielessä rajapinnat jakautuvat kahteen päämuotoon: EFace (Empty Interface) ja IFace (Interface with Methods). Näistä EFace on sama käsite kuin tähän mennessä käsittelemämme any-tyyppi.

구조

Koska EFace on erityinen rakenteellinen tyyppi, joka on olemassa ainoastaan Go-kielen ajonaikaisessa ympäristössä (runtime), käsittelemme sitä luomalla sille jäljitelmän (mimic).

 1type runtimeTypeMimic struct {
 2	Size_       uintptr
 3	PtrBytes    uintptr // osoittimia sisältävien etuliitebyttien lukumäärä
 4	Hash        uint32  // tyypin tiiviste; välttää laskentaa hajautustauluissa
 5	TFlag       uint8   // tyypin lisätietoliput
 6	Align_      uint8   // tämän tyypin muuttujan kohdistus
 7	FieldAlign_ uint8   // struct-kentän kohdistus tämän tyypin mukaan
 8	Kind_       uint8   // enumeeraatio C:lle
 9	Equal       func(unsafe.Pointer, unsafe.Pointer) bool
10	GCData      *byte
11	Str         int32 // merkkimuoto
12	PtrToThis   int32 // osoitin tämän tyypin tyyppiin, voi olla nolla
13}
14
15type eFaceMimic struct {
16	Type *runtimeTypeMimic
17	Data unsafe.Pointer
18}

EFace sisältää kaksi kenttää:

  • Type: Osoitin, joka sisältää metatietoa arvon todellisesta tyypistä.
  • Data: Osoitin, joka sisältää itse arvon.

Lisäksi EFace hallinnoi tyyppitietoja sisäisesti runtimeType-rakenteen kautta. Tämä rakenne sisältää monenlaisia metatietoja, kuten tyypin koon, kohdistuksen ja tiivisteen arvon.

Tässä rakenteessa meidän tulisi kiinnittää huomiota kenttiin Kind_, Hash ja Equal. Kind_ ilmoittaa tyypin luokan, ja kenttiä Hash sekä Equal käytetään tyypin vertailuun hajautustauluissa ja tiivisteen arvon laskemiseen.

포장

Luodaan nyt tämä EFace kääreeseen, jotta voimme suorittaa tähän liittyvää syvempää manipulaatiota helpommin.

 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}

Yllä oleva funktio vastaanottaa any-tyyppisen muuttujan, poimii siitä sisäisen EFace-rakenteen ja palauttaa siitä erotellut tyyppitiedot TypeInfo-rakenteeseen. Tämän TypeInfo-rakenteen avulla voimme nyt helposti käyttää tyypin tiivisteen arvoa, luokkaa ja vertailufunktiota.

활용

Käyttö on erittäin yksinkertaista; GetTypeInfo-funktiolle tulee ainoastaan välittää any-tyyppinen muuttuja.

 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	// Vertaa kahta arvoa käyttämällä tyyppitiedon Equal-funktiota
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}

Edellisessä esimerkissä kokonaisluku ja liukuluku välitetään GetTypeInfo-funktiolle, jolloin kunkin tyyppitiedot tulostetaan ja arvoja verrataan käyttäen Equal-funktiota. Tämän menetelmän avulla tyyppitiedon saaminen ja tyyppien vertailu on mahdollista suorittaa nopeammin.

Lisäksi tätä lähestymistapaa voidaan soveltaa samalla tavalla rakenteisiin, osoittimiin ja rajapintoihin, ja se kykenee ratkaisemaan myös usein ilmenevän ongelman, nimittäin tyypillisen nil-arvon (typed nil) tapauksen. Tämä johtuu siitä, että Hash on todennäköisesti määritetty, jos tyyppi on olemassa, kun taas This saa arvon nolla, jos arvo on nil.

결론

EFace näyttelee merkittävää roolia käsiteltäessä any-tyyppiä nykyisessä Go-kielessä. Sen avulla mahdollistuu erilaisten tyyppien arvojen joustava ja nopea käsittely sekä tyyppitietojen yksityiskohtainen hankinta ja hyödyntäminen. On kuitenkin syytä huomioida, että tämä on ajonaikaisen ympäristön sisällä piilotettu toiminnallisuus, joka voi altistua kielen spesifikaation muutosten vaikutuksille.