Pertanyaan Jenis Memeriksa: typeof, GetType, atau apakah?


Saya telah melihat banyak orang menggunakan kode berikut:

Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

Tapi saya tahu Anda juga bisa melakukan ini:

if (obj1.GetType() == typeof(int))
    // Some code here

Atau ini:

if (obj1 is int)
    // Some code here

Secara pribadi, saya merasa yang terakhir adalah yang paling bersih, tetapi apakah ada sesuatu yang saya lewatkan? Mana yang terbaik untuk digunakan, atau apakah itu preferensi pribadi?


1187
2018-06-11 19:10


asal


Jawaban:


Semuanya berbeda.

  • typeof mengambil nama jenis (yang Anda tentukan pada waktu kompilasi).
  • GetType mendapat jenis runtime instance.
  • is mengembalikan nilai true jika instance berada di pohon warisan.

Contoh

class Animal { } 
class Dog : Animal { }

void PrintTypes(Animal a) { 
    Console.WriteLine(a.GetType() == typeof(Animal)); // false 
    Console.WriteLine(a is Animal);                   // true 
    Console.WriteLine(a.GetType() == typeof(Dog));    // true
    Console.WriteLine(a is Dog);                      // true 
}

Dog spot = new Dog(); 
PrintTypes(spot);

Bagaimana dengan typeof(T)? Apakah ini juga diselesaikan pada waktu kompilasi?

Iya nih. T selalu seperti apa jenis ekspresi itu. Ingat, metode generik pada dasarnya adalah sejumlah metode dengan tipe yang tepat. Contoh:

string Foo<T>(T object) { return typeof(T).Name; }

Animal probably_a_dog = new Dog();
Dog    definitely_a_dog = new Dog();

Foo(probably_a_dog); // this calls Foo<Animal> and returns "Animal"
Foo<Animal>(probably_a_dog); // this is exactly the same as above
Foo<Dog>(probably_a_dog); // !!! This will not compile. The parameter expects a Dog, you cannot pass in an Animal.

Foo(definitely_a_dog); // this calls Foo<Dog> and returns "Dog"
Foo<Dog>(definitely_a_dog); // this is exactly the same as above.
Foo<Animal>(definitely_a_dog); // this calls Foo<Animal> and returns "Animal". 
Foo((Animal)definitely_a_dog); // this does the same as above, returns "Animal"

1496
2018-06-11 19:15



Menggunakan typeof ketika Anda ingin mendapatkan jenisnya waktu kompilasi. Menggunakan GetType ketika Anda ingin mendapatkan jenisnya waktu eksekusi. Jarang ada kasus yang digunakan is seperti halnya cast dan, dalam banyak kasus, Anda akhirnya casting variabel.

Ada opsi keempat yang belum Anda pertimbangkan (terutama jika Anda akan melemparkan objek ke jenis yang Anda temukan juga); yang digunakan as.

Foo foo = obj as Foo;

if (foo != null)
    // your code here

Ini hanya menggunakan satu cor sedangkan pendekatan ini:

if (obj is Foo)
    Foo foo = (Foo)obj;

membutuhkan dua.


163
2018-06-11 19:14



1

Type t = typeof(obj1);
if (t == typeof(int))

Ini ilegal, karena jenis hanya berfungsi pada jenis, bukan pada variabel. Saya berasumsi obj1 adalah variabel. Jadi, dengan cara ini typeof bersifat statis, dan melakukan kerjanya pada waktu kompilasi, bukan runtime.

2

if (obj1.GetType() == typeof(int))

Ini benar jika obj1 adalah tipe int. Jika obj1 berasal dari int, kondisi if akan salah.

3

if (obj1 is int)

Ini benar jika obj1 adalah int, atau jika berasal dari kelas yang disebut int, atau jika mengimplementasikan antarmuka yang disebut int.


59
2018-06-11 19:17



Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

Ini adalah kesalahan. Operator typeof di C # hanya dapat mengambil nama jenis, bukan objek.

if (obj1.GetType() == typeof(int))
    // Some code here

Ini akan berhasil, tetapi mungkin tidak seperti yang Anda harapkan. Untuk jenis nilai, seperti yang telah Anda perlihatkan di sini, dapat diterima, tetapi untuk tipe referensi, itu hanya akan mengembalikan true jika jenisnya adalah sama persis ketik, bukan sesuatu yang lain dalam hierarki warisan. Contohnya:

