Type Checking: typeof, GetType, or is?

前端 未结 14 2264
北海茫月
北海茫月 2020-11-22 00:49

I\'ve seen many people use the following code:

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

But I know you could also

相关标签:
14条回答
  • 2020-11-22 01:10
    if (c is UserControl) c.Enabled = enable;
    
    0 讨论(0)
  • 2020-11-22 01:10

    You can use "typeof()" operator in C# but you need to call the namespace using System.IO; You must use "is" keyword if you wish to check for a type.

    0 讨论(0)
  • 2020-11-22 01:17

    Used to obtain the System.Type object for a type. A typeof expression takes the following form:

    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
        */
    

    This sample uses the GetType method to determine the type that is used to contain the result of a numeric calculation. This depends on the storage requirements of the resulting number.

        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
        */
    
    0 讨论(0)
  • 2020-11-22 01:20

    All are different.

    • typeof takes a type name (which you specify at compile time).
    • GetType gets the runtime type of an instance.
    • is returns true if an instance is in the inheritance tree.

    Example

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

    What about typeof(T)? Is it also resolved at compile time?

    Yes. T is always what the type of the expression is. Remember, a generic method is basically a whole bunch of methods with the appropriate type. Example:

    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"
    
    0 讨论(0)
  • 2020-11-22 01:21

    1.

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

    This is illegal, because typeof only works on types, not on variables. I assume obj1 is a variable. So, in this way typeof is static, and does its work at compile time instead of runtime.

    2.

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

    This is true if obj1 is exactly of type int. If obj1 derives from int, the if condition will be false.

    3.

    if (obj1 is int)
    

    This is true if obj1 is an int, or if it derives from a class called int, or if it implements an interface called int.

    0 讨论(0)
  • 2020-11-22 01:21
    Type t = typeof(obj1);
    if (t == typeof(int))
        // Some code here
    

    This is an error. The typeof operator in C# can only take type names, not objects.

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

    This will work, but maybe not as you would expect. For value types, as you've shown here, it's acceptable, but for reference types, it would only return true if the type was the exact same type, not something else in the inheritance hierarchy. For instance:

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

    This would print "o is something else", because the type of o is Dog, not Animal. You can make this work, however, if you use the IsAssignableFrom method of the Type class.

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

    This technique still leaves a major problem, though. If your variable is null, the call to GetType() will throw a NullReferenceException. So to make it work correctly, you'd do:

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

    With this, you have equivalent behavior of the is keyword. Hence, if this is the behavior you want, you should use the is keyword, which is more readable and more efficient.

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

    In most cases, though, the is keyword still isn't what you really want, because it's usually not enough just to know that an object is of a certain type. Usually, you want to actually use that object as an instance of that type, which requires casting it too. And so you may find yourself writing code like this:

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

    But that makes the CLR check the object's type up to two times. It will check it once to satisfy the is operator, and if o is indeed an Animal, we make it check again to validate the cast.

    It's more efficient to do this instead:

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

    The as operator is a cast that won't throw an exception if it fails, instead returning null. This way, the CLR checks the object's type just once, and after that, we just need to do a null check, which is more efficient.

    But beware: many people fall into a trap with as. Because it doesn't throw exceptions, some people think of it as a "safe" cast, and they use it exclusively, shunning regular casts. This leads to errors like this:

    (o as Animal).Speak();
    

    In this case, the developer is clearly assuming that o will always be an Animal, and as long as their assumption is correct, everything works fine. But if they're wrong, then what they end up with here is a NullReferenceException. With a regular cast, they would have gotten an InvalidCastException instead, which would have more correctly identified the problem.

    Sometimes, this bug can be hard to find:

    class Foo{
        readonly Animal animal;
    
        public Foo(object o){
            animal = o as Animal;
        }
    
        public void Interact(){
            animal.Speak();
        }
    }
    

    This is another case where the developer is clearly expecting o to be an Animal every time, but this isn't obvious in the constructor, where the as cast is used. It's not obvious until you get to the Interact method, where the animal field is expected to be positively assigned. In this case, not only do you end up with a misleading exception, but it isn't thrown until potentially much later than when the actual error occurred.

    In summary:

    • If you only need to know whether or not an object is of some type, use is.

    • If you need to treat an object as an instance of a certain type, but you don't know for sure that the object will be of that type, use as and check for null.

    • If you need to treat an object as an instance of a certain type, and the object is supposed to be of that type, use a regular cast.

    0 讨论(0)
提交回复
热议问题