Bagaimana cara memeriksa apakah suatu objek dapat dibatalkan?


203

Bagaimana cara memeriksa apakah objek yang diberikan dapat dibatalkan dengan kata lain cara menerapkan metode berikut ...

bool IsNullableValueType(object o)
{
    ...
}

EDIT: Saya mencari jenis nilai yang dapat dibatalkan. Saya tidak memiliki tipe ref dalam pikiran.

//Note: This is just a sample. The code has been simplified 
//to fit in a post.

public class BoolContainer
{
    bool? myBool = true;
}

var bc = new BoolContainer();

const BindingFlags bindingFlags = BindingFlags.Public
                        | BindingFlags.NonPublic
                        | BindingFlags.Instance
                        ;


object obj;
object o = (object)bc;

foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
    obj = (object)fieldInfo.GetValue(o);
}

obj sekarang merujuk ke objek bertipe bool( System.Boolean) dengan nilai sama dengan true. Yang benar-benar saya inginkan adalah objek bertipeNullable<bool>

Jadi sekarang sebagai pekerjaan di sekitar saya memutuskan untuk memeriksa apakah o dapat dibatalkan dan membuat pembungkus nullable di sekitar obj.


Haruskah kode menyertakan string sebagai nullable? Mereka adalah ValueType non-generik yang tampaknya nullable. Atau apakah mereka bukan ValueType?
TamusJRoyce

String bukan ValueType. Ini adalah tipe Referensi.
Suncat2000

Ini pertanyaan yang sangat bagus! 'Type.IsNullableType ()' agak menipu karena sebenarnya hanya memeriksa jenisnya sebagai 'Nullable <T>', yang tidak mengembalikan hasil yang diharapkan jika Anda benar-benar ingin memeriksa semua jenis yang dapat menerima null nilai (misalnya saya mencoba menggunakan dengan a.IsNullableType (), di mana 'a' adalah 'typeof (string)' ditentukan saat runtime)
ErrCode

Jawabannya ada di fieldInfo.FieldType: periksa apakah FieldType adalah tipe generik dan tipe generik dari tipe <dapat diterima. (Contoh: jika (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))). Jangan mencoba untuk mendapatkan obj.GetType () itu akan memiliki UndelyingSystemType dari Nullable <T> variabel T (dalam kasus Anda dari tipe Boolean, bukan Nullable <Boolean>), itu adalah masalah tinju.
SoLaR

Jawaban:


272

Ada dua jenis nullable - Nullable<T>dan tipe referensi.

Jon telah mengoreksi saya bahwa sulit untuk mendapatkan ketik jika kotak, tetapi Anda bisa dengan obat generik: - jadi bagaimana kalau di bawah ini. Ini sebenarnya tipe pengujian T, tetapi menggunakan objparameter murni untuk inferensi tipe generik (untuk membuatnya mudah dipanggil) - itu akan bekerja hampir identik tanpa objparam, meskipun.

static bool IsNullable<T>(T obj)
{
    if (obj == null) return true; // obvious
    Type type = typeof(T);
    if (!type.IsValueType) return true; // ref-type
    if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
    return false; // value-type
}

Tapi ini tidak akan bekerja dengan baik jika Anda telah mengotakkan nilai ke variabel objek.

Dokumentasi Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type


7
Baris terakhir hanya valid jika Anda entah bagaimana berhasil mendapatkan kotak Nullable <T> daripada tinju langsung ke T. Itu mungkin, tetapi sulit untuk mencapai dari apa yang saya ingat.
Jon Skeet

Kode ini sangat membantu bagi saya, bukan karena saya mendapatkan Nullable kotak <T> tetapi karena saya menulis kelas dasar WPF converter generik dan beberapa properti nullable, jadi saya menggunakan Nullable.GetUnderlyingType untuk mendeteksi kasing dan Activator.CreateInstance untuk membuat nullable kotak, (Convert.ChangeType tidak menangani nullables btw).
Qwertie

