Bagikan melalui


Metode Ekstensi (Visual Basic)

Metode ekstensi memungkinkan pengembang untuk menambahkan fungsionalitas kustom ke jenis data yang sudah ditentukan tanpa membuat jenis turunan baru. Metode ekstensi memungkinkan untuk menulis metode yang dapat dipanggil seolah-olah itu adalah metode instans dari jenis yang ada.

Komentar

Metode ekstensi hanya dapat berupa Sub prosedur atau Function prosedur. Anda tidak dapat menentukan properti ekstensi, bidang, atau peristiwa. Semua metode ekstensi harus ditandai dengan atribut <Extension> ekstensi dari System.Runtime.CompilerServices namespace dan harus didefinisikan dalam Modul. Jika metode ekstensi didefinisikan di luar modul, pengkompilasi Visual Basic menghasilkan kesalahan BC36551, "Metode ekstensi hanya dapat ditentukan dalam modul".

Parameter pertama dalam definisi metode ekstensi menentukan jenis data mana yang diperluas oleh metode. Ketika metode dijalankan, parameter pertama terikat ke instans jenis data yang memanggil metode .

Atribut Extension hanya dapat diterapkan ke Visual Basic Module, , Subatau Function. Jika Anda menerapkannya ke Class atau Structure, pengkompilasi Visual Basic menghasilkan kesalahan BC36550, atribut "'Ekstensi' hanya dapat diterapkan ke deklarasi 'Modul', 'Sub', atau 'Fungsi'".

Contoh

Contoh berikut mendefinisikan Print perluasan untuk jenis data String. Metode ini menggunakan Console.WriteLine untuk menampilkan string. Parameter metode Print, aString, menetapkan bahwa metode memperluas kelas String.

Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()> 
    Public Sub Print(ByVal aString As String)
        Console.WriteLine(aString)
    End Sub

End Module

Perhatikan bahwa definisi metode ekstensi ditandai dengan atribut ekstensi <Extension()>. Menandai modul tempat metode didefinisikan bersifat opsional, tetapi setiap metode ekstensi harus ditandai. System.Runtime.CompilerServices harus diimpor untuk mengakses atribut ekstensi.

Metode ekstensi hanya dapat dideklarasikan dalam modul. Biasanya, modul di mana metode ekstensi didefinisikan bukan modul yang sama dengan modul yang dipanggilnya. Sebagai gantinya, modul yang berisi metode ekstensi diimpor, jika perlu, untuk membawanya ke dalam cakupan. Setelah modul yang berisi Print berada dalam cakupan, metode dapat dipanggil seolah-olah itu adalah metode instans biasa yang tidak mengambil argumen, seperti ToUpper:

Module Class1

    Sub Main()

        Dim example As String = "Hello"
        ' Call to extension method Print.
        example.Print()

        ' Call to instance method ToUpper.
        example.ToUpper()
        example.ToUpper.Print()

    End Sub

End Module

Contoh berikutnya, PrintAndPunctuate, juga merupakan ekstensi untuk String, kali ini didefinisikan dengan dua parameter. Parameter pertama, aString, menetapkan bahwa metode ekstensi memperluas String. Parameter kedua, punc, dimaksudkan untuk menjadi string tanda baca yang diteruskan sebagai argumen ketika metode dipanggil. Metode menampilkan string diikuti dengan tanda baca.

<Extension()> 
Public Sub PrintAndPunctuate(ByVal aString As String, 
                             ByVal punc As String)
    Console.WriteLine(aString & punc)
End Sub

Metode ini dipanggil dengan mengirim dalam argumen string untuk punc: example.PrintAndPunctuate(".")

Contoh berikut menunjukkan Print dan PrintAndPunctuate yang didefinisikan dan dipanggil. System.Runtime.CompilerServices diimpor dalam modul definisi untuk mengaktifkan akses ke atribut ekstensi.

Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()>
    Public Sub Print(aString As String)
        Console.WriteLine(aString)
    End Sub

    <Extension()>
    Public Sub PrintAndPunctuate(aString As String, punc As String)
        Console.WriteLine(aString & punc)
    End Sub
End Module

Selanjutnya, metode ekstensi dibawa ke dalam cakupan dan disebut:

Imports ConsoleApplication2.StringExtensions

Module Module1

    Sub Main()
        Dim example As String = "Example string"
        example.Print()

        example = "Hello"
        example.PrintAndPunctuate(".")
        example.PrintAndPunctuate("!!!!")
    End Sub
End Module

Semua yang diperlukan untuk dapat menjalankan metode ekstensi ini atau serupa adalah bahwa mereka berada dalam cakupan. Jika modul yang berisi metode ekstensi berada dalam cakupan, modul tersebut terlihat di IntelliSense dan dapat dipanggil seolah-olah itu adalah metode instans biasa.

