Yazım Denetimi: typeof, GetType, ya da?


1512

Birçok kişinin aşağıdaki kodu kullandığını gördüm:

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

Ama bunu da yapabileceğini biliyorum:

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

Veya bu:

if (obj1 is int)
    // Some code here

Şahsen, en sonuncunun en temiz olduğunu hissediyorum, ama özlediğim bir şey var mı? Hangisini kullanmak en iyisi, yoksa kişisel tercih mi?


28
Unutma as!
RCIX

82
asgerçekten türü olsa kontrol değil ...
jasonh

49
askesinlikle bir tür kontrol biçimidir, her bit olduğu kadar is! isPerde arkasında etkili bir şekilde kullanır ve MSDN'deki her yerde kod temizliğine karşı kod temizliğini geliştirdiği yerlerde kullanılır is. isİlk önce kontrol etmek yerine, askullanıma hazır türünde bir değişken oluşturma çağrısı : Boşsa, uygun şekilde yanıt verin; aksi takdirde devam edin. Kesinlikle biraz gördüğüm ve kullandığım bir şey.
Zaccone

15
Sizin durumunuz için semantik çalışmalarını varsayarsak, ( as/ stackoverflow.com/a/27813381/477420is kapsamındadır ) lehine önemli bir performans farkı vardır .
Alexei Levenkov

@ samusarin yansımayı "kullanmaz". GetTypeEğer bağlantı veriyor yöntem olduğunu System.Reflection.Assemblyburada tamamen farklı bir yöntemle ve alakasız -.
Kirk Woll

Yanıtlar:


1848

Hepsi farklı.

  • typeof tür adını alır (derleme zamanında belirttiğiniz).
  • GetType bir örneğin çalışma zamanı türünü alır.
  • is bir örnek miras ağacındaysa true değerini döndürür.

Misal

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);

Ne olmuş typeof(T)? Derleme zamanında da çözüldü mü?

Evet. T her zaman ifadenin türüdür. Unutmayın, genel bir yöntem temel olarak uygun tipte bir grup yöntemdir. Misal:

string Foo<T>(T parameter) { 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"

29
Yani, eğer Arabadan ve Ford'un bir örneğinden türeyen bir Ford sınıfım varsa, o örnekte "Araba'dır" ifadesi doğru olacaktır. Mantıklı!
jasonh

2
Açıklığa kavuşturmak için bunun farkındaydım, ancak bir kod örneği eklemeden önce yorum yaptım. Zaten mükemmel cevabınıza biraz açık İngilizce netlik eklemek istedim.
jasonh

12
@Shimmy, compeof derleme zamanında ve GetType () çalışma zamanında değerlendirilirse, GetType () 'ın hafif bir performans isabetine girmesi mantıklıdır
Cedric Mamo

Ya yeni Köpek (). GetType () Hayvan Veya typeof (Köpek) Hayvan, sadece bir uyarı verir, bir hata değil?
Prerak K

7
@PrerakK , tür bir nesne new Dog().GetType() is Animaldöndürdüğü için false (ve diğer sürümünüz de) .GetType()döndürür Typeve Typebir değildir Animal.
Maarten

195

typeofTürü derleme zamanında almak istediğinizde kullanın . GetTypeTürü yürütme sırasında almak istediğinizde kullanın . isBir döküm yaptığı gibi kullanmak için nadiren herhangi bir durum vardır ve çoğu durumda, değişkeni yine de dökersiniz.

Göz önünde bulundurmadığınız dördüncü bir seçenek var (özellikle de bir nesneyi bulduğunuz türe de atacaksanız); kullanmaktır as.

Foo foo = obj as Foo;

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

Bu sadece bir oyuncu kullanırken bu yaklaşım:

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

iki tane gerektirir .

Güncelleme (Ocak 2020):

  • C # 7'den itibaren , artık satır içi yayın yapabilirsiniz, böylece 'is' yaklaşımı artık tek bir dökümde de yapılabilir.

Misal:

if(obj is Foo newLocalFoo)
{
    // For example, you can now reference 'newLocalFoo' in this local scope
    Console.WriteLine(newLocalFoo);
}

4
.NET 4'teki değişiklikler ishâlâ oyuncu kadrosu mu yapıyor?
Mart'ta ahsteele

6
Bu cevap doğru mu? Bir örneği typeof () öğesine gerçekten aktarabileceğiniz doğru mu? Deneyimim hayır oldu. Ama sanırım bir örnek kontrolünün çalışma zamanında olması gerekebilir, oysa bir sınıfı kontrol etmek derleme zamanında yapılabilir olmalıdır.
Jon Coombs

4
@jon (q'nuzdan 4 yıl sonra), hayır, bir örneği aktaramazsınız typeof()ve bu cevap yapabileceğinizi göstermez. Bunun yerine türü geçersiniz, yani typeof(string)çalışır, typeof("foo")geçmez.
Abel

Ben isdöküm böyle yapar inanıyorum , IL özel operasyon yerine.
abatishchev

3
Artık yapabilirizif (obj is Foo foo) { /* use foo here */ }
Ivan García Topete

71

1.

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

Bu yasa dışı, çünkü typeof değişkenler üzerinde değil, yalnızca türler üzerinde çalışır. Ben obj1 bir değişken olduğunu varsayalım. Böylece, bu şekildetypeof statiktir ve çalışma zamanını çalışma zamanı yerine derleme zamanında yapar.

2.

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

Bu trueise obj1tam tiptedir int. Eğer obj1türetilmişseint , if koşulufalse .

3.

if (obj1 is int)

Bu trueise obj1, bir olduğu int, ya da adı bir sınıftan türetmesi durumunda int, ya da adı verilen bir arabirim uygulaması halinde int.


1'i düşündüğünüzde haklısınız. Ve yine de, burada birkaç kod örneğinde gördüm. Tür t = obj1.GetType ();
jasonh

4
Evet, sanırım. "typeof (obj1)" denediğimde derlenmiyor.
Scott Langham

4
System.Int32'den veya C # 'da başka bir değer türünden türetmek imkansız
reggaeguitar

ne olacağını söyleyebilir misin (typeof (system.int32))
Sana

1
@Sana, neden denemiyorsunuz :) System.Type türünü temsil eden bir System.Type örneğini geri almayı hayal ediyorum! Typeof
Scott Langham

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

Bu bir hatadır. C # 'daki typeof işleci yalnızca nesne adlarını alabilir, nesneleri değil.

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

Bu işe yarayacak, ama beklediğiniz gibi olmayabilir. Burada gösterildiği gibi değer türleri için kabul edilebilir, ancak referans türleri için, tür yalnızca miras hiyerarşisinde başka bir şey değil , tam olarak aynı türdeyse true değerini döndürür . Örneğin:

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");
}

Bu basacaktır "o is something else"tipi nedeniyle, oolan Dog, değil Animal. Ancak, sınıf IsAssignableFromyöntemini kullanırsanız bu işlemi yapabilirsiniz Type.

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

Yine de bu teknik hala büyük bir sorun bırakıyor. Değişkeniniz null ise, çağrısı GetType()bir NullReferenceException özel durumu oluşturur. Böylece, düzgün çalışmasını sağlamak için:

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

Bununla, isanahtar kelimenin eşdeğer davranışına sahip olursunuz . Bu nedenle, istediğiniz davranış buysa, isdaha okunabilir ve daha verimli olan anahtar kelimeyi kullanmalısınız .

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

Bununla birlikte, çoğu durumda, isanahtar kelime hala gerçekten istediğiniz şey değildir, çünkü genellikle bir nesnenin belirli bir türde olduğunu bilmek yeterli değildir. Genellikle, o nesneyi, bu türün bir örneği olarak kullanmak istersiniz , bu da onu döküm gerektirir. Ve böylece kendinizi böyle kod yazarken bulabilirsiniz:

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

Ancak bu, CLR'nin nesnenin türünü iki kez kontrol etmesini sağlar. isOperatörü tatmin etmek için bir kez kontrol eder ve ogerçekten bir ise Animal, almayı doğrulamak için tekrar kontrol ederiz.