1
@ Bel jika Anda bermaksud mengeditnya untuk mengklarifikasi bahwa dia tidak mempertimbangkan jenis referensi, saya pikir jawaban saya mendahului edit itu; pembaca dapat membuat keputusan sendiri di sana, berdasarkan kebutuhan mereka sendiri, saya curiga (dikonfirmasikan: komentarnya adalah ref-type seperti ditambahkan pada 14:42; jawaban saya adalah semua <= 14:34)
Marc Gravell

1
Akankah (obj == null) memberikan pengecualian ketika obj = 1?
Qi Fan

3
@JustinMorgan Jika Tparameter generik dibatasi oleh T : struct, maka Ttidak boleh Nullable<>, jadi Anda tidak perlu memeriksa dalam kasus itu! Saya tahu jenisnya Nullable<>adalah sebuah struct, tetapi dalam C # batasannya where T : structsecara khusus mengecualikan tipe-nilai yang dapat dibatalkan. Spesifikasi mengatakan: "Perhatikan bahwa meskipun diklasifikasikan sebagai tipe nilai, tipe nullable (§4.1.10) tidak memenuhi batasan tipe nilai."
Jeppe Stig Nielsen

46

Ada solusi yang sangat sederhana menggunakan kelebihan metode

http://deanchalk.com/is-it-nullable/

kutipan:

public static class ValueTypeHelper
{
    public static bool IsNullable<T>(T t) { return false; }
    public static bool IsNullable<T>(T? t) where T : struct { return true; }
}

kemudian

