Rumah >pembangunan bahagian belakang >Golang >Generik dalam Go: Mengubah Kebolehgunaan Semula Kod

Generik dalam Go: Mengubah Kebolehgunaan Semula Kod

Patricia Arquette
Patricia Arquetteasal
2025-01-08 06:20:41530semak imbas

Generik, yang diperkenalkan dalam Go 1.18, telah merevolusikan cara menulis kod yang boleh digunakan semula dan selamat jenis. Generik membawa fleksibiliti dan kuasa sambil mengekalkan falsafah kesederhanaan Go. Walau bagaimanapun, memahami nuansa, faedah dan cara generik dibandingkan dengan pendekatan tradisional (seperti antara muka{} ) memerlukan pandangan yang lebih dekat.

Mari kita terokai selok-belok generik, teliti kekangan, bandingkan generik dengan antara muka{} dan tunjukkan aplikasi praktikalnya. Kami juga akan menyentuh pertimbangan prestasi dan implikasi saiz binari. Mari selami!

Apakah Generik?

Generik membolehkan pembangun menulis fungsi dan struktur data yang boleh beroperasi pada sebarang jenis sambil mengekalkan keselamatan jenis. Daripada bergantung pada antara muka{}, yang melibatkan penegasan jenis dalam masa jalan, generik membenarkan anda menentukan set kekangan yang menentukan operasi yang dibenarkan pada jenis.

Sintaks

func FunctionName[T TypeConstraint](parameterName T) ReturnType {
    // Function body using T
}

T: Parameter jenis, mewakili ruang letak untuk jenis.

TypeConstraint: Mengehadkan jenis T kepada jenis tertentu atau set jenis.

parameterName T: Parameter menggunakan jenis generik T.

ReturnType: Fungsi ini juga boleh mengembalikan nilai jenis T.

Contoh

func Sum[T int | float64](a, b T) T {
    return a + b
}

func Sum: Mengisytiharkan nama fungsi, Sum

[T int | float64]: Menentukan senarai parameter jenis yang memperkenalkan T sebagai parameter jenis, terhad kepada jenis tertentu (int atau float64). Fungsi sum boleh mengambil hanya parameter sama ada int atau float64, bukan digabungkan, kedua-duanya perlu sama ada int atau float64. Kami akan meneroka perkara ini dengan lebih lanjut dalam bahagian di bawah.

(a, b T): Mengisytiharkan dua parameter, a dan b, kedua-duanya jenis T (jenis generik ).

T: Menentukan jenis pemulangan fungsi, yang sepadan dengan parameter jenis T.

Kekangan: Blok pembinaan Generik

Kekangan menentukan operasi yang sah untuk jenis generik. Go menyediakan alatan berkuasa untuk kekangan, termasuk pakej kekangan percubaan(golang.org/x/exp/constraints).

Kekangan Terbina dalam

Perkenalkan kekangan terbina dalam dengan generik untuk menyediakan keselamatan jenis sambil membenarkan fleksibiliti dalam menentukan kod boleh guna semula dan generik. Kekangan ini membolehkan pembangun menguatkuasakan peraturan pada jenis yang digunakan dalam fungsi atau jenis generik.

Go mempunyai kekangan terbina dalam di bawah

  1. mana-mana: Mewakili sebarang jenis. Ia adalah alias untuk antara muka{}. Ini digunakan apabila tiada kekangan diperlukan
func FunctionName[T TypeConstraint](parameterName T) ReturnType {
    // Function body using T
}
  1. setanding: Membenarkan jenis yang menyokong perbandingan kesaksamaan(== dan !=). Berguna untuk kunci peta, pengesanan pendua atau semakan kesaksamaan. Ini tidak boleh digunakan untuk peta, kepingan dan fungsi, kerana jenis ini tidak menyokong perbandingan langsung.
func Sum[T int | float64](a, b T) T {
    return a + b
}

Kekangan eksperimen

  1. kekangan.Kompleks: Membenarkan jenis numerik kompleks(kompleks64 dan kompleks128).
  2. kekangan. Terapung: Membenarkan jenis angka terapung(float32 dan float64)
  3. kekangan.Integer: Membenarkan mana-mana integer ditandatangani dan tidak ditandatangani (int8, int16, int32, int64, int, uint8, uint16, uint32, uint64 dan uint)
  4. kekangan. Ditandatangani: Membenarkan mana-mana integer yang ditandatangani(int8, int16, int32, int64 dan int)
  5. kekangan.Tidak Ditandatangani: Membenarkan sebarang integer yang tidak ditandatangani (uint8, uint16, uint32, uint64 dan uint).
  6. kekangan.Tertib: Membenarkan jenis yang membenarkan perbandingan (<. <=, >, >=), semua jenis angka dan rentetan disokong(int, float64, rentetan, dsb.).
func PrintValues[T any](values []T) {
    for _, v := range values {
        fmt.Println(v)
    }
}

