Saya baru-baru ini bekerja dengan DateTime
objek, dan menulis sesuatu seperti ini:
DateTime dt = DateTime.Now;
dt.AddDays(1);
return dt; // still today's date! WTF?
Dokumentasi intellisense untuk AddDays()
mengatakan itu menambahkan hari ke tanggal, yang tidak - itu benar-benar mengembalikan tanggal dengan hari ditambahkan ke dalamnya, jadi Anda harus menuliskannya seperti:
DateTime dt = DateTime.Now;
dt = dt.AddDays(1);
return dt; // tomorrow's date
Yang ini telah menggigit saya beberapa kali sebelumnya, jadi saya pikir akan berguna untuk membuat katalog C # gotchas terburuk.
Jawaban:
Blammo. Aplikasi Anda macet tanpa jejak tumpukan. Terjadi sepanjang waktu.
(Perhatikan modal
MyVar
alih-alih huruf kecilmyVar
pada pengambil).sumber
Ketik.Tipe
Salah satu yang saya lihat menggigit banyak orang adalah
Type.GetType(string)
. Mereka bertanya-tanya mengapa itu bekerja untuk tipe dalam perakitan mereka sendiri, dan beberapa jenis sukaSystem.String
, tetapi tidakSystem.Windows.Forms.Form
. Jawabannya adalah bahwa ia hanya terlihat dalam perakitan saat ini dan dimscorlib
.Metode anonim
C # 2.0 memperkenalkan metode anonim, yang mengarah ke situasi buruk seperti ini:
Apa yang akan dicetak? Yah, itu sepenuhnya tergantung pada penjadwalan. Ini akan mencetak 10 angka, tetapi mungkin tidak akan mencetak 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 yang mungkin Anda harapkan. Masalahnya adalah bahwa itu adalah
i
variabel yang telah ditangkap, bukan nilainya pada saat penciptaan delegasi. Ini dapat diselesaikan dengan mudah dengan variabel lokal ekstra dari cakupan yang tepat:Eksekusi blok iterator yang ditangguhkan
"Unit test orang miskin" ini tidak lulus - mengapa tidak?
Jawabannya adalah bahwa kode dalam sumber
CapitalLetters
kode tidak dieksekusi sampai metode iteratorMoveNext()
pertama kali dipanggil.Saya memiliki beberapa keanehan lain di halaman brainteasers saya .
sumber
Melempar kembali pengecualian
Gotcha yang mendapat banyak pengembang baru, adalah semantik pengecualian lemparan ulang.
Banyak waktu saya melihat kode seperti berikut ini
Masalahnya adalah itu menghapus jejak stack dan membuat masalah diagnosis jauh lebih sulit, karena Anda tidak dapat melacak di mana pengecualian berasal.
Kode yang benar adalah pernyataan melempar tanpa argumen:
Atau bungkus pengecualian di yang lain, dan gunakan pengecualian dalam untuk mendapatkan jejak tumpukan asli:
sumber
Jendela Jaga Heisenberg
Ini dapat menggigit Anda dengan buruk jika Anda melakukan hal-hal berdasarkan permintaan, seperti ini:
Sekarang katakanlah Anda memiliki beberapa kode di tempat lain menggunakan ini:
Sekarang Anda ingin men-debug
CreateMyObj()
metode Anda . Jadi Anda meletakkan breakpoint pada Jalur 3 di atas, dengan maksud untuk masuk ke dalam kode. Hanya untuk ukuran yang baik, Anda juga meletakkan breakpoint pada baris di atas yang mengatakan_myObj = CreateMyObj();
, dan bahkan breakpoint di dalamnyaCreateMyObj()
.Kode tersebut mencapai breakpoint Anda di Jalur 3. Anda masuk ke dalam kode. Anda berharap memasukkan kode kondisional, karena
_myObj
jelas nol, kan? Uh ... jadi ... mengapa itu melewati kondisi dan langsungreturn _myObj
? ?! Anda mengarahkan mouse ke _myObj ... dan memang, itu memiliki nilai! Bagaimana itu bisa terjadi?!Jawabannya adalah bahwa IDE Anda menyebabkannya mendapatkan nilai, karena Anda memiliki jendela "jam tangan" terbuka - terutama jendela jam tangan "Autos", yang menampilkan nilai-nilai semua variabel / properti yang relevan dengan jalur eksekusi saat ini atau sebelumnya. Ketika Anda mencapai breakpoint Anda di Jalur 3, jendela arloji memutuskan bahwa Anda akan tertarik untuk mengetahui nilai
MyObj
- jadi di balik layar, mengabaikan salah satu breakpoints Anda , ia pergi dan menghitung nilaiMyObj
untuk Anda - termasuk panggilan untukCreateMyObj()
itu menetapkan nilai _myObj!Karena itulah saya menyebutnya Heisenberg Watch Window - Anda tidak dapat mengamati nilainya tanpa mempengaruhinya ... :)
KENA KAU!
Sunting - Saya merasa komentar ChristianHayter layak dimasukkan dalam jawaban utama, karena sepertinya solusi yang efektif untuk masalah ini. Jadi, kapan pun Anda memiliki properti bermalas-malasan ...
sumber
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
atau[DebuggerDisplay("<loaded on demand>")]
.Lazy<T>
kelas (khususnya untuk yangValue
properti) adalah salah satu contoh di mana ini digunakan.ToString
. Setiap kali dia mendekatinya, tooltip memberinya nilai yang berbeda - dia tidak bisa mengetahuinya ...Inilah waktu lain yang membuat saya:
TimeSpan.Seconds adalah bagian detik dari rentang waktu (2 menit dan 0 detik memiliki nilai detik 0).
TimeSpan.TotalSeconds adalah seluruh rentang waktu yang diukur dalam detik (2 menit memiliki nilai total detik 120).
sumber
TimeSpan
bahkan memiliki sebuahSeconds
properti sama sekali. Ngomong-ngomong, siapakah yang memberi keledai tikus berapa porsi jangka waktu kedua? Ini nilai arbitrer, tergantung pada unit; Saya tidak bisa membayangkan penggunaan praktis untuk itu.SecondsPart
danSecondsTotal
untuk membedakan keduanya.Memori bocor karena Anda tidak membatalkan acara.
Ini bahkan menangkap beberapa pengembang senior yang saya tahu.
Bayangkan formulir WPF dengan banyak hal di dalamnya, dan di suatu tempat di sana Anda berlangganan sebuah acara. Jika Anda tidak berhenti berlangganan maka seluruh formulir disimpan dalam memori setelah ditutup dan tidak direferensikan.
Saya percaya masalah yang saya lihat adalah membuat DispatchTimer dalam bentuk WPF dan berlangganan acara Tick, jika Anda tidak melakukan - = pada penghitung waktu, memori Anda bocor!
Dalam contoh ini, kode teardown Anda seharusnya
Yang ini sangat rumit karena Anda membuat instance DispatchTimer di dalam formulir WPF, jadi Anda akan berpikir bahwa itu akan menjadi referensi internal yang ditangani oleh proses Pengumpulan Sampah ... sayangnya DispatchTimer menggunakan daftar langganan dan layanan internal statis. meminta pada utas UI, sehingga referensi 'dimiliki' oleh kelas statis.
sumber
timer.Tick += (s, e,) => { Console.WriteLine(s); }
Mungkin bukan benar-benar gotcha karena perilaku ini ditulis dengan jelas dalam MSDN, tetapi telah mematahkan leher saya sekali karena saya merasa agak kontra-intuitif:
Orang ini meninggalkan
"nice.pic"
file terkunci sampai gambar dibuang. Pada saat saya menghadapinya, saya pikir akan lebih baik memuat ikon dengan cepat dan tidak menyadari (pada awalnya) bahwa saya berakhir dengan lusinan file yang terbuka dan terkunci! Gambar melacak dari mana file itu dimuat dari ...Bagaimana cara mengatasinya? Saya pikir satu kapal akan melakukan pekerjaan itu. Saya mengharapkan parameter tambahan untuk
FromFile()
, tetapi tidak memilikinya, jadi saya menulis ini ...sumber
Jika Anda menghitung ASP.NET, saya akan mengatakan siklus hidup bentuk web adalah gotcha yang cukup besar bagi saya. Saya telah menghabiskan banyak waktu men-debug kode webform yang ditulis dengan buruk, hanya karena banyak pengembang tidak benar-benar mengerti kapan harus menggunakan event handler mana (termasuk saya, sayangnya).
sumber
kelebihan muatan == operator dan kontainer yang tidak diketik (daftar array, dataset, dll.):
Solusi?
selalu gunakan
string.Equals(a, b)
ketika Anda membandingkan tipe stringmenggunakan obat generik ingin
List<string>
memastikan bahwa kedua operan adalah string.sumber
Moral dari cerita: Inisialisasi lapangan tidak berjalan ketika deserialisasi objek
sumber
DateTime.ToString ("hh / MM / tttt") ; Ini sebenarnya tidak akan selalu memberi Anda hh / MM / tttt tetapi sebaliknya akan mempertimbangkan pengaturan regional dan mengganti pemisah tanggal Anda tergantung di mana Anda berada. Jadi Anda mungkin mendapatkan dd-MM-yyyy atau sesuatu yang serupa.
Cara yang tepat untuk melakukan ini adalah dengan menggunakan DateTime.ToString ("dd '/' MM '/' yyyy");
DateTime.ToString ("r") seharusnya dikonversi ke RFC1123, yang menggunakan GMT. GMT berada dalam sepersekian detik dari UTC, namun specifier format "r" tidak dikonversi ke UTC , bahkan jika DateTime yang dimaksud ditentukan sebagai Lokal.
Ini menghasilkan gotcha berikut (bervariasi tergantung pada seberapa jauh waktu lokal Anda dari UTC):
Aduh!
sumber
DateTime.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
Saya melihat ini diposting hari yang lain, dan saya pikir itu cukup jelas, dan menyakitkan bagi mereka yang tidak tahu
Karena itu akan mengembalikan 0 dan bukan 1 seperti yang diharapkan kebanyakan orang
sumber
Aku agak terlambat ke pesta ini, tapi aku punya dua gotcha yang baru-baru ini menggigitku:
Resolusi DateTime
Properti Ticks mengukur waktu dalam 10-juta detik (100 nanodetik blok), namun resolusinya bukan 100 nanodetik, ini sekitar 15 ms.
Kode ini:
akan memberi Anda output (misalnya):
Demikian pula, jika Anda melihat DateTime.Now.Millisecond, Anda akan mendapatkan nilai dalam potongan bulat 15,625ms: 15, 31, 46, dll.
Perilaku khusus ini bervariasi dari satu sistem ke sistem lainnya , tetapi ada gotcha terkait resolusi lainnya di API tanggal / waktu ini.
Jalan. Menggabungkan
Cara yang bagus untuk menggabungkan jalur file, tetapi tidak selalu berperilaku seperti yang Anda harapkan.
Jika parameter kedua dimulai dengan
\
karakter, itu tidak akan memberi Anda path lengkap:Kode ini:
Memberi Anda hasil ini:
sumber
CD
perintah untuk melihat apa yang saya maksud .... 1) GotoC:\Windows\System32
2) KetikCD \Users
3) Woah! Sekarang Anda berada diC:\Users
... GOT IT? ... Path.Combine (@ "C: \ Windows \ System32", @ "\ Users") harus mengembalikan\Users
yang artinya tepat[current_drive_here]:\Users
Ketika Anda memulai proses (menggunakan System.Diagnostics) yang menulis ke konsol, tetapi Anda tidak pernah membaca aliran Console.Out, setelah sejumlah output tertentu aplikasi Anda tampaknya akan menggantung.
sumber
Tidak ada pintasan operator di Linq-To-Sql
Lihat di sini .
Singkatnya, di dalam klausa kondisional dari kueri Linq-To-Sql, Anda tidak dapat menggunakan pintasan bersyarat seperti
||
dan&&
untuk menghindari pengecualian referensi nol; Linq-To-Sql mengevaluasi kedua sisi operator OR atau AND bahkan jika kondisi pertama meniadakan kebutuhan untuk mengevaluasi kondisi kedua!sumber
Menggunakan parameter default dengan metode virtual
sumber
Base
, dari mana kompiler harus mendapatkan nilai default dari jika tidakBase
? Saya akan berpikir sedikit lebih mengerti bahwa nilai default dapat berbeda jika tipe yang dideklarasikan adalah tipe turunan , meskipun metode yang disebut (statis) adalah metode dasar.Nilai objek dalam koleksi yang bisa diubah
tidak berpengaruh.
mypoints[i]
mengembalikan salinanPoint
objek nilai. C # dengan senang hati memungkinkan Anda memodifikasi bidang salinan. Diam-diam tidak melakukan apa-apa.Pembaruan: Ini tampaknya diperbaiki di C # 3.0:
sumber
arr[i].attr=
adalah sintaks khusus untuk array yang tidak dapat Anda kode dalam wadah pustaka; (. Mengapa (<ekspresi nilai>). attr = <expr> diizinkan sama sekali? Bisakah ini masuk akal?Mungkin bukan yang terburuk, tetapi beberapa bagian kerangka kerja .net menggunakan derajat sementara yang lain menggunakan radian (dan dokumentasi yang muncul dengan Intellisense tidak pernah memberi tahu Anda, Anda harus mengunjungi MSDN untuk mengetahuinya)
Semua ini bisa dihindari dengan memiliki
Angle
kelas sebagai gantinya ...sumber
Untuk programmer C / C ++, transisi ke C # adalah alami. Namun, gotcha terbesar yang saya temui (dan telah saya lihat dengan orang lain membuat transisi yang sama) tidak sepenuhnya memahami perbedaan antara kelas dan struct di C #.
Dalam C ++, kelas dan struct identik; mereka hanya berbeda dalam visibilitas default, di mana kelas default untuk visibilitas pribadi dan struct default untuk visibilitas publik. Dalam C ++, definisi kelas ini
secara fungsional setara dengan definisi struct ini.
Di C #, bagaimanapun, kelas adalah tipe referensi sementara struct adalah tipe nilai. Hal ini membuat sebuah BESAR perbedaan dalam (1) memutuskan kapan untuk menggunakan satu atas yang lain, (2) menguji objek kesetaraan, (3) kinerja (misalnya, tinju / pembukaan kemasan), dll
Ada semua jenis informasi di web terkait dengan perbedaan antara keduanya (mis., Di sini ). Saya akan sangat mendorong siapa pun yang melakukan transisi ke C # untuk setidaknya memiliki pengetahuan tentang perbedaan dan implikasinya.
sumber
Pengumpulan dan Buang Sampah (). Meskipun Anda tidak perlu melakukan apa pun untuk membebaskan memori , Anda masih harus membebaskan sumber daya melalui Buang (). Ini adalah hal yang sangat mudah untuk dilupakan ketika Anda menggunakan WinForms, atau melacak objek dengan cara apa pun.
sumber
Array implement
IList
Tapi jangan menerapkannya. Saat Anda memanggil Tambah, ia memberi tahu Anda bahwa itu tidak berfungsi. Jadi mengapa sebuah kelas mengimplementasikan sebuah antarmuka ketika tidak dapat mendukungnya?
Kompilasi, tetapi tidak berfungsi:
Kami memiliki masalah ini banyak, karena serializer (WCF) mengubah semua ILists menjadi array dan kami mendapatkan kesalahan runtime.
sumber
IEnumerable<T>
danIEnumerator<T>
mendukungFeatures
properti serta beberapa metode "opsional" yang kegunaannya akan ditentukan oleh apa yang dilaporkan "Fitur". Saya mendukung poin utama saya, yaitu bahwa ada beberapa kasus di mana kode yang menerimaIEnumerable<T>
akan membutuhkan janji yang lebih kuat daripada yangIEnumerable<T>
disediakan. MemanggilToList
akan menghasilkan suatuIEnumerable<T>
yang menjunjung tinggi janji-janji seperti itu, tetapi dalam banyak kasus akan menjadi tidak perlu mahal. Saya berpendapat bahwa harus ada ...IEnumerable<T>
dapat membuat salinan konten jika diperlukan tetapi dapat menahan diri dari melakukan hal yang tidak perlu.foreach loop lingkup variabel!
mencetak lima "amet", sedangkan contoh berikut berfungsi dengan baik
sumber
MS SQL Server tidak dapat menangani tanggal sebelum 1753. Secara signifikan, itu tidak selaras dengan
DateTime.MinDate
konstanta .NET , yaitu 1/1/1. Jadi, jika Anda mencoba menyelamatkan mindate, tanggal cacat (seperti baru-baru ini terjadi pada saya dalam impor data) atau hanya tanggal lahir William Sang Penakluk, Anda akan berada dalam masalah. Tidak ada solusi bawaan untuk ini; jika Anda perlu bekerja dengan tanggal sebelum 1753, Anda harus menulis solusi sendiri.sumber
The Gotcha Caching Gotcha Jahat
Lihat pertanyaan saya yang mengarah pada penemuan ini, dan blogger yang menemukan masalah.
Singkatnya, DataContext menyimpan cache dari semua objek Linq-to-Sql yang pernah Anda muat. Jika ada orang lain yang membuat perubahan pada catatan yang sebelumnya Anda muat, Anda tidak akan bisa mendapatkan data terbaru, bahkan jika Anda secara eksplisit memuat ulang catatan!
Ini karena properti yang dipanggil
ObjectTrackingEnabled
pada DataContext, yang secara default adalah benar. Jika Anda menyetel properti itu menjadi false, catatan akan dimuat lagi setiap kali ... TETAPI ... Anda tidak dapat mempertahankan perubahan apa pun pada catatan itu dengan SubmitChanges ().KENA KAU!
sumber
Kontrak pada Stream. Baca adalah sesuatu yang saya lihat membuat banyak orang tersandung:
Alasan ini salah adalah yang
Stream.Read
akan membaca paling banyak jumlah byte yang ditentukan, tetapi sepenuhnya bebas untuk membaca hanya 1 byte, bahkan jika 7 byte lainnya tersedia sebelum akhir aliran.Itu tidak membantu bahwa penampilan ini begitu mirip dengan
Stream.Write
yang ini dijamin telah menulis semua byte jika kembali tanpa terkecuali. Juga tidak membantu bahwa kode di atas berfungsi hampir setiap saat . Dan tentu saja itu tidak membantu bahwa tidak ada metode yang siap pakai dan nyaman untuk membaca dengan tepat N byte dengan benar.Jadi, untuk menutup lubang, dan meningkatkan kesadaran akan hal ini, berikut adalah contoh cara yang benar untuk melakukan ini:
sumber
var r = new BinaryReader(stream); ulong data = r.ReadUInt64();
. BinaryReader memilikiFillBuffer
metode juga ...Acara
Saya tidak pernah mengerti mengapa acara adalah fitur bahasa. Mereka rumit untuk digunakan: Anda perlu memeriksa nol sebelum menelepon, Anda perlu membatalkan pendaftaran (sendiri), Anda tidak dapat menemukan siapa yang terdaftar (misalnya: apakah saya mendaftar?). Mengapa acara bukan hanya kelas di perpustakaan? Pada dasarnya spesialis
List<delegate>
?sumber
button.Click += (s, e) => { Console.WriteLine(s); }
?IEventSubscription clickSubscription = button.SubscribeClick((s,e)=>{Console.WriteLine(s);});
dan berhenti berlangganan melaluiclickSubscription.Dispose();
. Jika objek saya akan menyimpan semua langganan sepanjang masa hidupnya,MySubscriptions.Add(button.SubscribeClick((s,e)=>{Console.WriteLine(s);}));
dan kemudianMySubscriptions.Dispose()
untuk membunuh semua langganan.Hari ini saya memperbaiki bug yang berhasil dihindari untuk waktu yang lama. Bug ada di kelas umum yang digunakan dalam skenario multi-ulir dan bidang int statis digunakan untuk menyediakan sinkronisasi bebas kunci menggunakan Interlocked. Bug disebabkan karena setiap instantiation dari kelas generik untuk suatu tipe memiliki statiknya sendiri. Jadi setiap utas memiliki bidang statis sendiri dan tidak menggunakan kunci seperti yang dimaksudkan.
Ini mencetak 5 10 5
sumber
i
memiliki tipeT
.Type
.SomeGeneric<int>
adalah Tipe berbeda dariSomeGeneric<string>
; jadi tentu saja masing-masing memiliki sendiripublic static int i
Enumerables dapat dievaluasi lebih dari satu kali
Ini akan menggigit Anda ketika Anda memiliki enumerable yang malas disebutkan dan Anda mengulanginya dua kali dan mendapatkan hasil yang berbeda. (atau Anda mendapatkan hasil yang sama tetapi dieksekusi dua kali tidak perlu)
Misalnya, saat menulis tes tertentu, saya memerlukan beberapa file temp untuk menguji logikanya:
Bayangkan betapa terkejutnya saya ketika
File.Delete(file)
melemparFileNotFound
!!Apa yang terjadi di sini adalah bahwa
files
enumerable mendapat iterasi dua kali (hasil dari iterasi pertama tidak diingat) dan pada setiap iterasi baru Anda akan menelepon ulangPath.GetTempFilename()
sehingga Anda akan mendapatkan satu set nama file temp yang berbeda.Solusinya adalah, tentu saja, ingin menghitung nilai dengan menggunakan
ToArray()
atauToList()
:Ini bahkan lebih menakutkan ketika Anda melakukan sesuatu yang multi-threaded, seperti:
dan Anda tahu
content.Length
masih 0 setelah semua menulis !! Anda kemudian mulai memeriksa dengan teliti bahwa Anda tidak memiliki kondisi balapan ketika .... setelah satu jam yang terbuang ... Anda tahu itu hanya hal kecil yang bisa Anda dapatkan.sumber
Baru saja menemukan yang aneh yang membuat saya terjebak dalam debug untuk sementara waktu:
Anda dapat menambah nol untuk int nullable tanpa membuang kutipan dan nilainya tetap nol.
sumber
Ya, perilaku ini didokumentasikan, tetapi itu tentu saja tidak membuatnya benar.
sumber