static void Main(string[] args)
{
    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    bool result1 = ValueTypeHelper.IsNullable(a); // false
    bool result2 = ValueTypeHelper.IsNullable(b); // true
    bool result3 = ValueTypeHelper.IsNullable(c); // false
    bool result4 = ValueTypeHelper.IsNullable(d); // false
    bool result5 = ValueTypeHelper.IsNullable(e); // true
    bool result6 = ValueTypeHelper.IsNullable(f); // true

7
ditambah satu untuk Anda, Tuan untuk menambahkan kasus uji. Saya telah menggunakan test case untuk memeriksa semua jawaban lainnya. Lebih banyak orang harus melakukan ini ekstra.
Marty Neal

4
Untuk apa nilainya, ini tidak berfungsi di VB.NET. Ini menghasilkan kesalahan kompilator " Resolusi kelebihan beban gagal karena tidak dapat diakses 'IsNullable' paling spesifik untuk argumen ini " dalam semua situasi di mana Trueakan dikembalikan.
ckittel

1
Saya sangat suka solusi ini - dan sayang sekali VB tidak bisa mengatasinya. Saya mencoba bekerja dengan ValueType tetapi mengalami masalah dengan kompiler VB yang tidak konsisten tentang overload yang digunakan berdasarkan apakah itu disebut sebagai metode bersama atau ekstensi, saya bahkan mengajukan pertanyaan tentang ini karena tampaknya aneh: stackoverflow.com/ pertanyaan / 12319591 / ...
James Close

22
Anda sedang memeriksa tipe waktu kompilasi , tetapi sudah jelas (dari intellisense) jika tipe waktu kompilasi nullable ( System.Nullable<>). Jika Anda berkata object g = e;dan kemudian ValueTypeHelper.IsNullable(g), apa yang Anda harapkan untuk dapatkan?
Jeppe Stig Nielsen

18
Saya baru saja memverifikasi; ini tidak berhasil , seperti kata Jeppe. Jika variabel dilemparkan ke objek, itu akan selalu mengembalikan false. Jadi, Anda tidak dapat menentukan jenis objek yang tidak dikenal saat runtime dengan cara ini. Satu-satunya waktu ini berfungsi adalah jika jenisnya diperbaiki pada waktu kompilasi, dan dalam hal ini Anda tidak memerlukan pemeriksaan runtime sama sekali.
HugoRune

30

Pertanyaan "Bagaimana cara memeriksa apakah suatu jenis dapat dibatalkan?" sebenarnya "Bagaimana memeriksa apakah suatu tipe adalah Nullable<>?", yang dapat digeneralisasi menjadi "Bagaimana memeriksa apakah suatu tipe adalah tipe yang dikonstruksi dari beberapa tipe generik?", sehingga tidak hanya menjawab pertanyaan "Apakah Nullable<int>a Nullable<>?", tetapi juga "Apakah List<int>a List<>?".

Sebagian besar solusi yang disediakan menggunakan Nullable.GetUnderlyingType()metode ini, yang jelas hanya akan bekerja dengan case Nullable<>. Saya tidak melihat solusi reflektif umum yang akan bekerja dengan semua jenis generik, jadi saya memutuskan untuk menambahkannya di sini untuk anak cucu, meskipun pertanyaan ini sudah dijawab sejak lama.

Untuk memeriksa apakah suatu jenis Nullable<>menggunakan bentuk refleksi, Anda harus terlebih dahulu mengubah tipe generik yang Anda buat, misalnya Nullable<int>, ke definisi tipe generik Nullable<>,. Anda dapat melakukannya dengan menggunakan GetGenericTypeDefinition()metode Typekelas. Anda kemudian dapat membandingkan jenis yang dihasilkan dengan Nullable<>:

Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true

Hal yang sama dapat diterapkan untuk semua jenis generik:

Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

Beberapa tipe mungkin terlihat sama, tetapi jumlah argumen tipe yang berbeda berarti tipe yang sama sekali berbeda.

Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false

Karena Typeobjek dipakai sekali per jenis, Anda dapat memeriksa kesetaraan referensi di antara mereka. Jadi, jika Anda ingin memeriksa apakah dua objek memiliki definisi tipe generik yang sama, Anda dapat menulis:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

Jika Anda ingin memeriksa apakah suatu objek nullable, bukan a Type, maka Anda dapat menggunakan teknik di atas bersama dengan solusi Marc Gravell untuk membuat metode yang agak sederhana:

static bool IsNullable<T>(T obj)
{
    if (!typeof(T).IsGenericType)
        return false;

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}

@ AllonGuralnek Ada versi yang disederhanakan di sana dalam jawaban saya. Saya ingin menjadikannya sebagai edit dan karena reputasi saya tidak setingkat Anda, itu akan diedit tanpa nama saya pada jawaban Anda, meskipun demikian, tampaknya peninjauan itu selalu menembaki kaki saya, bahwa itu adalah pengarang yang menghargai penulis meskipun itu tidak. Dunia yang aneh, beberapa orang tidak mendapatkan definisi :).
ipavlu

@ ipavlu: Versi Anda tidak disederhanakan, itu sebenarnya lebih rumit. Saya pikir maksud Anda ini dioptimalkan sejak Anda menyimpan hasilnya. Itu membuatnya lebih sulit untuk dipahami.
Allon Guralnek

@ AllonGuralnek kelas generik statis dan bidang statis satu kali diinisialisasi, yang rumit? Ya Tuhan, aku membuat kejahatan yang mengerikan :).
ipavlu

@ipavku: Ya, karena tidak ada hubungannya dengan pertanyaan "Bagaimana cara memeriksa apakah suatu objek dapat dibatalkan?". Saya mencoba untuk tetap sederhana dan to the point, dan saya menghindari memperkenalkan konsep yang tidak perlu dan tidak terkait.
Allon Guralnek