class Animal{}
class Dog : Animal{}

static void Foo(){
    object o = new Dog();

    if(o.GetType() == typeof(Animal))
        Console.WriteLine("o is an animal");
    Console.WriteLine("o is something else");
}

Ini akan dicetak "o is something else", karena jenis o aku s Dogtidak Animal. Anda dapat membuat karya ini, namun, jika Anda menggunakan IsAssignableFrom metode dari Type kelas.

if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
    Console.WriteLine("o is an animal");

Teknik ini masih menyisakan masalah besar. Jika variabel Anda null, panggilan ke GetType() akan melempar NullReferenceException. Jadi untuk membuatnya bekerja dengan benar, Anda akan melakukan:

if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
    Console.WriteLine("o is an animal");

Dengan ini, Anda memiliki perilaku yang setara is kata kunci. Oleh karena itu, jika ini adalah perilaku yang Anda inginkan, Anda harus menggunakan is kata kunci, yang lebih mudah dibaca dan lebih efisien.

if(o is Animal)
    Console.WriteLine("o is an animal");

Dalam banyak kasus, meskipun, itu is kata kunci masih bukan yang Anda inginkan, karena biasanya tidak cukup hanya mengetahui bahwa suatu objek adalah jenis tertentu. Biasanya, Anda ingin benar-benar menggunakan objek itu sebagai turunan dari jenis itu, yang juga perlu mentransmisikannya. Jadi Anda mungkin menemukan diri Anda menulis kode seperti ini:

if(o is Animal)
    ((Animal)o).Speak();

Tapi itu membuat CLR memeriksa jenis objek hingga dua kali. Ini akan mengeceknya sekali untuk memuaskan is operator, dan jika o memang sebuah Animal, kami membuatnya memeriksa kembali untuk memvalidasi para pemain.

Lebih efisien untuk melakukan ini sebagai gantinya:

Animal a = o as Animal;
if(a != null)
    a.Speak();

Itu as operator adalah pemain yang tidak akan melempar pengecualian jika gagal, malah kembali null. Dengan cara ini, CLR memeriksa jenis objek hanya sekali, dan setelah itu, kita hanya perlu melakukan pemeriksaan null, yang lebih efisien.

Namun berhati-hatilah: banyak orang jatuh ke dalam perangkap as. Karena itu tidak membuang pengecualian, beberapa orang menganggapnya sebagai pemeran "aman", dan mereka menggunakannya secara eksklusif, membuang gips biasa. Ini menyebabkan kesalahan seperti ini:

(o as Animal).Speak();

Dalam hal ini, pengembang jelas berasumsi itu o akan selalu kacang Animal, dan selama asumsi mereka benar, semuanya berjalan dengan baik. Tetapi jika mereka salah, maka apa yang mereka dapatkan di sini adalah a NullReferenceException. Dengan pemain reguler, mereka akan mendapatkan InvalidCastException Sebaliknya, yang akan lebih tepat mengidentifikasi masalah.

Terkadang, bug ini sulit ditemukan:

class Foo{
    readonly Animal animal;

    public Foo(object o){
        animal = o as Animal;
    }

    public void Interact(){
        animal.Speak();
    }
}

Ini adalah kasus lain di mana pengembang jelas mengharapkan o untuk menjadi seorang Animal setiap kali, tetapi ini tidak jelas dalam konstruktor, di mana as cor digunakan. Tidak jelas sampai Anda mencapai Interact metode, di mana animal bidang diharapkan untuk ditugaskan secara positif. Dalam hal ini, Anda tidak hanya berakhir dengan pengecualian menyesatkan, tetapi tidak dibuang sampai berpotensi jauh lebih lambat daripada ketika kesalahan yang sebenarnya terjadi.

Kesimpulan:

  • Jika Anda hanya perlu mengetahui apakah suatu objek memiliki beberapa jenis, gunakan is.

  • Jika Anda perlu memperlakukan objek sebagai contoh dari jenis tertentu, tetapi Anda tidak tahu pasti bahwa objek akan menjadi seperti itu, gunakan as dan periksa null.

  • Jika Anda perlu memperlakukan objek sebagai turunan dari tipe tertentu, dan objek tersebut seharusnya dari jenis itu, gunakan gips biasa.


39
2018-06-11 19:34



