我见过很多人使用以下代码:
Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
但我知道你也可以这样做:
if (obj1.GetType() == typeof(int))
// Some code here
或这个:
if (obj1 is int)
// Some code here
就个人而言,我觉得最后一个是最干净的,但是我缺少什么吗?哪个最好用,还是个人喜好?
as
!
as
并不是真正的类型检查......
as
肯定是一种类型检查,与 is
一样多!它在幕后有效地使用了 is
,并且在 MSDN 中的所有地方都在使用它与 is
相比它提高了代码清洁度的地方。不是首先检查 is
,而是调用 as
建立一个可供使用的类型化变量:如果它为空,则适当响应;否则,继续。当然,我已经看到并使用了很多东西。
都是不同的。
typeof 采用类型名称(您在编译时指定)。
GetType 获取实例的运行时类型。
如果实例在继承树中,则返回 true。
例子
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);
那么 typeof(T) 呢?它是否也在编译时解决?
是的。 T 始终是表达式的类型。请记住,泛型方法基本上是一堆具有适当类型的方法。例子:
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"
如果您想在编译时获取类型,请使用 typeof
。如果您想在执行时获取类型,请使用 GetType
。很少有使用 is
的情况,因为它会进行强制转换,并且在大多数情况下,您最终还是会强制转换变量。
您还没有考虑第四个选项(特别是如果您要将对象也转换为您找到的类型);即使用as
。
Foo foo = obj as Foo;
if (foo != null)
// your code here
这仅使用一个演员,而这种方法:
if (obj is Foo) Foo foo = (Foo)obj;
需要两个。
更新(2020 年 1 月):
从 C# 7+ 开始,您现在可以进行内联转换,因此“is”方法现在也可以在一次转换中完成。
例子:
if(obj is Foo newLocalFoo)
{
// For example, you can now reference 'newLocalFoo' in this local scope
Console.WriteLine(newLocalFoo);
}
is
是否仍然执行强制转换?
typeof()
,而且这个答案并不建议您可以。您改为传入类型,即 typeof(string)
有效,typeof("foo")
无效。
is
在 IL 中执行这样的转换,而是执行特殊操作。
if (obj is Foo foo) { /* use foo here */ }
1.
Type t = typeof(obj1);
if (t == typeof(int))
这是非法的,因为 typeof
仅适用于类型,不适用于变量。我假设 obj1 是一个变量。因此,以这种方式 typeof
是静态的,并且在编译时而不是运行时完成其工作。
2.
if (obj1.GetType() == typeof(int))
如果 obj1
恰好属于 int
类型,则为 true
。如果 obj1
派生自 int
,则 if 条件将为 false
。
3.
if (obj1 is int)
如果 obj1
是 int
,或者如果它派生自名为 int
的类,或者如果它实现名为 int
的接口,则这是 true
。
Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
这是一个错误。 C# 中的 typeof 运算符只能采用类型名称,不能采用对象。
if (obj1.GetType() == typeof(int))
// Some code here
这会起作用,但可能不会像您期望的那样。对于值类型,正如您在此处显示的那样,它是可以接受的,但对于引用类型,它只会在类型完全相同的情况下返回 true,而不是继承层次结构中的其他类型。例如:
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");
}
这将打印 "o is something else"
,因为 o
的类型是 Dog
,而不是 Animal
。但是,如果您使用 Type
类的 IsAssignableFrom
方法,您可以完成这项工作。
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
但是,这种技术仍然存在一个主要问题。如果您的变量为 null,则对 GetType()
的调用将引发 NullReferenceException。因此,要使其正常工作,您可以:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
这样,您就有了 is
关键字的等效行为。因此,如果这是您想要的行为,您应该使用 is
关键字,它更具可读性和效率。
if(o is Animal)
Console.WriteLine("o is an animal");
但是,在大多数情况下,is
关键字仍然不是您真正想要的,因为仅仅知道对象属于某种类型通常是不够的。通常,您希望实际使用该对象作为该类型的实例,这也需要强制转换它。所以你可能会发现自己在编写这样的代码:
if(o is Animal)
((Animal)o).Speak();
但这会使 CLR 最多检查对象的类型两次。它将检查一次以满足 is
运算符,如果 o
确实是 Animal
,我们让它再次检查以验证强制转换。
这样做更有效:
Animal a = o as Animal;
if(a != null)
a.Speak();
as
运算符是一个强制转换,如果失败则不会引发异常,而是返回 null
。这样,CLR 只检查一次对象的类型,之后我们只需要进行一次空值检查,这样效率更高。
但请注意:很多人会陷入 as
的陷阱。因为它不会抛出异常,所以有些人认为它是一个“安全”的强制转换,他们只使用它,避开常规强制转换。这会导致如下错误:
(o as Animal).Speak();
在这种情况下,开发人员清楚地假设 o
将始终是 Animal
,只要他们的假设是正确的,一切正常。但如果他们错了,那么他们最终得到的是 NullReferenceException
。如果使用常规演员表,他们会得到一个 InvalidCastException
,这样可以更正确地识别问题。
有时,这个错误很难找到:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
这是另一种情况,开发人员显然希望 o
每次都是 Animal
,但这在使用 as
强制转换的构造函数中并不明显。在您使用 Interact
方法之前,这并不明显,其中 animal
字段预计会被积极分配。在这种情况下,您不仅最终会得到一个误导性的异常,而且直到可能比实际错误发生的时间晚得多时才会抛出它。
总之:
如果您只需要知道对象是否属于某种类型,请使用 is。
如果您需要将对象视为某种类型的实例,但您不确定该对象是否属于该类型,请使用 as 并检查 null。
如果您需要将对象视为某种类型的实例,并且该对象应该属于该类型,请使用常规强制转换。
o is Animal
,需要 CLR 检查变量 o
的类型是否为 Animal
。第二次检查是在语句 ((Animal)o).Speak()
中强制转换时。与其检查两次,不如使用 as
检查一次。
如果您使用的是 C# 7,那么是时候更新 Andrew Hare 的出色答案了。 Pattern matching 引入了一个很好的快捷方式,它在 if 语句的上下文中为我们提供了一个类型化变量,而无需单独的声明/强制转换和检查:
if (obj1 is int integerValue)
{
integerValue++;
}
对于像这样的单个演员来说,这看起来相当平淡无奇,但当你有许多可能的类型进入你的日常工作时,它真的会发光。以下是避免两次投射的旧方法:
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
尽可能地缩小这段代码,以及避免对同一对象的重复转换一直困扰着我。以上内容通过模式匹配很好地压缩为以下内容:
switch (obj1)
{
case Button button:
// do stuff...
break;
case TextBox text:
// do stuff...
break;
case Label label:
// do stuff...
break;
// and so on...
}
编辑:根据 Palec 的评论更新了更长的新方法以使用开关。
switch
statement with pattern matching。
if (obj1 is int integerValue) { integerValue++; }
_
) 的 switch/case 模式匹配。例如case Button _:
。效果很好,比一个大的 if 语句噪音小得多。
我有一个要比较的 Type
属性,但不能使用 is
(如 my_type is _BaseTypetoLookFor
),但我可以使用这些:
base_type.IsInstanceOfType(derived_object);
base_type.IsAssignableFrom(derived_type);
derived_type.IsSubClassOf(base_type);
请注意,当比较相同类型时,IsInstanceOfType
和 IsAssignableFrom
返回 true
,其中 IsSubClassOf 将返回 false
。 IsSubclassOf
不适用于其他两个接口的接口。 (另见this question and answer。)
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
我更喜欢的是
也就是说,如果您使用的是,您可能没有正确使用继承。
假设 Person : Entity 和 Animal : Entity。 Feed 是 Entity 中的一个虚拟方法(让 Neil 开心)
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
}
}
}
相当
class Person
{
public override void Feed( Person p )
{
// feed the person
}
public override void Feed( Animal a )
{
// feed the animal
}
}
我相信最后一个也关注继承(例如 Dog is Animal == true),这在大多数情况下更好。
这取决于我在做什么。如果我需要一个 bool 值(例如,确定是否要转换为 int),我将使用 is
。如果出于某种原因我确实需要该类型(例如,传递给其他方法),我将使用 GetType()
。
最后一个更清晰,更明显,并且还检查子类型。其他的不检查多态性。
用于获取类型的 System.Type 对象。 typeof 表达式采用以下形式:
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 方法来确定用于包含数值计算结果的类型。这取决于结果数字的存储要求。
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
*/
if (c is UserControl) c.Enabled = enable;
您可以在 C# 中使用“typeof()”运算符,但需要使用 System.IO 调用命名空间;如果要检查类型,则必须使用“is”关键字。
typeof
没有在命名空间中定义,它是一个关键字。 System.IO
与此无关。
性能测试 typeof() 与 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();
}
}
调试模式下的结果:
00:00:08.4096636
00:00:10.8570657
发布模式的结果:
00:00:02.3799048
00:00:07.1797128
DateTime
,因为它使用操作系统的时间范围。与使用处理器的 Tick
的 Stopwatch
相比,Win7 中 DateTime
使用的分辨率高达 15 毫秒。
不定期副业成功案例分享
new Dog().GetType() is Animal
返回 false(以及您的其他版本),因为.GetType()
返回类型为Type
的对象,并且Type
不是Animal
。is
时,如果结果(true
或false
)在编译时已知,您会收到编译时警告。这意味着您应该更改代码!示例 1:void M(Dog d) { var test = d is System.Exception; }
在编译时可以看出,空引用或Dog
的实例永远不可能是System.Exception
的实例,因为涉及的类型是class
类型,每个class
可以有只有一个直接基类。示例 2:void M(int i) { var test = i is IConvertible; }
在编译时可以看出这始终为真(i
永远不会为空)。