1
@nawfal: Jika saya mengerti Anda dengan benar, pencarian Anda implementasi saya dalam menghadapi keberadaan Nullable.GetUnderlyingType()yang sudah disediakan oleh kerangka kerja. Mengapa tidak hanya menggunakan metode dalam kerangka kerja? Nah, kamu harus. Lebih jelas, lebih ringkas dan lebih teruji. Tetapi dalam posting saya, saya mencoba mengajarkan bagaimana menggunakan refleksi untuk mendapatkan informasi yang Anda inginkan, sehingga seseorang dapat menerapkannya ke semua jenis (dengan mengganti typeof(Nullable<>)dengan jenis lain). Jika Anda melihat sumber GetUnderlyingType()(asli atau didekompilasi), Anda akan melihatnya sangat mirip dengan kode saya.
Allon Guralnek

30

Ini bekerja untuk saya dan tampaknya sederhana:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

Untuk jenis nilai:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}

7
Untuk apa nilainya, ini juga merupakan tes yang digunakan oleh Microsoft
canton7

1
Bagus ... Apakah ini bukan jawaban teratas karena itu datang kemudian? Saya menemukan jawaban teratas sangat membingungkan.
Vincent Buscarello

1
Ini harus menjadi jawaban teratas. Setelah berhari-hari mencoba metode yang berbeda, saya secara acak memikirkan solusi ini, mencobanya, dan tampaknya berfungsi dengan baik (dibandingkan dengan jawaban berperingkat teratas)
user3163495

2
Ini adalah solusi yang sangat baik untuk mengetahui apakah ada instance yang dapat diatur ke NULL, tetapi itu akan mengembalikan true untuk semua yang dapat diatur ke nol, termasuk objek biasa. Sangat penting untuk menyadari bahwa pertanyaan asli secara khusus ingin mendeteksi ValueTypes Nullable.
JamesHoux

20

Nah, Anda bisa menggunakan:

return !(o is ValueType);

... tetapi sebuah objek itu sendiri tidak dapat dibatalkan atau sebaliknya - suatu tipe adalah. Bagaimana Anda berencana menggunakan ini?


2
Ini membuat saya sedikit marah. misalnya int? i = 5; typeof (i) mengembalikan System.Int32 bukannya Nullable <Int32> - typeof (int?) mengembalikan Nullable <Int32> .. di mana saya bisa mendapatkan kejelasan tentang topik ini?
Gishu

2
typeof (i) akan memberikan kesalahan kompiler - Anda tidak bisa menggunakan typeof dengan variabel. Apa yang sebenarnya kamu lakukan?
Jon Skeet

15
i.GetType () akan mengotakkan ke Objek terlebih dahulu, dan tidak ada yang namanya kotak nullable - Nullable <int> akan dikotakkan ke referensi nol atau int kotak.
Jon Skeet

Cara itu lebih baik daripada Nullable.GetUnderlyingType (type)! = Null?
Kiquenet

@Kiquenet: Kami tidak memiliki tipe di sini - hanya nilainya.
Jon Skeet

11

Cara paling sederhana yang bisa saya pikirkan adalah:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}

+1. Solusi luar biasa untuk jenis kemas tanpa kotak. Saya belum menguji ini secara khusus. Jadi, jika orang lain dapat memverifikasi, itu akan dihargai.
TamusJRoyce

Saya sudah mengujinya. Saya harus membuat sejenis Nullable, tetapi dengan semantik yang berbeda. Dalam situasi saya, saya harus mendukung nullsebagai nilai yang valid dan juga tidak mendukung nilai sama sekali. Jadi suatu Optionaljenis menciptakan . Karena itu diperlukan untuk mendukung nullnilai-nilai, saya juga harus menerapkan kode untuk menangani Nullablenilai-nilai sebagai bagian dari implementasi saya. Dari situlah kode ini berasal.
CARLOS LOTH

9
Saya pikir solusi ini salah. Melewati tipe nilai Nullable sebagai argumen ke metode yang mengharapkan parameter tipe objek harus menyebabkan tinju terjadi. Nullable adalah tipe nilai dan hasil konversi tinju adalah tipe referensi. Tidak ada nullable kotak. Saya percaya metode ini selalu mengembalikan false?
Mishax

1
Adakah tes tentang hal itu seperti jawaban lain?
Kiquenet