Kekangan Tersuai

Kekangan tersuai ialah antara muka yang mentakrifkan satu set jenis atau gelagat jenis yang mesti dipenuhi oleh parameter jenis generik. Dengan mewujudkan kekangan anda sendiri, kami boleh;

  • Hadkan jenis kepada subset tertentu, seperti jenis angka.

  • Memerlukan jenis untuk melaksanakan kaedah atau tingkah laku tertentu.

  • Tambahkan lagi kawalan dan kekhususan pada fungsi dan jenis generik anda.

Sintaks

func CheckDuplicates[T comparable](items []T) []T {
    seen := make(map[T]bool)
    duplicates := []T{}
    for _, item := range items {
        if seen[item] {
            duplicates = append(duplicates, item)
        } else {
            seen[item] = true
        }
    }
    return duplicates
}

Contoh

import (
     "golang.org/x/exp/constraints"
     "fmt"
)

func SortSlice[T constraints.Ordered](items []T) []T {
    sorted := append([]T{}, items...) // Copy slice
    sort.Slice(sorted, func(i, j int) bool {
        return sorted[i] < sorted[j]
    })
    return sorted
}

func main() {
    nums := []int{5, 2, 9, 1}
    fmt.Println(SortSlice(nums)) // Output: [1 2 5 9]

    words := []string{"banana", "apple", "cherry"}
    fmt.Println(SortSlice(words)) // Output: [apple banana cherry]
}

Fungsi jumlah boleh dipanggil hanya menggunakan parameter int, int64 dan float64.

Kekangan mengikut kaedah

Jika anda ingin menguatkuasakan sesuatu jenis mesti melaksanakan kaedah tertentu, anda boleh mentakrifkannya menggunakan kaedah tersebut.

type Numeric interface {
    int | float64 | uint
}

Kekangan Formatter memerlukan sebarang jenis yang digunakan sebagai T mesti mempunyai kaedah Format yang mengembalikan rentetan.

Menggabungkan Kekangan

Kekangan tersuai boleh menggabungkan set jenis dan keperluan kaedah

type Number interface {
    int | int64 | float64
}

func Sum[T Number](a, b T) T {
    return a + b
}

Kekangan ini termasuk kedua-dua jenis khusus (int, float54) dan memerlukan kehadiran kaedah abs.

Generik lwn antara muka{}

Sebelum pengenalan generik, antara muka{} telah digunakan untuk mencapai fleksibiliti. Walau bagaimanapun, pendekatan ini mempunyai had.

Jenis Keselamatan

  • antara muka{}: Bergantung pada penegasan jenis masa jalan, meningkatkan peluang ralat semasa masa jalan.

  • Generik: Menawarkan keselamatan jenis masa kompilasi, menangkap ralat lebih awal semasa pembangunan.

Prestasi

  • antara muka{}: Lebih perlahan disebabkan pemeriksaan jenis masa jalan tambahan.

  • Generik: Lebih pantas, kerana pengkompil menjana laluan kod yang dioptimumkan khusus untuk jenis.

Kebolehbacaan Kod

  • antara muka{}: Selalunya bertele-tele dan kurang intuitif, menjadikan kod lebih sukar untuk dikekalkan.

  • Generik: Sintaks yang lebih bersih membawa kepada kod yang lebih intuitif dan boleh diselenggara.

Saiz Binari

  • antara muka{}: Hasil dalam binari yang lebih kecil kerana ia tidak menduplikasi kod untuk jenis yang berbeza.

  • Generik: Meningkatkan sedikit saiz binari disebabkan pengkhususan jenis untuk prestasi yang lebih baik.

Contoh

func FunctionName[T TypeConstraint](parameterName T) ReturnType {
    // Function body using T
}

Kod berfungsi dengan baik, penegasan jenis berada di atas kepala. Fungsi tambah boleh dipanggil dengan sebarang argumen, kedua-dua parameter a dan b boleh terdiri daripada jenis yang berbeza, namun kod akan ranap dalam masa jalan.

func Sum[T int | float64](a, b T) T {
    return a + b
}

Generik menghapuskan risiko panik masa jalan yang disebabkan oleh penegasan jenis yang salah dan meningkatkan kejelasan.

Prestasi

Generik menghasilkan kod khusus untuk setiap jenis, yang membawa kepada prestasi masa jalan yang lebih baik berbanding antara muka{}.

Saiz Binari

Pertukaran wujud: generik meningkatkan saiz binari disebabkan pertindihan kod untuk setiap jenis, tetapi ini selalunya boleh diabaikan berbanding dengan faedah.

Had Go Generics

Kerumitan dalam Kekangan: Walaupun kekangan seperti kekangan. Diperintahkan memudahkan kes penggunaan biasa, mentakrifkan kekangan yang sangat tersuai boleh menjadi bertele-tele.

