Type Checking: typeof, GetType, or is?
我见过很多人使用以下代码:
但我知道你也可以这样做:
1 2 |
或者:
1 2 |
就我个人而言,我觉得最后一个是最干净的,但有什么我遗漏了吗?哪一个最好用,还是个人喜好?
一切都不同。
typeof 采用类型名(在编译时指定)。GetType 获取实例的运行时类型。- 如果实例在继承树中,则
is 返回true。
例子
1 2 3 4 5 6 7 8 9 10 11 12 | 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?
对。t总是表达式的类型。记住,一般方法基本上是一组具有适当类型的方法。例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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" |
当您想在编译时获取类型时,请使用
还有第四种选择是您没有考虑的(特别是如果您要将对象强制转换为您找到的类型);那就是使用
1 2 3 4 | Foo foo = obj as Foo; if (foo != null) // your code here |
这只使用一个强制转换,而此方法:
1 2 |
需要两个。
1。
这是非法的,因为typeof只适用于类型,而不适用于变量。我假设obj1是一个变量。所以,通过这种方式,typeof是静态的,它在编译时而不是运行时工作。
2。
1 |
如果obj1的类型正好是int,则为true。如果obj1从int派生,则if条件将为false。
三。
1 |
如果obj1是一个int,或者它是从一个名为int的类派生的,或者它实现了一个名为int的接口,那么这是真的。
这是一个错误。C中的typeof运算符只能使用类型名,不能使用对象。
1 2 |
这是可行的,但可能不像你所期望的那样。对于值类型,如本文所示,它是可以接受的,但是对于引用类型,只有当类型是完全相同的类型,而不是继承层次结构中的其他类型时,它才会返回true。例如:
1 2 3 4 5 6 7 8 9 10 |
这将打印
1 2 | if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type Console.WriteLine("o is an animal"); |
不过,这项技术仍然存在一个主要问题。如果变量为空,则对
1 2 |
这样,您就有了与
1 2 |
不过,在大多数情况下,
1 2 |
但这使得clr最多检查两次对象的类型。它将检查一次以满足
这样做更有效率:
1 2 3 | Animal a = o as Animal; if(a != null) a.Speak(); |
但是要小心:很多人都会被
1 | (o as Animal).Speak(); |
在这种情况下,开发者明确地假设
有时,很难找到此错误:
1 2 3 4 5 6 7 8 9 10 11 | class Foo{ readonly Animal animal; public Foo(object o){ animal = o as Animal; } public void Interact(){ animal.Speak(); } } |
这是另一种情况,开发人员显然希望
综上所述:
如果您只需要知道一个对象是否属于某种类型,那么使用
is 。如果需要将对象视为某个类型的实例,但您不确定该对象是否属于该类型,请使用
as 并检查null 。如果需要将对象视为某个类型的实例,并且该对象应该是该类型的,请使用常规强制转换。
我有一个
1 2 3 | base_type.IsInstanceOfType(derived_object); base_type.IsAssignableFrom(derived_type); derived_type.IsSubClassOf(base_type); |
注意,在比较同一类型时,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
如果你使用的是C 7,那么是时候更新安德鲁·黑尔的伟大答案了。模式匹配引入了一个很好的快捷方式,它在if语句的上下文中为我们提供了一个类型化变量,而不需要单独的声明/强制转换和检查:
1 2 3 4 |
对于这样一个演员组来说,这看起来并不是很吸引人,但是当你有很多可能的类型进入你的日常生活时,它确实会发光。以下是避免两次铸造的旧方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
尽可能地缩小这段代码,同时避免同一对象的重复强制转换,这一直困扰着我。上面的压缩很好,模式匹配如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | switch (obj1) { case Button button: // do stuff... break; case TextBox text: // do stuff... break; case Label label: // do stuff... break; // and so on... } |
编辑:更新了更长的新方法,以根据palac的注释使用开关。
我更喜欢
也就是说,如果您使用的是IS,那么您可能没有正确地使用继承。
假设那个人:实体,那个动物:实体。feed是实体中的一种虚拟方法(使neil高兴)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
宁愿
1 2 3 4 5 6 7 8 9 10 11 | class Person { public override void Feed( Person p ) { // feed the person } public override void Feed( Animal a ) { // feed the animal } } |
我相信最后一个研究的也是遗传(例如狗是动物==true),这在大多数情况下是更好的。
这取决于我在做什么。如果我需要一个bool值(例如,要确定是否要强制转换为int),我将使用
用于获取类型的System.Type对象。类型表达式采用以下形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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 */ |
此示例使用GetType方法确定用于包含数值计算结果的类型。这取决于结果编号的存储要求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 */ |
最后一个更清晰、更明显,还检查子类型。其他的则不检查多态性。
1 |
性能测试typeof()与gettype():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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(); } } |
调试模式下的结果:
1 2 | 00:00:08.4096636 00:00:10.8570657 |
释放模式下的结果:
1 2 | 00:00:02.3799048 00:00:07.1797128 |
您可以在C中使用"typeof()"运算符,但需要使用System.io调用命名空间;如果要检查类型,则必须使用"is"关键字。