5
Itu tidak bekerja karena nilai tinju. Itu akan selalu mengembalikan FALSE.
N Rocking

10

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.


8

Solusi paling sederhana yang saya buat adalah dengan mengimplementasikan solusi Microsoft ( Cara: Mengidentifikasi Jenis Nullable (C # Programming Guide) ) sebagai metode ekstensi:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

Ini kemudian dapat disebut seperti ini:

bool isNullable = typeof(int).IsNullable();

Ini juga tampaknya cara yang logis untuk mengakses IsNullable()karena cocok dengan semua IsXxxx()metode lain di Typekelas.


1
Bukankah Anda ingin menggunakan "==" bukannya "! ="?
vkelman

Good spot @vkelman Alih-alih melakukan perubahan itu, saya memperbarui jawaban untuk menggunakan saran saat ini dari Microsoft karena ini telah berubah sejak saya menulis ini.
sclarke81

6

Hati-hati, saat meninju tipe nullable ( Nullable<int>atau int? Misalnya):

int? nullValue = null;
object boxedNullValue = (object)nullValue;
Debug.Assert(boxedNullValue == null);

int? value = 10;
object boxedValue = (object)value;
Debug.Assert( boxedValue.GetType() == typeof(int))

Itu menjadi tipe referensi yang benar, jadi Anda kehilangan fakta bahwa itu nullable.


3

Mungkin sedikit di luar topik, tetapi masih beberapa informasi menarik. Saya menemukan banyak orang yang menggunakan Nullable.GetUnderlyingType() != nullidentitas jika suatu tipe dapat dibatalkan. Ini jelas bekerja, tetapi Microsoft menyarankan berikut ini type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)(lihat http://msdn.microsoft.com/en-us/library/ms366789.aspx ).

Saya melihat ini dari sisi kinerja. Kesimpulan dari pengujian (satu juta upaya) di bawah ini adalah bahwa ketika suatu jenis nullable, opsi Microsoft memberikan kinerja terbaik.

Nullable.GetUnderlyingType (): 1335ms (3 kali lebih lambat)

GetGenericTypeDefinition () == typeof (Nullable <>): 500ms

Saya tahu bahwa kita berbicara tentang sejumlah kecil waktu, tetapi semua orang suka men-tweak milidetik :-)! Jadi jika Anda bos ingin Anda mengurangi beberapa milidetik maka ini adalah penyelamat Anda ...

/// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
    int attempts = 1000000;

    Type nullableType = typeof(Nullable<int>);

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
    {
        Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable"); 
    }

    Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);

    stopwatch.Restart();

    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
   {
       Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
   }

   Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
   stopwatch.Stop();
}

1
Hai, mungkin ada satu masalah dengan pengukuran waktu, Pernyataan dapat memengaruhi hasil. Sudahkah Anda menguji tanpa Menegaskan? Console.WriteLine juga harus berada di luar area meteran. +1 untuk upaya mengukur masalah kinerja :).
ipavlu

@ipavlu Console.WriteLinememang di luar area meteran;)
nawfal

Roel, seperti yang disebutkan ipavlu, Assertharus berada di luar loop. Kedua, Anda juga harus mengujinya terhadap non-nullable juga untuk menguji kasus-kasus palsu. Saya melakukan tes yang sama (2 nulables dan 4 non-nullables) dan saya mendapatkan ~ 2 detik untuk GetUnderlyingTypedan ~ 1 detik untuk GetGenericTypeDefinition, yaitu GetGenericTypeDefinitiondua kali lebih cepat (bukan tiga kali).
nawfal

Lakukan putaran lain dengan 2 nullables dan 2 non-nullables - kali GetUnderlyingTypeini lebih lambat 2,5 kali. Dengan hanya non-nullables - kali ini keduanya leher dan leher.
nawfal