Bunun yerine bunu yapmak daha verimlidir:

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

asOperatör, yerine dönen başarısız olursa bir istisna olmayacak bir döküm olduğununull . Bu şekilde, CLR nesnenin türünü sadece bir kez kontrol eder ve bundan sonra, daha verimli olan bir boş kontrol yapmamız gerekir.

Ama dikkat: birçok insan ile bir tuzağa düşer as. İstisnalar atmadığından, bazı insanlar bunu "güvenli" bir oyuncu olarak düşünür ve sadece düzenli olarak atmalarını sağlar. Bu, aşağıdaki gibi hatalara yol açar:

(o as Animal).Speak();

Bu durumda, geliştirici bunun oher zaman bir olacağını varsayar Animalve varsayımları doğru olduğu sürece her şey iyi çalışır. Ama eğer yanılıyorlarsa, o zaman burada neyle sonuçlanırlar NullReferenceException? Düzenli bir oyuncu kadrosuyla, InvalidCastExceptionsorunu daha doğru bir şekilde tanımlayacak bir yer alacaklardı.

Bazen, bu hatayı bulmak zor olabilir:

class Foo{
    readonly Animal animal;

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

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

Bu geliştirici açıkça bekliyor başka bir durumdur obir olmaya Animalher zaman, ama bu yapıcı, belirgin olmayan asdöküm kullanılır. Alanın pozitif olarak atanması beklenen Interactyönteme ulaşana kadar açık değildir animal. Bu durumda, sadece yanıltıcı bir istisna elde etmekle kalmaz, aynı zamanda gerçek hatanın ortaya çıkmasından çok daha sonraya kadar atılmaz.

Özetle:

  • Yalnızca bir nesnenin bir tür olup olmadığını bilmeniz gerekiyorsa kullanın is.

  • Bir nesneyi belirli bir türün örneği olarak ele almanız gerekiyorsa, ancak nesnenin bu türden olacağından emin değilseniz, kullanın asve kontrol edin null.

  • Bir nesneyi belirli bir türün örneği olarak ele almanız gerekiyorsa ve nesnenin bu türden olması gerekiyorsa, normal bir döküm kullanın.


(o Hayvansal ise) ((Hayvansal) o). ? lütfen daha fazla bilgi verebilir misiniz?
batmaci

2
@batmaci: cevapta - iki tip kontrole neden oluyor. İlk kez o is AnimalDeğişken bir kontrol etmek amacıyla CLR gerektiren, obir bir Animal. İkinci kontrol, ifadede yayınlandığı zamandır ((Animal)o).Speak(). İki kez kontrol etmek yerine, kullanarak bir kez kontrol edin as.
siride

Bu kesinlikle harika bir açıklama buldum, açıklama için teşekkürler!
Paul Efford

16

C # 7 kullanıyorsanız, Andrew Hare'in harika cevabını güncelleme zamanı. Kalıp eşleme , if bildirimi bağlamında bize ayrı bir bildirim / döküm ve denetim gerektirmeden yazılan bir değişken veren güzel bir kısayol tanıttı:

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

Bu, böyle bir tek oyuncu için oldukça etkileyici görünüyor, ancak rutininize gelen birçok olası tipiniz olduğunda gerçekten parlıyor. Aşağıdakiler iki kez dökümden kaçınmanın eski yoludur:

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

Bu kodu olabildiğince küçültmek ve aynı nesnenin yinelenen dökümlerinden kaçınmak her zaman beni rahatsız etti. Yukarıdakiler, aşağıdakilerle eşleşen desenle güzelce sıkıştırılmıştır:

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

EDIT: Palec'in yorumuna göre bir anahtar kullanmak için daha uzun yeni yöntem güncellendi.


1
Kullanılması switchdesen eşleştirme ile deyimi bu durumda tavsiye edilir.
Palec

A ile nasıl başa çıkacaksınız? Bu özel kod bloğunda? if (obj1 is int integerValue) { integerValue++; }
Ben Vertonghen

Ben, sorunuzu anlarsam, tamsayı değişkenine tamsayı koyamayacağınız için diğer durumları ele almak için başka bir ifadem olurdu. :)
JoelC