Tiada Jenis Inferens dalam Struktur: Tidak seperti fungsi, anda mesti menyatakan parameter jenis secara eksplisit untuk struct.

func PrintValues[T any](values []T) {
    for _, v := range values {
        fmt.Println(v)
    }
}

Terhad kepada Kekangan Masa Kompilasi: Generik Go memfokuskan pada keselamatan masa kompilasi, manakala bahasa seperti Rust menawarkan kekangan yang lebih berkuasa menggunakan jangka hayat dan sifat.

Mari Menanda Aras — Lebih baik dilakukan daripada dikatakan

Kami akan melaksanakan Baris Gilir mudah dengan kedua-dua antara muka{} dan generik serta menanda aras hasilnya.

Pelaksanaan baris gilir{} antara muka

func CheckDuplicates[T comparable](items []T) []T {
    seen := make(map[T]bool)
    duplicates := []T{}
    for _, item := range items {
        if seen[item] {
            duplicates = append(duplicates, item)
        } else {
            seen[item] = true
        }
    }
    return duplicates
}

Pelaksanaan Giliran Generik

import (
     "golang.org/x/exp/constraints"
     "fmt"
)

func SortSlice[T constraints.Ordered](items []T) []T {
    sorted := append([]T{}, items...) // Copy slice
    sort.Slice(sorted, func(i, j int) bool {
        return sorted[i] < sorted[j]
    })
    return sorted
}

func main() {
    nums := []int{5, 2, 9, 1}
    fmt.Println(SortSlice(nums)) // Output: [1 2 5 9]

    words := []string{"banana", "apple", "cherry"}
    fmt.Println(SortSlice(words)) // Output: [apple banana cherry]
}
type Numeric interface {
    int | float64 | uint
}

Analisis Keputusan

  • Masa Pelaksanaan:
    Pelaksanaan generik adalah lebih kurang 63.64% lebih pantas daripada versi antara muka{} kerana ia mengelakkan penegasan jenis masa jalan dan beroperasi terus pada jenis yang diberikan.

  • Peruntukan:
    Versi antara muka{} membuat 3x lebih banyak peruntukan, terutamanya disebabkan oleh peninjuan/penyahkotak apabila memasukkan dan mendapatkan semula nilai. Ini menambah overhed kepada kutipan sampah.

Untuk beban kerja yang lebih besar, seperti 1 juta operasi enqueue/dequeue, jurang prestasi semakin melebar. Aplikasi dunia sebenar dengan keperluan pemprosesan tinggi (cth., baris gilir mesej, penjadual kerja) mendapat manfaat yang ketara daripada generik.

Fikiran Akhir

Generik dalam Go menyeimbangkan antara kuasa dan kesederhanaan, menawarkan penyelesaian praktikal untuk menulis kod yang boleh digunakan semula dan selamat jenis. Walaupun tidak kaya ciri seperti Rust atau C , selaras dengan sempurna dengan falsafah minimalis Go. Memahami kekangan seperti kekangan. Disusun dan memanfaatkan generik dengan berkesan boleh meningkatkan kualiti dan kebolehselenggaraan kod.

Memandangkan generik terus berkembang, mereka ditakdirkan untuk memainkan peranan penting dalam ekosistem Go. Jadi, selami, bereksperimen dan terima era baharu jenis keselamatan dan fleksibiliti dalam pengaturcaraan Go!

Lihat repositori github untuk beberapa sampel pada generik.

GitHub logo sadananddodawadakar / GoGenerics

Repositori mengandungi contoh kerja generik go

Go Generics: Repositori Contoh Komprehensif

Selamat datang ke Repositori Go Generics! Repositori ini ialah sumber sehenti untuk memahami, mempelajari dan menguasai generik dalam Go, yang diperkenalkan dalam versi 1.18. Generik membawa kuasa parameter jenis kepada Go, membolehkan pembangun menulis kod boleh guna semula dan selamat taip tanpa menjejaskan prestasi atau kebolehbacaan.

Repositori ini mengandungi contoh susun atur dengan teliti yang merangkumi pelbagai topik, daripada sintaks asas kepada corak lanjutan dan kes penggunaan praktikal. Sama ada anda seorang pemula atau pembangun Go yang berpengalaman, koleksi ini akan membantu anda memanfaatkan generik dengan berkesan dalam projek anda.


? Apa Ada Dalam

? Program Asas Generik

Contoh ini memperkenalkan konsep asas generik, membantu anda memahami sintaks dan ciri teras:

  1. GenericMap: Menunjukkan fungsi peta generik untuk mengubah kepingan apa-apa jenis.
  2. Tukar: Contoh mudah tetapi berkesan untuk menukar dua nilai secara umum.
  3. FilterSlice: Menunjukkan cara menapis…


Lihat di GitHub


Atas ialah kandungan terperinci Generik dalam Go: Mengubah Kebolehgunaan Semula Kod. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn