Ada dua masalah di sini: 1) pengujian untuk melihat apakah suatu Jenis dapat dibatalkan; dan 2) pengujian untuk melihat apakah suatu objek mewakili Tipe yang dapat dibatalkan.
Untuk edisi 1 (menguji Tipe), inilah solusi yang saya gunakan dalam sistem saya sendiri: Solusi TypeIsNullable-check
Untuk edisi 2 (menguji suatu objek), solusi Dean Chalk di atas berfungsi untuk tipe nilai, tetapi itu tidak berfungsi untuk tipe referensi, karena menggunakan <T> yang berlebihan selalu menghasilkan false. Karena tipe referensi secara inheren nullable, pengujian tipe referensi harus selalu mengembalikan true. Silakan lihat catatan [Tentang "nullability"] di bawah untuk penjelasan tentang semantik ini. Jadi, inilah modifikasi saya untuk pendekatan Dean:
public static bool IsObjectNullable<T>(T obj)
{
// If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
if (!typeof(T).IsValueType || obj == null)
return true;
// Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
return false;
}
public static bool IsObjectNullable<T>(T? obj) where T : struct
{
// Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
return true;
}
Dan inilah modifikasi saya pada kode uji klien untuk solusi di atas:
int a = 123;
int? b = null;
object c = new object();
object d = null;
int? e = 456;
var f = (int?)789;
string g = "something";
bool isnullable = IsObjectNullable(a); // false
isnullable = IsObjectNullable(b); // true
isnullable = IsObjectNullable(c); // true
isnullable = IsObjectNullable(d); // true
isnullable = IsObjectNullable(e); // true
isnullable = IsObjectNullable(f); // true
isnullable = IsObjectNullable(g); // true
Alasan saya memodifikasi pendekatan Dean di IsObjectNullable <T> (Tt) adalah bahwa pendekatan aslinya selalu kembali false untuk tipe referensi. Karena metode seperti IsObjectNullable harus dapat menangani nilai tipe referensi dan karena semua tipe referensi secara inheren nullable, maka jika salah satu tipe referensi atau nol dilewatkan, metode tersebut harus selalu mengembalikan true.
Dua metode di atas dapat diganti dengan metode tunggal berikut dan mencapai hasil yang sama:
public static bool IsObjectNullable<T>(T obj)
{
Type argType = typeof(T);
if (!argType.IsValueType || obj == null)
return true;
return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
}
Namun, masalah dengan pendekatan metode tunggal terakhir ini adalah bahwa kinerja menderita ketika parameter <T> Nullable digunakan. Dibutuhkan lebih banyak waktu prosesor untuk mengeksekusi baris terakhir metode tunggal ini daripada yang memungkinkan kompiler untuk memilih kelebihan metode kedua yang ditunjukkan sebelumnya ketika parameter tipe-Nullable <T> digunakan dalam panggilan IsObjectNullable. Oleh karena itu, solusi optimal adalah dengan menggunakan pendekatan dua metode yang diilustrasikan di sini.
CAVEAT: Metode ini hanya berfungsi jika dipanggil menggunakan referensi objek asli atau salinan yang tepat, seperti yang ditunjukkan dalam contoh. Namun, jika objek nullable dikotakkan ke Tipe lain (seperti objek, dll.) Alih-alih tetap dalam bentuk Nullable <> aslinya, metode ini tidak akan bekerja dengan andal. Jika kode yang memanggil metode ini tidak menggunakan referensi objek asli, tanpa kotak, atau salinan yang tepat, kode tersebut tidak dapat diandalkan untuk menentukan nullability objek menggunakan metode ini.
Dalam sebagian besar skenario pengkodean, untuk menentukan nullability, seseorang harus bergantung pada pengujian Jenis objek asli, bukan referensi (misalnya, kode harus memiliki akses ke Tipe asli objek untuk menentukan nullability). Dalam kasus yang lebih umum ini, IsTypeNullable (lihat tautan) adalah metode yang dapat diandalkan untuk menentukan nullability.
PS - Tentang "nullability"
Saya harus mengulangi pernyataan tentang pembatalan yang saya buat di pos terpisah, yang berlaku langsung untuk menangani topik ini dengan benar. Yaitu, saya percaya fokus pembahasan di sini seharusnya bukan bagaimana memeriksa untuk melihat apakah suatu objek adalah tipe Nullable generik, melainkan apakah seseorang dapat menetapkan nilai nol ke objek jenisnya. Dengan kata lain, saya pikir kita harus menentukan apakah tipe objek nullable, bukan apakah itu Nullable. Perbedaannya adalah dalam semantik, yaitu alasan praktis untuk menentukan nullability, yang biasanya adalah yang terpenting.
Dalam sistem yang menggunakan objek dengan tipe yang mungkin tidak diketahui hingga run-time (layanan web, panggilan jarak jauh, database, feed, dll.), Persyaratan umum adalah untuk menentukan apakah null dapat ditetapkan ke objek, atau apakah objek tersebut mungkin berisi sebuah nol. Melakukan operasi seperti itu pada tipe yang tidak dapat dibatalkan kemungkinan akan menghasilkan kesalahan, biasanya pengecualian, yang sangat mahal baik dalam hal kinerja dan persyaratan pengkodean. Untuk mengambil pendekatan yang sangat disukai untuk secara proaktif menghindari masalah seperti itu, perlu untuk menentukan apakah suatu objek dari Tipe arbitrer mampu mengandung null; yaitu, apakah secara umum 'nullable'.
Dalam arti yang sangat praktis dan khas, nullability dalam istilah .NET sama sekali tidak selalu menyiratkan bahwa Jenis objek adalah bentuk Nullable. Dalam banyak kasus pada kenyataannya, objek memiliki tipe referensi, dapat berisi nilai nol, dan dengan demikian semuanya dapat dibatalkan; tidak ada yang memiliki tipe Nullable. Oleh karena itu, untuk tujuan praktis dalam sebagian besar skenario, pengujian harus dilakukan untuk konsep umum nullability, vs konsep implementasi-tergantung Nullable. Jadi kita tidak boleh menutup telepon hanya dengan memfokuskan pada tipe .NET Nullable tetapi menggabungkan pemahaman kita tentang persyaratan dan perilakunya dalam proses pemfokusan pada konsep umum praktis tentang nullability.