14

Ben Typekarşılaştırmak için -property vardı ve is(gibi my_type is _BaseTypetoLookFor) kullanmak olamazdı, ama bunları kullanabilirsiniz:

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

Bildirim IsInstanceOfTypeve IsAssignableFromdönüş trueaynı tür karşılaştırırken, IsSubClassOf dönecektir false. Ve IsSubclassOfdiğer ikisinin yaptığı arayüzlerde çalışmaz. (Ayrıca bu soru ve cevaba bakınız .)

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

9

Ben tercih IS

Kullandığınız, söyledi edilir , büyük olasılıkla konum değil düzgün devralma kullanarak.

Varsayalım: Varlık ve şu Hayvan: Varlık. Feed, Varlıkta sanal bir yöntemdir (Neil'i mutlu etmek için)

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
    }
  }
}

Daha doğrusu

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

1
Doğru, Asla asla Hayvandan türediğini bilerek yapmazdım.
jasonh

3
İkincisi de miras kullanmıyor. Foo, Kişi ve Hayvanda geçersiz kılınan sanal bir Varlık yöntemi olmalıdır.
Neil Williams

2
@bobobobo "Miras" değil, "aşırı yükleme" demek istediğinizi düşünüyorum.
Ic.

@lc: Hayır, yani kalıtım. İlk örnek, farklı davranışlar elde etmenin bir tür yanlış yoludur ( is kullanarak ). İkinci örnek aşırı yükleme evet kullanır, ancak is kullanmaktan kaçınır .
bobobobo

1
Örnekle ilgili sorun ölçeklenmemesidir. Yemesi gereken yeni varlıklar (örneğin bir Böcek veya Canavar) eklediyseniz, Entity sınıfına yeni bir yöntem eklemeniz ve ardından onu besleyecek alt sınıflarda geçersiz kılmanız gerekir. (Varlık Y ise) (varlık Y ise) ise bu bir listeden daha fazla tercih edilmez ... Bu, LSP ve OCP'yi ihlal ediyorsa, kalıtım muhtemelen sorunun en iyi çözümü değildir. Muhtemelen bir çeşit heyet tercih edilecektir.
ebrown

5

Sonuncusunun mirasa da baktığına inanıyorum (örneğin Köpek Hayvansal == doğru), bu çoğu durumda daha iyidir.


2

