Daftar lengkap perbedaan antara VB.NET dan C #. Bagian 1

gambar

Menurut peringkat TIOBE pada tahun 2018, VB.NET melampaui popularitas C #. Secara kebetulan atau tidak, tetapi pada bulan Februari, Eric Lippert, salah satu pencipta C #, mendesak pembaca untuk memperhatikan blog temannya, mantan rekan satu tim dari kompiler Roslyn dan, dalam kombinasi, seorang penggemar VB.NET yang bersemangat, Anthony Green . "Sumber daya semacam itu adalah perincian mendalam dari para ahli yang tidak begitu mudah ditemukan dengan membaca dokumentasinya," tulis Eric. Kami mempersembahkan perhatian Anda pada bagian pertama dari terjemahan artikel Anthony Green “Daftar Perbedaan yang Luas Antara VB.NET dan C #”. Mungkin justru pada perbedaan-perbedaan inilah letak rahasia dinamika peringkat bahasa-bahasa ini.

Selama hampir setengah hidup saya, saya telah menyaksikan dan berpartisipasi dalam diskusi yang tak terhitung jumlahnya tentang seberapa mirip atau berbeda dua bahasa .NET yang paling populer. Pertama, sebagai seorang amatir, kemudian sebagai seorang profesional, dan akhirnya, sebagai penasihat pelanggan, manajer program dan perancang bahasa, saya dapat mengatakan tanpa berlebihan berapa kali saya mendengar atau membaca sesuatu seperti:
"... VB.NET benar-benar hanya lapisan tipis di atas IL, seperti C # ..."
atau
"... VB.NET sebenarnya hanya C # tanpa titik koma ..."
Seolah-olah bahasa adalah konversi XML atau style sheet.

Dan jika beberapa pengunjung yang bersemangat tidak menulis ini dalam komentar, maka sering kali ini tersirat dalam pertanyaan seperti: "Halo, Anthony! Saya menemukan perbedaan kecil di satu tempat - apakah ini bug? Bagaimana mungkin kedua bahasa yang identik ini, yang seharusnya identik atas nama semua yang baik dan suci di dunia ini, tersebar di satu tempat ini? Mengapa kita membutuhkan ketidakadilan seperti itu ?!

" Pisahkan, " seolah-olah mereka sama sampai terjadi mutasi, dan kemudian menjadi spesies yang terpisah. Hah!