Perhatikan bahwa ketika metode dipanggil, tidak ada argumen yang dikirim untuk parameter pertama. Parameter aString dalam definisi metode sebelumnya terikat ke example, instans String yang memanggilnya. Pengkompilasi akan menggunakan example sebagai argumen yang dikirim ke parameter pertama.

Jika metode ekstensi dipanggil untuk objek yang diatur ke Nothing, metode ekstensi dijalankan. Ini tidak berlaku untuk metode instans biasa. Anda dapat memeriksa Nothing secara eksplisit dalam metode ekstensi.

Jenis yang dapat diperluas

Anda dapat menentukan metode ekstensi pada sebagian besar jenis yang dapat diwakili dalam daftar parameter Visual Basic, termasuk yang berikut ini:

  • Kelas (jenis referensi)
  • Struktur (jenis nilai)
  • Antarmuka
  • Perwakilan
  • Argumen ByRef dan ByVal
  • Parameter metode generik
  • Larik

Karena parameter pertama menentukan jenis data yang diperluas oleh metode ekstensi, diperlukan dan tidak dapat bersifat opsional. Untuk alasan itu, Optional parameter dan ParamArray parameter tidak dapat menjadi parameter pertama dalam daftar parameter.

Metode ekstensi tidak dipertimbangkan dalam pengikatan terlambat. Dalam contoh berikut, pernyataan anObject.PrintMe() menimbulkan MissingMemberException pengecualian, pengecualian yang sama dengan yang akan Anda lihat jika definisi metode ekstensi kedua PrintMe dihapus.

Option Strict Off
Imports System.Runtime.CompilerServices

Module Module4

    Sub Main()
        Dim aString As String = "Initial value for aString"
        aString.PrintMe()

        Dim anObject As Object = "Initial value for anObject"
        ' The following statement causes a run-time error when Option
        ' Strict is off, and a compiler error when Option Strict is on.
        'anObject.PrintMe()
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal str As String)
        Console.WriteLine(str)
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal obj As Object)
        Console.WriteLine(obj)
    End Sub

End Module

Praktik terbaik

Metode ekstensi menyediakan cara yang nyaman dan kuat untuk memperluas jenis yang ada. Namun, untuk menggunakannya dengan sukses, ada beberapa poin yang perlu dipertimbangkan. Pertimbangan ini terutama berlaku untuk penulis pustaka kelas, tetapi dapat memengaruhi aplikasi apa pun yang menggunakan metode ekstensi.

Umumnya, metode ekstensi yang Anda tambahkan ke jenis yang tidak Anda miliki lebih rentan daripada metode ekstensi yang ditambahkan ke jenis yang Anda kontrol. Sejumlah hal dapat terjadi di kelas yang tidak Anda miliki yang dapat mengganggu metode ekstensi Anda.

  • Jika ada anggota instans yang dapat diakses yang memiliki signature yang kompatibel dengan argumen dalam pernyataan panggilan, tanpa konversi yang mempersempit yang diperlukan dari argumen ke parameter, metode instans akan lebih dipilih daripada metode ekstensi apa pun. Oleh karena itu, jika metode instans yang sesuai ditambahkan ke kelas pada titik tertentu, anggota ekstensi yang ada yang Anda mengandalkan mungkin menjadi tidak dapat diakses.

  • Penulis metode ekstensi tidak dapat mencegah programmer lain menulis metode ekstensi yang bertentangan yang mungkin lebih diutamakan daripada ekstensi asli.

  • Anda dapat meningkatkan ketahanan dengan menempatkan metode ekstensi di namespace mereka sendiri. Konsumen pustaka Anda kemudian dapat menyertakan namespace layanan atau mengecualikannya, atau memilih di antara namespace layanan, secara terpisah dari pustaka lainnya.

  • Mungkin lebih aman untuk memperluas antarmuka daripada memperluas kelas, terutama jika Anda tidak memiliki antarmuka atau kelas. Perubahan antarmuka memengaruhi setiap kelas yang mengimplementasikannya. Oleh karena itu, penulis mungkin lebih kecil kemungkinannya untuk menambahkan atau mengubah metode dalam antarmuka. Namun, jika kelas mengimplementasikan dua antarmuka yang memiliki metode ekstensi dengan tanda tangan yang sama, tidak ada metode ekstensi yang terlihat.

  • Perluas jenis yang paling spesifik yang Anda bisa. Dalam hierarki tipe, jika Anda memilih tipe dari mana banyak tipe lain diturunkan, ada lapisan kemungkinan untuk diperkenalkannya metode instance atau metode ekstensi lain yang mungkin mengganggu milik Anda.