Saya punya Type-properti dibandingkan dengan dan tidak bisa digunakan is (seperti my_type is _BaseTypetoLookFor), tetapi saya bisa menggunakan ini:

base_type.IsInstanceOfType(derived_object);
base_type.IsAssignableFrom(derived_type);
derived_type.IsSubClassOf(base_type);

Perhatikan itu IsInstanceOfType dan IsAssignableFrom kembali true ketika membandingkan jenis yang sama, di mana IsSubClassOf akan kembali false. Dan IsSubclassOf tidak berfungsi pada antarmuka, di mana dua lainnya melakukannya. (Lihat juga pertanyaan dan jawaban ini.)

public class Animal {}
public interface ITrainable {}
public class Dog : Animal, ITrainable{}

Animal dog = new Dog();

typeof(Animal).IsInstanceOfType(dog);     // true
typeof(Dog).IsInstanceOfType(dog);        // true
typeof(ITrainable).IsInstanceOfType(dog); // true

typeof(Animal).IsAssignableFrom(dog.GetType());      // true
typeof(Dog).IsAssignableFrom(dog.GetType());         // true
typeof(ITrainable).IsAssignableFrom(dog.GetType()); // true

dog.GetType().IsSubclassOf(typeof(Animal));            // true
dog.GetType().IsSubclassOf(typeof(Dog));               // false
dog.GetType().IsSubclassOf(typeof(ITrainable)); // false

11
2018-05-15 10:39



aku lebih memilih aku s

Yang mengatakan, jika Anda menggunakan aku s, Anda mungkin tidak menggunakan warisan dengan benar.

Asumsikan bahwa Person: Entity, dan Animal: Entity. Feed adalah metode virtual di Entity (untuk membuat Neil bahagia)

class Person
{
  // A Person should be able to Feed
  // another Entity, but they way he feeds
  // each is different
  public override void Feed( Entity e )
  {
    if( e is Person )
    {
      // feed me
    }
    else if( e is Animal )
    {
      // ruff
    }
  }
}

Agak

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}

7
2018-06-11 19:15



Jika Anda menggunakan C # 7, maka saatnya untuk memperbarui jawaban Andrew Hare yang hebat. Pencocokan pola telah memperkenalkan cara pintas yang bagus yang memberi kita variabel yang diketik dalam konteks pernyataan if, tanpa memerlukan deklarasi / pemeran terpisah dan periksa:

if (obj1 is int integerValue)
{
    integerValue++;
}

Ini terlihat sangat mengecewakan untuk satu pemain seperti ini, tetapi sangat bersinar ketika Anda memiliki banyak tipe yang masuk ke rutinitas Anda. Di bawah ini adalah cara lama untuk menghindari casting dua kali:

Button button = obj1 as Button;
if (button != null)
{
    // do stuff...
    return;
}
TextBox text = obj1 as TextBox;
if (text != null)
{
    // do stuff...
    return;
}
Label label = obj1 as Label;
if (label != null)
{
    // do stuff...
    return;
}
// ... and so on

Bekerja di sekitar menyusutkan kode ini sebanyak mungkin, serta menghindari duplikat gips dari objek yang sama selalu mengganggu saya. Di atas dipadatkan dengan baik dengan pola yang cocok dengan yang berikut:

switch (obj1)
{
    case Button button:
        // do stuff...
        break;
    case TextBox text:
        // do stuff...
        break;
    case Label label:
        // do stuff...
        break;
    // and so on...
}

EDIT: Memperbarui metode baru yang lebih lama untuk menggunakan switch sesuai komentar Palec.


6
2018-02-01 15:47



Saya percaya yang terakhir juga melihat warisan (mis. Dog is Animal == true), yang lebih baik dalam banyak kasus.


5
2018-06-11 19:14



Itu tergantung pada apa yang saya lakukan. Jika saya membutuhkan nilai bool (katakanlah, untuk menentukan apakah saya akan mentransmisikan ke int), saya akan menggunakan is. Jika saya benar-benar membutuhkan jenis untuk beberapa alasan (katakanlah, untuk lulus ke beberapa metode lain) saya akan menggunakan GetType().


2
2018-06-11 19:14



Yang terakhir lebih bersih, lebih jelas, dan juga memeriksa subtipe. Yang lain tidak memeriksa polimorfisme.


0
2018-06-11 19:16