Tapi saya mengerti itu. Sebelum saya bergabung dengan Microsoft, saya mungkin juga memegang gagasan ini secara samar-samar dan menggunakannya sebagai argumen untuk menanggapi lawan atau meyakinkan seseorang. Saya mengerti pesonanya. Mudah dimengerti dan sangat mudah diulang. Tetapi bekerja pada Roslyn (pada dasarnya menulis ulang VB dan C # sepenuhnya dari awal) selama 5 tahun, saya menyadari betapa salahnya ide ini . Saya bekerja dengan tim pengembang dan penguji untuk mengimplementasikan kembali setiap inci dari kedua bahasa, serta alat-alat mereka dalam solusi multi-proyek besar dengan jutaan baris kode yang ditulis dalam kedua bahasa. Dan dengan mempertimbangkan sejumlah besar pengembang beralih di antara mereka bolak-balik, dan tingkat kompatibilitas yang tinggi dengan hasil dan pengalaman dari versi sebelumnya, serta kebutuhan untuk secara andal mereproduksi volume raksasa API secara sangat terperinci, saya dipaksa untuk mengetahui perbedaannya dengan sangat dekat. Bahkan, kadang-kadang saya merasa bahwa saya belajar sesuatu yang baru tentang VB.NET (bahasa favorit saya) setiap hari.

Dan akhirnya, saya mengambil waktu untuk duduk dan membongkar dari otak sebuah partikel dari apa yang saya pelajari menggunakan dan menciptakan VB.NET selama 15 tahun terakhir, dengan harapan setidaknya saya dapat menghemat waktu saya di waktu berikutnya.

Sebelum pindah ke daftar, saya akan menguraikan aturan dasar:

  • Daftar ini tidak lengkap dalam arti biasa. Dia lengkap. Ini tidak semua perbedaan. Ini bahkan tidak semua perbedaan yang umumnya saya tahu. Ini hanya perbedaan yang bisa saya ingat pertama, sampai saya terlalu lelah untuk melanjutkan; sampai aku kehabisan kekuatan. Jika saya atau salah satu dari Anda bertemu atau mengingat perbedaan lainnya, saya akan dengan senang hati memperbarui daftar ini.
  • Saya akan mulai pada awal spesifikasi VB 11 dan bergerak ke bawah menggunakan kontennya untuk mengingatkan diri saya tentang perbedaan yang muncul pertama kali dalam topik ini.
  • Ini BUKAN daftar fungsi dalam VB yang tidak dalam C #. Jadi tidak ada "XML literal versus pointer." Ini terlalu biasa, dan sudah ada banyak daftar seperti itu di Internet (beberapa di antaranya ditulis oleh saya, dan mungkin di masa depan saya akan menulis lebih banyak). Saya akan fokus terutama pada konstruksi yang memiliki analog dalam kedua bahasa, dan di mana pengamat yang kurang informasi dapat menyarankan bahwa kedua hal ini berperilaku sama, tetapi di mana ada perbedaan kecil atau besar; mereka mungkin terlihat sama, tetapi bekerja secara berbeda atau pada akhirnya menghasilkan kode yang berbeda.
  • Ini BUKAN daftar perbedaan sintaksis antara VB dan C # (yang tak terhitung jumlahnya). Saya terutama akan berbicara tentang perbedaan semantik (apa artinya), dan bukan tentang perbedaan sintaksis (bagaimana segala sesuatu ditulis). Jadi tidak ada bagian seperti "VB memulai komentar dengan ', dan C # menggunakan //" atau "dalam C # _ adalah pengidentifikasi yang valid, tetapi tidak dalam VB". Tetapi saya akan melanggar aturan ini untuk beberapa kasus. Bagaimanapun, bagian pertama dari spesifikasi adalah tentang aturan leksikal.
  • Cukup sering, saya akan memberikan contoh, dan kadang-kadang saya akan memberikan alasan mengapa desain bisa berjalan dengan satu atau lain cara. Beberapa keputusan desain dibuat di depan mata saya, tetapi sebagian besar mendahului waktu saya, dan saya hanya bisa menebak mengapa itu dibuat.
  • Silakan tinggalkan komentar atau tweet saya ( @ThatVBGuy ) untuk memberi tahu saya perbedaan favorit Anda dan / atau yang ingin Anda ketahui lebih lanjut.

Setelah menetapkan harapan dan tanpa penundaan lebih lanjut ...

Isi


Teks tersembunyi

Sintaks dan preprocessing



Pengumuman, dll.



Instruksi



Sintaks dan preprocessing


1. Kata kunci dan operator VB dapat menggunakan karakter lebar penuh


Beberapa bahasa (saya tidak tahu berapa tepatnya, tetapi setidaknya dalam beberapa bentuk bahasa Cina, Jepang dan Korea) menggunakan karakter lebar-lebar. Singkatnya, ini berarti bahwa ketika menggunakan font monospace (seperti kebanyakan programmer), karakter Cina akan memakan ruang horizontal dua kali lebih banyak daripada karakter Latin yang biasa kita lihat di barat. Sebagai contoh:



Di sini saya memiliki deklarasi variabel yang ditulis dalam bahasa Jepang, dan inisialisasi dengan string juga ditulis dalam bahasa Jepang. Menurut penerjemah Bing, variabel ini disebut "ucapan" dan baris mengatakan "Halo Dunia!" Nama variabel dalam bahasa Jepang hanya 2 karakter, tetapi membutuhkan ruang 4 karakter setengah-lebar yang biasanya diberikan keyboard saya, seperti yang ditunjukkan oleh komentar pertama. Ada versi angka dengan lebar penuh dan semua karakter ASCII tercetak lainnya yang memiliki lebar yang sama dengan karakter Jepang. Untuk menunjukkan ini, saya menulis komentar kedua menggunakan angka lebar-penuh "1" dan "2". Ini bukan "1" dan "2" seperti pada komentar pertama. Tidak ada spasi di antara angka-angka. Anda juga dapat melihat bahwa ukuran karakter tidak tepat 2 karakter, ada sedikit offset. Ini sebagian karena program ini menggabungkan karakter lebar-lebar dan setengah-lebar dalam satu baris dan dalam ketiga baris.

Spasi setengah lebar, karakter alfanumerik adalah lebar penuh. Kami bukan pemrogram kecuali kami terobsesi dengan perataan teks. Dan menurut saya, jika Anda orang Cina, Jepang, atau Korea (atau orang lain yang menggunakan karakter berukuran penuh untuk bahasa mereka) dan menggunakan pengidentifikasi atau string yang ditulis dalam bahasa asli mereka, kesalahan penyejajaran kecil ini menyebalkan.

Sejauh yang saya mengerti, tergantung pada keyboard Jepang Anda, beralih antara hieroglif dan Latin itu mudah, tetapi lebih baik menggunakan karakter Latin lebar-lebar. VB mendukung ini dalam kata kunci, spasi, operator, dan bahkan tanda kutip. Jadi semua ini bisa ditulis seperti ini:



Seperti yang Anda lihat, dalam versi ini kata kunci, spasi, komentar, operator, bahkan tanda kutip menggunakan versi ukuran penuhnya. Ke dalam kekacauan membawa ketertiban.

Ya Orang Jepang menggunakan VB. Bahkan, meskipun sintaksinya mirip dengan bahasa Inggris (dan mungkin itu sebabnya), bagi sebagian besar pengguna VB yang saya lihat di forum, bahasa Inggris bukanlah bahasa utama. Selama bekerja di Microsoft, saya bertemu VB MVP Jepang beberapa kali, setidaknya salah satu dari mereka terus-menerus membawa permen Jepang. Jika Anda seorang programmer VB dari China, Jepang atau Korea (atau dari negara lain yang menggunakan karakter lebar penuh), silakan tulis di komentar. (Dalam komentar kepada penulis, mereka menulis bahwa orang Jepang mencoba di mana-mana dalam kode untuk menggunakan ascii - kira - kira Per. )

Lucu saat: Ketika saya awalnya menerapkan garis interpolasi di VB, saya (saya malu) tidak memperhitungkan kemungkinan kurung keriting lebar penuh di tempat-tempat substitusi. Vladimir Reshetnikov ( @vreshetnikov ) menemukan dan memperbaiki kesalahan ini, sehingga tradisi besar toleransi VB pada lebar karakter tetap valid.

2. VB mendukung kutipan cerdas


Oke, ini, tentu saja, agak sepele, tetapi layak disebutkan. Pernahkah Anda melihat kode sampel dalam dokumen teks seperti ini:



Dan setelah menyalin contoh ke kode Anda, apakah Anda menemukan bahwa tidak ada kutipan (yang disorot) yang berfungsi, karena Word mengganti semua kutipan ASCII yang biasa dengan kutipan pintar ?

Saya tidak. Oke, saya sudah memilikinya, tetapi hanya ketika saya menyalin contoh ke C #. Di VB, kutipan cerdas adalah pembatas yang valid untuk string (lucu bahwa tanda kutip Rusia «» tidak berfungsi - Perkiraan. Per.):



Mereka juga bekerja di dalam string, meskipun mungkin dengan cara yang aneh. Jika Anda menggandakan kutipan pintar untuk melarikan diri, maka yang Anda dapatkan saat runtime hanyalah tanda kutip ("dumb"). Ini mungkin tampak sedikit aneh, tetapi tetap sangat praktis, karena hampir di mana pun kutipan pintar diizinkan dalam string. Kompiler TIDAK membuat Anda pasti mengakhiri dengan kutipan cerdas atau menggunakan yang benar jika Anda mulai dengan yang cerdas, sehingga Anda dapat mencampurnya tanpa khawatir. Dan ya, ini juga berfungsi dengan karakter kutipan tunggal yang digunakan untuk komentar:



Saya mencoba membuat Paul Wick ( @panopticoncntrl ) mengakui bahwa dia melakukan ini semata-mata karena dia tersiksa dengan masalah ini ketika mengerjakan spesifikasi, tetapi dia menyangkal kesalahan apa pun. Ini tidak terjadi di VB6, jadi seseorang menambahkan ini nanti.

3. Konstanta preprocessing dapat dari jenis primitif (termasuk tanggal) dan dapat mengandung nilai konstan



4. Operator aritmatika dapat digunakan dalam ekspresi preprocessing




Pengumuman, dll.


5. VB terkadang melewatkan IL mengimplementasikan deklarasi untuk mencegah implementasi implisit antarmuka dengan nama.


Item ini dari kategori esoterisme. Dalam VB, implementasi antarmuka selalu dilakukan secara eksplisit. Tetapi ternyata bahwa dengan tidak adanya implementasi eksplisit, perilaku default dari CLR ketika memanggil metode antarmuka adalah mencari metode publik dengan nama dan tanda tangan. Dalam kebanyakan kasus, ini normal, karena VB biasanya perlu menyediakan implementasi untuk setiap anggota antarmuka yang Anda implementasikan, kecuali untuk satu kasus:

 Interface IFoo Sub Bar() Sub Baz() End Interface Class Foo Implements IFoo Private Sub Bar() Implements IFoo.Bar Exit Sub End Sub Private Sub IFoo_Baz() Implements IFoo.Baz Exit Sub End Sub End Class Class FooDerived Inherits Foo Implements IFoo Public Sub Bar() Implements IFoo.Bar Exit Sub End Sub Public Sub Baz() ' Does something unrelated to what an IFoo.Baz would do. End Sub End Class 

gist.github.com/AnthonyDGreen/39634fd98a0cacc093719ab62d7ab1e6#file-partial-re-implementation-vb

Dalam contoh ini, kelas FooDerived hanya ingin menetapkan kembali IFoo.Bar ke metode baru, tetapi membiarkan implementasi yang tersisa tidak berubah. Ternyata jika kompiler hanya menghasilkan arahan implement untuk FooDerived , CLR juga akan mengambil FooDerived.Baz sebagai implementasi baru dari IFoo.Baz (walaupun dalam contoh ini tidak terkait dengan IFoo ). Dalam C #, ini terjadi secara implisit (dan saya tidak yakin apakah saya bisa menolaknya), tetapi dalam VB, kompiler sebenarnya menghilangkan 'Implements' dari seluruh deklarasi untuk menghindari ini, dan menimpa hanya anggota tertentu yang diimplementasikan kembali. Dengan kata lain, jika Anda bertanya kepada FooDerived apakah IFoo secara langsung, ia akan mengatakan tidak:


Mengapa saya tahu ini dan mengapa ini penting? Selama bertahun-tahun, pengguna VB telah meminta dukungan untuk implementasi implisit dari sebuah antarmuka (tanpa secara eksplisit menentukan Implements dalam setiap deklarasi), biasanya untuk pembuatan kode. Hanya menggabungkan ini dengan sintaks saat ini akan melanggar perubahan, karena FooDerived.Baz sekarang secara implisit mengimplementasikan IFoo.Baz , meskipun belum pernah melakukan ini sebelumnya. Tetapi baru-baru ini, saya belajar lebih banyak tentang perilaku ini ketika membahas potensi masalah dalam desain fitur "implementasi antarmuka default", yang akan memungkinkan antarmuka untuk memasukkan implementasi standar dari beberapa anggota dan tidak memerlukan implementasi ulang di setiap kelas. Ini akan berguna untuk kelebihan beban, misalnya, ketika implementasi cenderung sama untuk semua pelaksana (pendelegasian ke kelebihan beban utama). Skenario lain adalah versi. Jika sebuah antarmuka dapat menyertakan implementasi default, Anda dapat menambahkan anggota baru ke dalamnya tanpa merusak implementasi lama. Tapi ada masalah. Karena perilaku default di CLR adalah untuk mencari implementasi publik dengan nama dan tanda tangan, jika kelas VB tidak mengimplementasikan anggota antarmuka dengan implementasi default, tetapi memiliki anggota publik dengan nama dan tanda tangan yang sesuai, mereka secara implisit mengimplementasikan anggota antarmuka ini, bahkan jika Anda melakukannya sepenuhnya tidak seharusnya. Ada hal-hal yang dapat Anda lakukan untuk menyiasati hal ini ketika set lengkap anggota antarmuka diketahui pada waktu kompilasi. Tetapi jika anggota ditambahkan setelah mengkompilasi kode, itu hanya mengubah perilaku diam-diam saat runtime.

6. VB secara default menyembunyikan anggota kelas dasar dengan nama (Bayangan), dan bukan dengan nama dan tanda tangan (Kelebihan)


Saya pikir perbedaan ini cukup terkenal. Skenarionya adalah ini: Anda mewarisi kelas dasar ( DomainObject ), mungkin di luar kendali Anda, dan mendeklarasikan metode dengan nama yang masuk akal dalam konteks kelas Anda, misalnya, Print :

 Class DomainObject End Class Class Invoice Inherits DomainObject Public Sub Print(copies As Integer) ' Sends contents of invoice to default printer. End Sub End Class 

gist.github.com/AnthonyDGreen/863cfd1e7536fe8bda7cd145795eaf9f#file-shadows-example-vb

Fakta bahwa faktur dapat dicetak masuk akal. Tetapi dalam versi API berikutnya di mana kelas dasar Anda dideklarasikan, mereka memutuskan untuk debugging untuk menambahkan ke semua DomainObjects metode yang menampilkan konten penuh objek di jendela debug. Metode ini secara brilian disebut Cetak. Masalahnya adalah bahwa klien API Anda mungkin memperhatikan bahwa objek Faktur memiliki metode Print() dan Print(Integer) , dan berpikir bahwa ini adalah kelebihan beban terkait. Mungkin yang pertama hanya mencetak satu salinan. Tapi ini sama sekali bukan apa yang Anda bayangkan sebagai penulis Faktur. Anda tidak tahu bahwa DomainObject.Print akan muncul. Jadi ya, ini tidak berfungsi di VB. Ketika situasi ini muncul, peringatan muncul, tetapi yang lebih penting, perilaku default di VB adalah untuk menyembunyikan nama. Yaitu, sampai Anda secara eksplisit menunjukkan dengan kata kunci Overloads bahwa Print Anda adalah kelebihan Print kelas dasar, anggota kelas dasar (dan kelebihannya) sepenuhnya tersembunyi. Hanya API yang awalnya Anda nyatakan yang ditampilkan kepada klien kelas Anda. Ini berfungsi secara default, tetapi Anda dapat melakukannya secara eksplisit melalui kata kunci Shadows. C # hanya dapat melakukan Overloads (meskipun memperhitungkan Shadows ketika merujuk ke pustaka VB) dan melakukannya secara default (menggunakan kata kunci new ). Tetapi perbedaan ini muncul dari waktu ke waktu ketika beberapa hierarki warisan muncul dalam proyek-proyek di mana satu kelas didefinisikan dalam satu bahasa dan yang lain dalam bahasa lain, dan ada metode kelebihan beban, tetapi ini di luar ruang lingkup item saat ini dalam daftar perbedaan.

7. VB11 dan di bawah ini lebih ketat untuk anggota yang dilindungi dalam generik


Bahkan, kami mengubah ini antara VS2013 dan VS2015. Secara khusus, kami memutuskan untuk tidak repot dengan implementasi ulang. Tapi saya menulis perbedaan ini jika Anda menggunakan versi lama dan perhatikan. Singkatnya: jika anggota yang dilindungi dideklarasikan dalam tipe generik, maka pewarisnya, yang juga generik, dapat mengakses anggota yang dilindungi ini hanya melalui instance turunan dengan argumen tipe yang sama.

 Class Base(Of T) Protected x As T End Class Class Derived(Of T) Inherits Base(Of T) Public Sub F(y As Derived(Of String)) ' Error: Derived(Of T) cannot access Derived(Of String)'s ' protected members yx = "a" End Sub End Class 

gist.github.com/AnthonyDGreen/ce12ac986219eb51d6c85fa02c339a2f#file-protected-in-generics-vb

8. Sintaks "Named argumen" dalam atribut selalu menginisialisasi properti / bidang


VB menggunakan sintaks yang sama := untuk menginisialisasi properti atribut / bidang seperti untuk melewati argumen metode dengan nama. Oleh karena itu, tidak ada cara untuk meneruskan argumen ke konstruktor atribut dengan nama.

9. Semua deklarasi tingkat atas (biasanya) secara implisit terletak di root namespace proyek


Perbedaan ini hampir dalam kategori "fitur-fitur canggih", tetapi saya telah memasukkannya dalam daftar karena itu mengubah makna kode. Ada bidang dalam properti proyek VB:


Secara default, ini hanyalah nama proyek Anda pada saat pembuatan. Ini BUKAN bidang yang sama dengan "Ruang nama default" di properti proyek C #. Namespace default hanya menetapkan kode mana yang ditambahkan secara default ke file baru di C #. Tetapi root namespace di VB berarti bahwa, kecuali dinyatakan sebaliknya, setiap deklarasi tingkat atas dalam proyek ini secara implisit ada dalam namespace ini. Inilah sebabnya mengapa templat dokumen VB biasanya tidak mengandung deklarasi namespace. Selain itu, jika Anda menambahkan deklarasi namespace, itu tidak menimpa root, tetapi ditambahkan ke dalamnya:

 Namespace Controllers ' Child namespace. End Namespace Namespace Global.Controllers ' Top-level namespace End Namespace 

gist.github.com/AnthonyDGreen/fd1e5e3a58aee862a5082e1d2b078084#file-root-namespace-vb

Dengan demikian, namespace Controllers sebenarnya mendeklarasikan namespace VBExamples.Controllers , kecuali Anda menyingkirkan mekanisme ini dengan secara eksplisit mendeklarasikan Global di namespace.

Ini nyaman karena menyimpan satu tingkat lekukan dan satu konsep tambahan untuk setiap file VB. Dan ini sangat berguna jika Anda membuat aplikasi UWP (karena semuanya harus ada dalam namespace di UWP), dan itu sangat nyaman jika Anda memutuskan untuk mengubah namespace tingkat atas untuk seluruh proyek Anda, katakanlah, dari beberapa nama kode seperti Roslyn ke rilis yang lebih lama, seperti Microsoft.CodeAnalysis , karena Anda tidak perlu memperbarui setiap file secara manual dalam solusi. Penting juga untuk mengingat hal ini ketika bekerja dengan generator kode, ruang nama XAML, dan .vbproj file .vbproj baru.

10. Modul tidak dihasilkan sebagai kelas abstrak disegel di IL, sehingga mereka tidak persis terlihat seperti kelas C # statis dan sebaliknya.


Modul di VB ada sebelum kelas C # statis, meskipun kami mencoba pada 2010 untuk membuatnya sama dalam hal IL. Sayangnya, ini merupakan perubahan besar, karena XML Serializer (atau mungkin itu biner) untuk versi .NET (saya pikir mereka memperbaikinya) tidak ingin menyambungkan tipe yang bersarang ke dalam tipe yang tidak dapat dibuat (dan kelas abstrak) tidak bisa). Dia melemparkan pengecualian.

Kami menemukan ini setelah melakukan perubahan dan mengembalikannya karena beberapa kode di suatu tempat menggunakan tipe enum yang tertanam dalam modul. Dan karena Anda tidak tahu versi serializer mana program yang dikompilasi akan bekerja, itu tidak akan pernah mungkin untuk mengubahnya, karena itu akan bekerja dalam satu versi aplikasi dan memberikan pengecualian pada yang lain.

11. Anda tidak perlu metode eksplisit untuk titik masuk (Sub Utama) dalam aplikasi WinForms


Jika proyek Anda menggunakan Formulir sebagai objek awal dan tidak menggunakan "Kerangka Aplikasi" (lebih lanjut tentang ini di posting berikutnya), VB menghasilkan Sub Main , yang membuat formulir awal Anda dan meneruskannya ke Application.Run , menyelamatkan Anda seluruh file Untuk mengelola proses ini, baik metode tambahan di Form Anda, atau bahkan perlu memikirkan masalah ini.

12. Jika Anda memanggil beberapa metode runtime VB lama (misalnya, FileOpen), metode pemanggilan akan ditandai secara implisit dengan atribut untuk menonaktifkan inlining karena alasan kebenaran


Singkatnya, metode untuk bekerja dengan file gaya VB6 seperti FileOpen mengandalkan konteks khusus untuk perakitan di mana kode berada. Sebagai contoh, file # 1 dapat berupa log in satu proyek dan konfigurasi di yang lain. Untuk menentukan perakitan mana yang sedang berjalan, Assembly.GetCallingAssembly() dipanggil. Tetapi jika JIT memasukkan metode Anda ke dalam penelepon, maka dari sudut pandang tumpukan, metode runtime VB tidak akan dipanggil oleh metode Anda, tetapi oleh penelepon, yang mungkin berada di majelis berbeda, yang kemudian dapat memungkinkan kode Anda untuk mengakses atau melanggar keadaan internal penelepon. objek Ini bukan masalah keamanan, karena jika kode yang berkompromi sedang berjalan dalam proses Anda, Anda telah kehilangan. Ini masalah kebenaran. Oleh karena itu, jika Anda menggunakan metode ini, kompiler menonaktifkan inlining.

Perubahan ini dibuat pada saat terakhir di 2010, karena JIT x64 SANGAT agresif ketika inlining / mengoptimalkan kode, dan kami menemukannya sangat terlambat, dan itu adalah pilihan paling aman.

13. Jika tipe Anda ditandai dengan atribut DesignerGenerated dan tidak mengandung deklarasi konstruktor eksplisit, maka kompiler yang dihasilkan secara default akan memanggil InitializeComponent jika itu ditentukan untuk tipe ini


Di era sebelum munculnya jenis Parsial, tim VB berperang untuk mengurangi kode boilerplate dalam proyek-proyek WinForms. Tetapi bahkan dengan Partial ini berguna karena memungkinkan file yang dihasilkan untuk sepenuhnya menghilangkan konstruktor, dan pengguna dapat secara manual mendeklarasikannya dalam file-nya jika diperlukan, atau tidak menyatakannya jika tidak. Tanpa ini, perancang akan dipaksa untuk menambahkan konstruktor hanya untuk memanggil InitializeComponent , dan jika pengguna menambahkan juga, mereka akan menjadi duplikat, atau toolkit harus cukup pintar untuk memindahkan konstruktor dari file desainer ke pengguna dan tidak membuat ulang di perancang, jika dia sudah ada di file pengguna.

14. Tidak adanya pengubah parsial TIDAK berarti bahwa jenisnya tidak parsial


Secara teknis, dalam VB, hanya satu kelas yang harus ditandai sebagai Partial. Ini biasanya (dalam proyek GUI) file yang dihasilkan.

Mengapa Ini membuat file pengguna cantik dan bersih, dan bisa sangat nyaman untuk dimasukkan setelah membuat atau menambahkan kode khusus ke kode yang dihasilkan. Namun, disarankan bahwa paling banyak satu kelas tidak memiliki pengubah Parsial, jika tidak, peringatan akan dikeluarkan.

15. Di kelas default, tingkat akses Publik adalah untuk semua hal kecuali bidang, dan dalam struktur Publik, untuk bidang juga


Saya memiliki perasaan campur aduk tentang ini. Dalam C #, semuanya bersifat private secara default (sorakan, enkapsulasi!), Tapi ada argumen yang harus dilakukan tergantung pada apa yang sering Anda nyatakan: kontrak publik atau detail implementasi. Properti dan acara umumnya ditujukan untuk penggunaan eksternal ( public ), dan operator tidak dapat diakses selain public . Namun, saya jarang mengandalkan aksesibilitas default (dengan pengecualian demo seperti contoh dalam artikel ini).

16. VB menginisialisasi bidang SETELAH konstruktor dasar dipanggil, sedangkan C # menginisialisasi bidang-bidang SEBELUM konstruktor dasar disebut


Pernahkah Anda mendengar bagaimana "beberapa" mengatakan bahwa hal pertama yang terjadi di konstruktor adalah panggilan ke konstruktor kelas dasar? Ya, bukan itu masalahnya, setidaknya dalam C #. Di C #, sebelum memanggil base() , eksplisit atau implisit, inisialisasi bidang dieksekusi terlebih dahulu, lalu panggilan konstruktor, dan kemudian kode Anda. Keputusan ini memiliki konsekuensi, dan saya pikir saya tahu mengapa pengembang bahasa bisa berjalan dengan satu atau lain cara. Saya percaya salah satu konsekuensi ini adalah bahwa kode berikut tidak dapat langsung diterjemahkan ke C #:

 Imports System.Reflection Class ReflectionFoo Private StringType As Type = GetType(String) Private StringLengthProperty As PropertyInfo = StringType.GetProperty("Length") Private StringGetEnumeratorMethod As MethodInfo = StringType.GetMethod("GetEnumerator") Private StringEnumeratorType As Type = StringGetEnumeratorMethod.ReturnType Sub New() Console.WriteLine(StringType) End Sub End Class 

gist.github.com/AnthonyDGreen/37d01c8e7f085e06172bfaf6a1e567d4#file-field-in-init-me-reference-vb

Pada hari-hari ketika saya terlibat dalam Refleksi, saya sering menulis kode seperti itu. Dan saya samar-samar mengingat seorang rekan sebelum Microsoft (Josh), yang menerjemahkan kode saya ke C #, kadang-kadang mengeluh tentang perlunya port semua inisialisasi saya ke konstruktor. Dalam C #, dilarang untuk merujuk ke objek yang dibuat sebelum base() dipanggil. Dan karena inisialisasi bidang dieksekusi sebelum panggilan yang ditentukan, mereka juga tidak dapat merujuk ke bidang lain atau anggota instance objek. Jadi contoh ini juga hanya berfungsi di VB:

 MustInherit Class Base ' OOP OP? Private Cached As Object = DerivedFactory() Protected MustOverride Function DerivedFactory() As Object End Class Class Derived Inherits Base Protected Overrides Function DerivedFactory() As Object Return New Object() End Function End Class 

gist.github.com/AnthonyDGreen/fe5ca89e5a98efee97ffee93aa684e50#file-base-derived-init-vb

Di sini kita memiliki kelas dasar, yang seharusnya memiliki banyak fungsi, tetapi membutuhkan beberapa objek kunci untuk manajemen, pekerjaan, yang ditentukan oleh tipe turunan. Ada banyak cara untuk mengimplementasikan template seperti itu, tetapi saya biasanya menggunakan ini karena:

  • dia pendek;
  • tidak mengharuskan saya untuk mendeklarasikan konstruktor;
  • tidak mengharuskan saya untuk memasukkan kode inisialisasi ke dalam konstruktor, jika ada;
  • memungkinkan saya untuk men-cache objek yang dibuat dan tidak memerlukan tipe turunan untuk mendeklarasikan dan mengelola penyimpanan untuk objek yang disediakan, meskipun sekarang ini bukan masalah dengan properti-otomatis.

Lebih jauh, saya telah dalam kedua situasi: ketika sebuah bidang dalam tipe turunan ingin memanggil metode yang dideklarasikan di kelas dasar, dan ketika inisialisasi bidang dari kelas dasar perlu memanggil anggota MustOverride diimplementasikan oleh tipe turunan. Keduanya valid dalam VB dan tidak ada dalam C #, dan itu masuk akal. Jika penginisialisasi bidang C # dapat memanggil anggota kelas dasar, anggota ini dapat bergantung pada bidang yang diinisialisasi dalam konstruktor basis (yang belum berjalan) dan hasilnya hampir pasti salah, dan tidak ada jalan lain untuk mengatasi hal ini.

Tetapi dalam VB, konstruktor dasar telah bekerja, sehingga Anda dapat melakukan apa saja! Dalam situasi yang berlawanan, semuanya sedikit lebih rumit, karena memanggil anggota Overridable dari penginisialisasi (atau konstruktor) dari kelas dasar dapat menyebabkan akses ke bidang sebelum mereka "diinisialisasi". Tetapi hanya implementasi Anda yang tahu apakah ini masalah. Dalam skrip saya, ini tidak terjadi. Mereka tidak bergantung pada keadaan instance, tetapi tidak dapat menjadi anggota yang Shared Overridable karena Anda tidak dapat memiliki anggota yang Shared Overridable dalam bahasa apa pun karena alasan teknis di luar cakupan artikel ini. Selain itu, secara jelas ditentukan apa yang terjadi pada bidang sebelum memulai inisialisasi khusus - mereka diinisialisasi dengan nilai default, seperti semua variabel dalam VB. Tidak ada kejutan.

Jadi mengapa? Sebenarnya, saya tidak tahu apakah skrip saya adalah apa yang ada dalam pikiran tim VB.NET asli saat mereka mendesainnya. Ini hanya berfungsi dalam kasus saya! , : VB , , . . .

, , , C# VB, , VB , C#.

17. (backing field) VB , C#,


( ). E , VB ( IDE) EEvent . C# E , , E , .

18. VB


P , _P' . IntelliSense, . C# «» ( mangled ) , , C# .

? VB , -, «WithEvents», -, , - , .

19. read-only


, , …. VB « » . WithEvents -, non-Custom , , . IntelliSense, , , . FTW! , VB , private set; C#.

 Class Alarm Private ReadOnly Code As Integer ReadOnly Property Status As String = "Disarmed" Sub New(code As Integer) Me.Code = code End Sub Sub Arm() ' I'm motifying the value of this externally read-only property here. _Status = "Armed" End Sub Function Disarm(code As Integer) As Boolean If code = Me.Code Then ' And here. _Status = "Disarmed" Return True Else Return False End If End Function End Class 

gist.github.com/AnthonyDGreen/57ce7962700c5498894ad417296f9066#file-read-only-auto-property-backing-field-is-writeable-vb

20.


, NonSerialized .

VB (expanded) Custom- 2005 (?) , , , NonSerialized . , , , , «» , « ».

, , , , . , , , , two-way bindable ( , PropertyChanged ), , , , , .

, , CLSA «Expert Business Objects» (Rocky Lhotka) , undo/redo ( , , - , , ), . , . , , , .


21. — , ; ( )


, GoTo . , - . , For For Each ; Using , SyncLock With , , , Finally . If Select Case , Do While , Try — , :

 Module Program Sub Main() Dim retryCount = 0 Try Retry: ' IO call. Catch ex As IO.IOException When retryCount < 3 retryCount += 1 GoTo Retry End Try End Sub End Module 

gist.github.com/AnthonyDGreen/b93adcf3c3705e4768dcab0b05b187a0#file-try-goto-retry-vb

, , , .NET VB «». VB6 Quick Basic ( ) . QB, . , « », . GoTo, — , .

: Try , VB - await Catch Finally , , GoTo .

22. <>


, VB ( ) ( static ) ( ). , . Catch 3 . Try Catch , , , Try .

, VB.NET , . CLR VB . : , .

, C# , , «». VB.NET .

23.


, , C# « » ( definite assignment ). , , , « ». , ( ) , , . C/C++. , ! , . , , , — . , , , , , , , , . , BASIC , , «» , = Nothing , = 0 , = False ..

, ( flow analysis ) VB , .

, C# , , , . VB , , , . Roslyn, , API « », , .

24. RaiseEvent , null


, - C# VB. RaiseEvent VB — , null ( ), null - — , .

 ' You don't have to write this: If PropertyChangedEvent IsNot Nothing Then RaiseEvent PropertyChanged(Me, e) End If ' You don't have to write this: Dim handlers = PropertyChangedEvent If handlers IsNot Nothing Then handlers(Me, e) End If ' You don't have to write this either: PropertyChangedEvent?(Me, e) ' Just write this: RaiseEvent PropertyChanged(Me, e) 

gist.github.com/AnthonyDGreen/c3dea3d91ef4ffc50cfa92c41f967937#file-null-safe-event-raising-vb

, null-conditional C# VS2015 C# , VB ( ), , ; VB.NET .

25. ; (shallow clone)


, , 17 , , . (boxed) Object, System.Runtime.CompilerServices.RuntimeHelper.GetObjectValue . , CLR. , :

  • , .
  • , (, Integer ), .
  • , .

, , , , ( late-bound situations ). , , ( ) , , , , ( caller's copy ). , , - , — .

. :

 Class MyEventArgs Property Value As Object End Class Structure MyStruct Public X, Y As Integer End Structure Module Program Sub Main() Dim defaultValue As Object = New MyStruct With {.X = 3, .Y = 5} Dim e = New MyEventArgs With {.Value = defaultValue} RaiseEvent DoSomething(Nothing, e) If e.Value Is defaultValue Then ' No handlers have changed anything. Console.WriteLine("Unchanged.") End If End Sub Event DoSomething(sender As Object, e As MyEventArgs) End Module 

gist.github.com/AnthonyDGreen/422ac4574af92d9bbbf59f0fbc40b74d#file-get-object-value-vb

, WPF, . , . , , . , . , - , , , .

, , « » . IronRuby/Python, dynamic C# ( C#): C# GetObjectValue . object.ReferenceEquals , , , - dynamic C# ( ). == , . C#, , .

26. Select Case «» (fall-through); break


Friday , Sunday — , 5 .

 Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday: Case DayOfWeek.Tuesday: Case DayOfWeek.Wednesday: Case DayOfWeek.Thursday: Case DayOfWeek.Friday: Console.WriteLine("Weekday") Case DayOfWeek.Saturday: Case DayOfWeek.Sunday: Console.WriteLine("Weekend") End Select End Sub End Module 

gist.github.com/AnthonyDGreen/7b7e136c71dd11b2417a6c7267bb3546#file-select-case-no-fallthrough-vb

Roslyn C# , - : «, ? !» «, » . . VS , , , , , . !

. C# , C, C. . , C# , case . - , goto , break . VB break , Exit Select , , VB .

27. Case


, . C#, :

 Module Program Sub Main() Select Case Today.DayOfWeek Case DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday Dim message = "Get to work!" Case DayOfWeek.Saturday, DayOfWeek.Sunday Dim message = "Funtime!" End Select End Sub End Module 

gist.github.com/AnthonyDGreen/bd642061896246c9336255881fb78546#file-select-case-scopes-vb

, message , C# switch case — . . , , - ( , C): , , , , .

28, 29, 30. Select Case , =


, , , , Select Case .

, , . :

  • Select Case — , , …
  • switch — / , « ».

, 26-30. switch , , , , if . IL switch , , If , VB , . switch , , C . VB .

31. , ,


x , , -1, -2, -3:

 Module Program Sub Main() For i = 1 To 3 Dim x As Integer x -= 1 Console.WriteLine(x) Next End Sub End Module 

gist.github.com/AnthonyDGreen/cbc3a9c70677354973d64f1d993a3c5d#file-loop-variables-retain-their-values-vb

« , , » ( ). , VB2008 , -:

 Module Program Sub Main() Dim lambdas = New List(Of Action) For i = 1 To 3 Dim x As Integer x -= 1 lambdas.Add(Sub() Console.WriteLine(x)) Next For Each lambda In lambdas lambda() Next End Sub End Module 

gist.github.com/AnthonyDGreen/2ef9ba3dfcf9a1abe0e94b0cde12faf1#file-loop-variables-captured-per-iteration-vb

-1, -2, -3. x — « », - x , . , x . flow analysis API — ! ( «… … ?» )

? , , , , , #22. , , -, .

, VB C# ( control variables ) For Each VS2012 (?), - « ». 10000% , ( , VB , ). , VB For , . , . , VB For For Each , for foreach C#. , For VB - , , .

32. For


For . , , 1,3,5,7,9, , .

 Module Program Sub Main() Dim lower = 1, upper = 9, increment = 2 For i = lower To upper Step increment Console.WriteLine(i) upper += 1 increment -= 1 Next End Sub End Module 

gist.github.com/AnthonyDGreen/1e48113be204f515c51e221858666ac7#file-for-loop-bounds-cached-vb

, ( ), , , , IndexOutOfRangeExceptions , .

, , , , , C, VB . - , VB , For i = a To b Step c ( , i> b ) ( , i <b ), c ? , , , b , — . , , , , .

33. For Each VB GetEnumerator


For Each , IEnumerable , GetEnumerator , For Each .
, , For Each IEnumerator , , :

 Module Program Sub Main() Dim list = New List(Of Integer) From {1, 2, 3, 4, 5} Dim info = list.FirstAndRest() If info.First IsNot Nothing Then Console.Write(info.First.GetValueOrDefault()) For Each other In info.Additional Console.Write(", ") Console.Write(other) Next Console.WriteLine() End If End Sub <Runtime.CompilerServices.Extension> Function FirstAndRest(Of T As Structure)(sequence As IEnumerable(Of T)) As (First As T?, Additional As IEnumerator(Of T)) Dim enumerator = sequence.GetEnumerator() If enumerator.MoveNext() Then Return (enumerator.Current, enumerator) Else Return (Nothing, enumerator) End If End Function <Runtime.CompilerServices.Extension> Function GetEnumerator(Of T)(enumerator As IEnumerator(Of T)) As IEnumerator(Of T) Return enumerator End Function End Module 

gist.github.com/AnthonyDGreen/d7dbb7a5b98a940765c4adc33e3eaeee#file-for-each-extension-get-enumerator-vb

F# , IEnumerator , For Each , .

VB , ( well-known name ), . , , Add, . C# , (. async / await ). , C# Roslyn () , .
Menit periklanan. 15-16 - .NET- DotNext 2019 Piter . , . , . .

Source: https://habr.com/ru/post/id443684/


All Articles