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.

Keterangan

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

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

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

Contoh

Contoh berikut menentukan ekstensi Print ke jenis data String. Metode ini menggunakan Console.WriteLine untuk menampilkan string. Parameter metode Print, aString, menetapkan bahwa metode ini 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 ditentukan 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 tempat metode ekstensi ditentukan bukan modul yang sama dengan modul tempat metode dipanggil. Sebaliknya, 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 ditentukan 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 argumen string untuk punc: example.PrintAndPunctuate(".")

Contoh berikut menunjukkan Print dan PrintAndPunctuate menentukan dan memanggil. 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 dipanggil:

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

Hal yang diperlukan untuk dapat menjalankan metode ekstensi ini atau yang serupa adalah bahwa metode berada dalam cakupan. Jika modul yang berisi metode ekstensi berada dalam cakupan, modul terlihat dalam IntelliSense dan dapat dipanggil seolah-olah modul 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. Pengompilasi akan menggunakan example sebagai argumen yang dikirim ke parameter pertama.

Jika metode ekstensi dipanggil untuk objek yang diatur ke Nothing, metode ekstensi akan dijalankan. Ini tidak berlaku untuk metode instans biasa. Anda dapat secara eksplisit memeriksa Nothing 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
  • Delegasikan
  • Argumen ByRef dan ByVal
  • Parameter metode generik
  • Larik

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

Metode ekstensi tidak dipertimbangkan dalam pengikatan terlambat. Dalam contoh berikut, pernyataan anObject.PrintMe() memunculkan pengecualian MissingMemberException, pengecualian yang sama seperti yang Anda lihat jika definisi metode ekstensi PrintMe kedua 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 tangguh untuk memperluas jenis yang ada. Namun, agar berhasil menggunakannya, ada beberapa poin yang perlu dipertimbangkan. Pertimbangan ini berlaku terutama untuk pembuat 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 dan dapat mengganggu metode ekstensi.

  • Jika ada anggota instans yang dapat diakses yang memiliki tanda tangan yang kompatibel dengan argumen dalam pernyataan panggilan, tanpa konversi penyempitan yang diperlukan dari argumen ke parameter, metode instans akan digunakan dalam preferensi ke metode ekstensi apa pun. Oleh karena itu, jika metode instans yang sesuai ditambahkan ke kelas pada titik tertentu, anggota ekstensi yang ada dan Anda andalkan mungkin menjadi tidak dapat diakses.

  • Pembuat 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 layanan 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, kemungkinannya kecil bagi pembuat untuk menambahkan atau mengubah metode dalam antarmuka. Namun, jika kelas mengimplementasikan dua antarmuka yang memiliki metode ekstensi dengan tanda tangan yang sama, tidak akan ada metode ekstensi yang terlihat.

  • Perluas jenis yang paling spesifik yang Anda bisa. Dalam hierarki jenis, jika Anda memilih jenis dari tempat berbagai jenis lainnya diturunkan, ada lapisan kemungkinan untuk pengenalan metode instans atau metode ekstensi lain yang mungkin mengganggu Anda.

Metode ekstensi, metode instans, dan properti

Ketika metode instans dalam cakupan memiliki tanda tangan yang kompatibel dengan argumen pernyataan panggilan, metode instans dipilih sebagai preferensi bagi metode ekstensi apa pun. Metode instans memiliki prioritas meski metode ekstensi lebih cocok. Dalam contoh berikut, ExampleClass berisi metode instans bernama ExampleMethod yang memiliki satu parameter jenis Integer. Metode ekstensi ExampleMethod memperluas ExampleClass, dan memiliki satu parameter 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 adalah Long dan hanya kompatibel dengan parameter Long dalam metode ekstensi. Panggilan kedua ke ExampleMethod memiliki argumen Integer, 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 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 bertentangan, 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 yang tertinggi hingga terendah.

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

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

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

  4. Metode ekstensi yang ditentukan di dalam impor namespace layanan apa pun dalam file saat ini.

  5. Metode ekstensi yang ditentukan di dalam impor jenis tingkat proyek apa pun.

  6. Metode ekstensi yang ditentukan di dalam impor namespace layanan tingkat proyek apa pun.

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

Lihat juga