Tetapi yang lebih penting, GetUnderlyingTypeberguna ketika Anda harus memeriksa nullability & mendapatkan tipe yang mendasarinya jika itu nullable. Ini sangat berguna dan Anda sering melihat pola Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). Itu seperti askata kunci, memeriksa para pemeran serta melakukannya & mengembalikan hasil. Jika Anda ingin mendapatkan tipe nullable yang mendasarinya kembali maka melakukan GetGenericTypeDefinitionpemeriksaan dan kemudian mendapatkan tipe generik akan menjadi ide yang buruk. Juga GetUnderlyingTypejauh lebih mudah dibaca & berkesan. Saya tidak keberatan jika saya melakukannya hanya ~ 1000 kali.
nawfal

0

Versi ini:

  • hasil caching lebih cepat,
  • tidak memerlukan variabel yang tidak perlu, seperti Metode (T obj)
  • TIDAK RUMIT :),
  • hanya kelas generik statis, yang memiliki bidang perhitungan satu kali

:

public static class IsNullable<T>
{
    private static readonly Type type = typeof(T);
    private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    public static bool Result { get { return is_nullable; } }
}

bool is_nullable = IsNullable<int?>.Result;

Saya pikir Anda menjawab sendiri dengan deklarasi statis 'is_nullable'. Kiat: mendeklarasikan objek dengan int? (objek a = (int?) 8;) dan lihat apa yang terjadi.
SoLaR

0

Inilah yang saya pikirkan, karena segala sesuatu yang lain tampaknya gagal - setidaknya pada PLC - Portable Class Library / .NET Core dengan> = C # 6

Solusi: Perluas metode statis untuk Jenis apa pun Tdan Nullable<T>dan gunakan fakta bahwa metode ekstensi statis, yang cocok dengan jenis yang mendasarinya akan dipanggil dan diutamakan daripada generikT ekstensi .

Untuk T:

public static partial class ObjectExtension
{
    public static bool IsNullable<T>(this T self)
    {
        return false;
    }
}

dan untuk Nullable<T>

public static partial class NullableExtension
{
    public static bool IsNullable<T>(this Nullable<T> self) where T : struct
    {
        return true;
    }
}

Menggunakan Refleksi dan type.IsGenericType... tidak berfungsi pada rangkaian .NET Runtimes saya saat ini. Dokumentasi MSDN juga tidak membantu.

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

Sebagian karena API Refleksi telah diubah cukup signifikan dalam .NET Core.


0

Saya pikir yang menggunakan pengujian yang disarankan Microsoft IsGenericTypebagus, tetapi dalam kode untuk GetUnderlyingType, Microsoft menggunakan tes tambahan untuk memastikan Anda tidak lulus dalam definisi tipe umum Nullable<>:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));

-1

cara sederhana untuk melakukan ini:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

ini adalah unit test saya dan semuanya lulus

    IsNullable_String_ShouldReturn_True
    IsNullable_Boolean_ShouldReturn_False
    IsNullable_Enum_ShouldReturn_Fasle
    IsNullable_Nullable_ShouldReturn_True
    IsNullable_Class_ShouldReturn_True
    IsNullable_Decimal_ShouldReturn_False
    IsNullable_Byte_ShouldReturn_False
    IsNullable_KeyValuePair_ShouldReturn_False

tes unit aktual

    [TestMethod]
    public void IsNullable_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Boolean_ShouldReturn_False()
    {
        var typ = typeof(bool);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Enum_ShouldReturn_Fasle()
    {
        var typ = typeof(System.GenericUriParserOptions);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Nullable_ShouldReturn_True()
    {
        var typ = typeof(Nullable<bool>);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Class_ShouldReturn_True()
    {
        var typ = typeof(TestPerson);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Decimal_ShouldReturn_False()
    {
        var typ = typeof(decimal);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Byte_ShouldReturn_False()
    {
        var typ = typeof(byte);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_KeyValuePair_ShouldReturn_False()
    {
        var typ = typeof(KeyValuePair<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.