Ne yaptığımla ilgili. Bir bool değerine ihtiyacım varsa (örneğin, bir int'e atacağım mı belirlemek için), kullanacağım is. Aslında herhangi bir nedenden dolayı tipe ihtiyacım varsa (örneğin, başka bir yönteme geçmek için) kullanacağım GetType().


1
İyi bir nokta. Bir türü kontrol etmek için bir if ifadesi kullanan çeşitli cevaplara baktıktan sonra bu soruya geldiğimi belirtmeyi unuttum.
jasonh

0

Sonuncusu daha temiz, daha açık ve aynı zamanda alt tipleri kontrol ediyor. Diğerleri polimorfizmi kontrol etmez.


0

Bir tür için System.Type nesnesini elde etmek için kullanılır. Bir typeof ifadesi aşağıdaki formu alır:

System.Type type = typeof(int);

Example:

    public class ExampleClass
    {
       public int sampleMember;
       public void SampleMethod() {}

       static void Main()
       {
          Type t = typeof(ExampleClass);
          // Alternatively, you could use
          // ExampleClass obj = new ExampleClass();
          // Type t = obj.GetType();

          Console.WriteLine("Methods:");
          System.Reflection.MethodInfo[] methodInfo = t.GetMethods();

          foreach (System.Reflection.MethodInfo mInfo in methodInfo)
             Console.WriteLine(mInfo.ToString());

          Console.WriteLine("Members:");
          System.Reflection.MemberInfo[] memberInfo = t.GetMembers();

          foreach (System.Reflection.MemberInfo mInfo in memberInfo)
             Console.WriteLine(mInfo.ToString());
       }
    }
    /*
     Output:
        Methods:
        Void SampleMethod()
        System.String ToString()
        Boolean Equals(System.Object)
        Int32 GetHashCode()
        System.Type GetType()
        Members:
        Void SampleMethod()
        System.String ToString()
        Boolean Equals(System.Object)
        Int32 GetHashCode()
        System.Type GetType()
        Void .ctor()
        Int32 sampleMember
    */

Bu örnek, sayısal bir hesaplamanın sonucunu içeren türü belirlemek için GetType yöntemini kullanır. Bu, elde edilen numaranın depolama gereksinimlerine bağlıdır.

    class GetTypeTest
    {
        static void Main()
        {
            int radius = 3;
            Console.WriteLine("Area = {0}", radius * radius * Math.PI);
            Console.WriteLine("The type is {0}",
                              (radius * radius * Math.PI).GetType()
            );
        }
    }
    /*
    Output:
    Area = 28.2743338823081
    The type is System.Double
    */

-4
if (c is UserControl) c.Enabled = enable;

4
Lütfen daha fazla bilgi ile düzenleyin. Yalnızca kod ve "bunu dene" yanıtları önerilmez, çünkü bunlar aranabilir içerik içermez ve birisinin neden "bunu denemesi" gerektiğini açıklamaz.
abarisone

Cevabınız soru ile ilgisiz.
menxin

-5

C # "typeof ()" işleci kullanabilirsiniz, ancak System.IO kullanarak ad alanını çağırmak gerekir; Bir türü kontrol etmek istiyorsanız "is" anahtar kelimesini kullanmalısınız.


7
typeofbir ad alanında tanımlanmamışsa, bir anahtar kelimedir. System.IObununla hiçbir ilgisi yok.
Arturo Torres Sánchez

-5

Performans testi typeof () ve GetType ():

using System;
namespace ConsoleApplication1
    {
    class Program
    {
        enum TestEnum { E1, E2, E3 }
        static void Main(string[] args)
        {
            {
                var start = DateTime.UtcNow;
                for (var i = 0; i < 1000000000; i++)
                    Test1(TestEnum.E2);
                Console.WriteLine(DateTime.UtcNow - start);
            }
            {
                var start = DateTime.UtcNow;
                for (var i = 0; i < 1000000000; i++)
                    Test2(TestEnum.E2);
                Console.WriteLine(DateTime.UtcNow - start);
            }
            Console.ReadLine();
        }
        static Type Test1<T>(T value) => typeof(T);
        static Type Test2(object value) => value.GetType();
    }
}

Hata ayıklama modunda sonuçlar:

00:00:08.4096636
00:00:10.8570657

Sürüm modundaki sonuçlar:

00:00:02.3799048
00:00:07.1797128

1
Performans ölçüleri için DateTime.UtcNow kullanılmamalıdır. Kodunuzla ancak Kronometre sınıfıyla Hata Ayıklama modu için kalıcı olarak zıt sonuçlar aldım. UseTypeOf: 00: 00: 14.5074469 UseGetType: 00: 00: 10.5799534. Bırakma modu beklendiği gibi aynı
Alexey Shcherbak

@AlexeyShcherbak Kronometre ve DateTime arasındaki fark.Şimdi 10-20 ms'den fazla olamaz, kodunuzu tekrar kontrol edin. Ve testimde milisaniye umurumda değil. Ayrıca kodum Kronometre ile birkaç satır daha uzun olacak.
Alexander Vasilyev

1
sizin durumunuzda değil, genel olarak kötü bir uygulamadır.
Alexey Shcherbak

4
@AlexanderVasilyev Kod satırlarının sayısı hiçbir zaman belgelenmeyen yanıltıcı bir şey yapmak için argüman olarak kullanılmamalıdır . Görüldüğü gibi msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx , DateTimeaşağıdaki zamanlarda endişe eğer kullanılmamalıdır 100ms o OS'nin zaman aralığı kullanıyor beri. Stopwatchİşlemcilerin Tickkullanıldığı karşılaştırmalı olarak , DateTimeWin7'de bir in tarafından kullanılan çözünürlük 15ms.
Eric Wu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.