Saya memiliki metode generik yang didefinisikan seperti ini:
public void MyMethod<T>(T myArgument)
Hal pertama yang ingin saya lakukan adalah memeriksa apakah nilai myArgument adalah nilai default untuk jenis itu, sesuatu seperti ini:
if (myArgument == default(T))
Tetapi ini tidak dapat dikompilasi karena saya belum menjamin bahwa T akan mengimplementasikan operator ==. Jadi saya beralih kodenya ke ini:
if (myArgument.Equals(default(T)))
Sekarang ini mengkompilasi, tetapi akan gagal jika myArgument adalah nol, yang merupakan bagian dari apa yang saya uji. Saya dapat menambahkan cek nol eksplisit seperti ini:
if (myArgument == null || myArgument.Equals(default(T)))
Sekarang ini terasa berlebihan bagi saya. ReSharper bahkan menyarankan agar saya mengubah bagian myArgument == null menjadi myArgument == default (T) yang merupakan tempat saya memulai. Apakah ada cara yang lebih baik untuk menyelesaikan masalah ini?
Saya perlu mendukung kedua jenis referensi dan tipe nilai.
if (myArgument?.Equals( default(T) ) != null )
.true
dalam hal apa pun karena kamiEquals
akan selalu dipanggil untuk tipe nilai karenamyArgument
tidak dapatnull
dalam kasus ini dan hasilEquals
(a boolean) tidak akan pernah adanull
.Jawaban:
Untuk menghindari tinju, cara terbaik untuk membandingkan obat generik untuk kesetaraan adalah dengan
EqualityComparer<T>.Default
. Ini menghormatiIEquatable<T>
(tanpa tinju) jugaobject.Equals
, dan menangani semuaNullable<T>
nuansa "terangkat". Karenanya:Ini akan cocok dengan:
Nullable<T>
sumber
Person
,p1.Equals(p2)
akan tergantung pada apakah mengimplementasikanIEquatable<Person>
pada API publik, atau melalui implementasi eksplisit - yaitu apakah kompiler dapat melihatEquals(Person other)
metode publik . Namun; dalam generik , IL yang sama digunakan untuk semuaT
; aT1
yang terjadi untuk mengimplementasikanIEquatable<T1>
perlu diperlakukan secara identik denganT2
yang tidak - jadi tidak, itu tidak akan menemukanEquals(T1 other)
metode, bahkan jika ada saat runtime. Dalam kedua kasus, ada juga yangnull
harus dipikirkan (salah satu objek). Jadi dengan obat generik, saya akan menggunakan kode yang saya posting.Bagaimana dengan ini:
Menggunakan
static object.Equals()
metode ini menghindari keharusan bagi Anda untuk melakukannull
pemeriksaan sendiri. Kualifikasi panggilan secara eksplisitobject.
mungkin tidak perlu tergantung pada konteks Anda, tetapi saya biasanya mengawalistatic
panggilan dengan nama jenis hanya untuk membuat kode lebih mudah larut.sumber
Saya dapat menemukan artikel Microsoft Connect yang membahas masalah ini secara terperinci:
Inilah yang dapat Anda lakukan ...
Saya telah memvalidasi bahwa kedua metode ini berfungsi untuk perbandingan referensi dan tipe nilai umum:
atau
Untuk melakukan perbandingan dengan operator "==", Anda harus menggunakan salah satu metode ini:
Jika semua case T berasal dari kelas dasar yang diketahui, Anda dapat membiarkan kompiler tahu menggunakan batasan tipe generik.
Kompiler kemudian mengenali cara menjalankan operasi
MyBase
dan tidak akan melemparkan "Operator '==' tidak dapat diterapkan ke operan kesalahan tipe 'T' dan 'T'" yang Anda lihat sekarang.Pilihan lain adalah membatasi T untuk semua jenis yang mengimplementasikan
IComparable
.Dan kemudian gunakan
CompareTo
metode yang didefinisikan oleh antarmuka IComparable .sumber
Coba ini:
yang harus dikompilasi, dan lakukan apa yang Anda inginkan.
sumber
Equals
MetodeIEqualityComparer
membutuhkan dua argumen, dua benda untuk membandingkan, sehingga tidak ada, tidak berlebihan.(Diedit)
Marc Gravell memiliki jawaban terbaik, tetapi saya ingin memposting potongan kode sederhana yang saya upayakan untuk menunjukkannya. Jalankan saja ini di aplikasi konsol C # sederhana:
Satu hal lagi: dapatkah seseorang dengan VS2008 mencoba ini sebagai metode ekstensi? Saya terjebak dengan 2005 di sini dan saya ingin tahu apakah itu akan diizinkan.
Sunting: Ini cara membuatnya berfungsi sebagai metode ekstensi:
sumber
Untuk menangani semua jenis T, termasuk di mana T adalah tipe primitif, Anda harus mengkompilasi dalam kedua metode perbandingan:
sumber
Akan ada masalah di sini -
Jika Anda mengizinkan ini berfungsi untuk semua jenis, default (T) akan selalu nol untuk tipe referensi, dan 0 (atau struct penuh 0) untuk tipe nilai.
Ini mungkin bukan perilaku yang Anda cari. Jika Anda ingin ini berfungsi secara umum, Anda mungkin perlu menggunakan refleksi untuk memeriksa tipe T, dan menangani tipe nilai yang berbeda dari tipe referensi.
Atau, Anda bisa meletakkan batasan antarmuka pada ini, dan antarmuka dapat memberikan cara untuk memeriksa terhadap default kelas / struct.
sumber
Saya pikir Anda mungkin perlu membagi logika ini menjadi dua bagian dan memeriksa null terlebih dahulu.
Dalam metode IsNull, kami mengandalkan fakta bahwa objek ValueType tidak bisa nol menurut definisi jadi jika nilai kebetulan kelas yang berasal dari ValueType, kami sudah tahu itu bukan nol. Di sisi lain, jika itu bukan tipe nilai maka kita bisa membandingkan nilai yang dilemparkan ke objek terhadap nol. Kita bisa menghindari tanda centang pada ValueType dengan langsung menuju ke pemain ke objek, tetapi itu berarti bahwa tipe nilai akan mendapatkan kotak yang mungkin ingin kita hindari karena itu menyiratkan bahwa objek baru dibuat pada heap.
Dalam metode IsNullOrEmpty, kami memeriksa kasus khusus string. Untuk semua jenis lainnya, kami membandingkan nilainya (yang sudah diketahui bukan nol) terhadap nilai defaultnya yang untuk semua jenis referensi adalah nol dan untuk jenis nilai biasanya berupa nol (jika tidak terpisahkan).
Menggunakan metode ini, kode berikut berperilaku seperti yang Anda harapkan:
sumber
Metode penyuluhan berdasarkan jawaban yang diterima.
Pemakaian:
Alternatif dengan nol untuk menyederhanakan:
Pemakaian:
sumber
Saya menggunakan:
sumber
Tidak tahu apakah ini berfungsi dengan kebutuhan Anda atau tidak, tetapi Anda bisa membatasi T untuk menjadi Tipe yang mengimplementasikan antarmuka seperti IComparable dan kemudian menggunakan metode ComparesTo () dari antarmuka itu (yang didukung / ditangani nullR oleh IIRC) seperti ini :
Mungkin ada antarmuka lain yang bisa Anda gunakan juga IEquitable, dll.
sumber
@ilitirit:
Operator '==' tidak dapat diterapkan ke operan tipe 'T' dan 'T'
Saya tidak bisa memikirkan cara untuk melakukan ini tanpa tes null eksplisit diikuti dengan memanggil metode atau objek Equals. Sama seperti yang disarankan di atas.
Anda dapat menemukan solusi menggunakan System.Comparison tetapi sebenarnya itu akan berakhir dengan lebih banyak baris kode dan meningkatkan kompleksitas secara substansial.
sumber
Saya pikir kamu sudah dekat.
Sekarang ini mengkompilasi, tetapi akan gagal jika
myArgument
nol, yang merupakan bagian dari apa yang saya uji. Saya dapat menambahkan cek nol eksplisit seperti ini:Anda hanya perlu membalikkan objek tempat persamaan dipanggil untuk pendekatan null-safe yang elegan.
sumber