wiki.php 用Markdown写wiki是一种什么样的体验?

如何确定一个对象的类型.md

最后更新于 2019-10-06 15:00:29

一篇旧文章,保存留档。

如何在运行时分析一个对象的类型呢?你也许会立刻想到 GetType 方法。没错!这个方法可以很方便地获取到一个对象的运行时类型(Type),但如何与类型模板(Class)进行比较呢?
直接利用 Type = Class 或者 Type Is Class 是不可以的,我们必须要首先获得这个类型模板的运行时类型,可以利用GetType关键字来获取。注意:GetType 在VB中是一个关键字,它的使用方法如:Dim t As Type = GetType(Integer)

假设已有一个Object对象sender,我们来分析它是否是一个Button类型。

方法1:

If sender.GetType Is GetType(Button) Then
MsgBox("对象是Button类型!")
End If

方法2:

If sender.GetType.Equals(GetType(Button)) Then
MsgBox("对象是Button类型!")
End If

方法3:

If GetType(Button).Equals(sender.GetType) Then
MsgBox("对象是Button类型!")
End If

上面的方法都能很好的工作,但是这个比较过程都比较复杂。
方法1实现获取对象的运行时类型,然后获取类型模板的运行时类型,最后再对两个运行时类型进行比较。
方法2和方法3雷同,都是获取两个运行时类型,然后再对另一个类型进行基础比较。

方法2的IL代码如下:

IL_0001: ldarg.1
IL_0002: callvirt   instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
IL_0007: ldtoken    [System.Windows.Forms]System.Windows.Forms.Button
IL_000c: call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0011: callvirt   instance bool [mscorlib]System.Type::Equals(class [mscorlib]System.Type)
IL_0016: stloc.0

首先从参数中加载参数sender,然后调用其GetType方法,接着使用ldtoken命令将Class转换为Type(实际上是RuntimeTypeHandle对象),最后执行比较。基本上,上面方法的代码在运行过程中的效率并不高。

我们再来看最后一个解决方法:

If TypeOf sender Is Button Then
MsgBox("对象是Button类型!")
End

先来认识一下 TypeOf 关键字,它在C#中如同VB中GetType关键字一样,是获取模板类型的运行时类型的关键字,但在VB中却拥有完全不同的功能,就是检测对象类型的功能!TypeOf 返回的是一个Boolean 类型的值,用以检测一个对象是否是目标类型,它在执行过程中完全不进行类型转换,我们来看看它生成的IL代码:

IL_0001: ldarg.1
IL_0002: isinst     [System.Windows.Forms]System.Windows.Forms.Button
IL_0007: ldnull
IL_0008: cgt.un
IL_000a: stloc.0

实际上上面的IL代码甚至可以直接省略为:

ldarg.1
isinst [System.Windows.Forms]System.Windows.Forms.Button]
stloc.0

由此可见,TypeOf 对应的是 isinst 命令,它的效率明显要比上面的 GetType 要高。
因此,在进行类型比较分析时,使用 TypeOf 要比 GetType 要更好。