Metode ekstensi, metode instans, dan properti

Ketika metode instans dalam cakupan memiliki tanda tangan yang kompatibel dengan argumen dari pernyataan pemanggilan, metode instans lebih dipilih daripada metode ekstensi apa pun. Metode instans lebih diutamakan walaupun metode ekstensi lebih cocok. Dalam contoh berikut, ExampleClass berisi metode instans bernama ExampleMethod yang memiliki satu parameter jenis Integer. Metode ekstensi ExampleMethod memperpanjang ExampleClass, yang memiliki satu parameter dari jenis Long.

Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Integer)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Long)
    Console.WriteLine("Extension method")
End Sub

Panggilan pertama ke ExampleMethod dalam kode berikut memanggil metode ekstensi, karena arg1 dan Long hanya kompatibel dengan Long parameter dalam metode ekstensi. Panggilan kedua untuk ExampleMethod memiliki Integer argumen, arg2, dan memanggil metode instans.

Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the extension method.
    example.exampleMethod(arg1)
    ' The following statement calls the instance method.
    example.exampleMethod(arg2)
End Sub

Sekarang balikkan jenis data parameter dalam dua metode:

Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Long)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Integer)
    Console.WriteLine("Extension method")
End Sub

Kali ini kode dalam Main memanggil metode instans dua kali. Ini karena keduanya arg1 dan arg2 memiliki konversi yang melebar ke Long, dan metode instans lebih diutamakan daripada metode ekstensi dalam kedua kasus.

Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the instance method.
    example.ExampleMethod(arg1)
    ' The following statement calls the instance method.
    example.ExampleMethod(arg2)
End Sub

Oleh karena itu, metode ekstensi tidak dapat menggantikan metode instans yang ada. Namun, ketika metode ekstensi memiliki nama yang sama dengan metode instans tetapi tanda tangan tidak berkonflik, kedua metode dapat diakses. Misalnya, jika kelas ExampleClass berisi metode bernama ExampleMethod yang tidak mengambil argumen, metode ekstensi dengan nama yang sama tetapi tanda tangan yang berbeda diizinkan, seperti yang ditunjukkan dalam kode berikut.

Imports System.Runtime.CompilerServices

Module Module3

    Sub Main()
        Dim ex As New ExampleClass
        ' The following statement calls the extension method.
        ex.ExampleMethod("Extension method")
        ' The following statement calls the instance method.
        ex.ExampleMethod()
    End Sub

    Class ExampleClass
        ' Define an instance method named ExampleMethod.
        Public Sub ExampleMethod()
            Console.WriteLine("Instance method")
        End Sub
    End Class

    <Extension()> 
    Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal stringParameter As String)
        Console.WriteLine(stringParameter)
    End Sub

End Module

Output dari kode ini adalah sebagai berikut:

Extension method
Instance method

Situasinya lebih sederhana dengan properti: jika metode ekstensi memiliki nama yang sama dengan properti kelas yang diperluasnya, metode ekstensi tidak terlihat dan tidak dapat diakses.

Prioritas metode ekstensi

Ketika dua metode ekstensi yang memiliki tanda tangan identik berada dalam cakupan dan dapat diakses, metode dengan prioritas yang lebih tinggi akan dipanggil. Prioritas metode ekstensi didasarkan pada mekanisme yang digunakan untuk membawa metode ke dalam cakupan. Daftar berikut menunjukkan hierarki prioritas, dari tertinggi hingga terendah.

  1. Metode ekstensi yang ditentukan di dalam modul saat ini.

  2. Metode ekstensi yang didefinisikan dalam jenis data di namespace saat ini atau salah satu namespace induknya, dengan namespace anak memiliki prioritas lebih tinggi daripada namespace induk.

  3. Metode ekstensi yang ditentukan di dalam impor jenis apa pun dalam file saat ini.

  4. Metode ekstensi didefinisikan di dalam impor namespace mana pun dalam file saat ini.

  5. Metode ekstensi yang didefinisikan dalam impor tipe pada tingkat proyek mana pun.

  6. Metode ekstensi yang ditentukan di dalam setiap impor namespace tingkat proyek.

Jika prioritas tidak menyelesaikan ambiguitas, Anda dapat menggunakan nama yang sepenuhnya memenuhi syarat untuk menentukan metode yang Anda panggil. Jika metode dalam contoh sebelumnya didefinisikan dalam modul bernama Print, nama yang sepenuhnya memenuhi syarat adalah StringExtensions bukan StringExtensions.Print(example).

Lihat juga