zoukankan      html  css  js  c++  java
  • 有趣的重写GetType()方法


    最近和同事们聚在一起的时候聊起了.net面试题。有一个面试题是问GetType()能否被重载。答案是否定的。原因是GetType()不是Virtual的,所以是不能重载的。不让重载GetType()就是为了保证类型安全。我的一个同事突发奇想,说能否用new来重写GetType()方法。如下面的代码:
     

    namespace test
    {
    public class Employee
    {
    public new Type GetType()
    {
    return typeof(string);
    }
    }

    public class Program
    {
    public static void Main()
    {
    Employee emp
    = new Employee();
    Console.WriteLine(
    "hello {0}", emp.GetType().Name);
    //Object obj = emp;
    //Console.WriteLine("obj is System.String = {0}", obj is System.String);
    //Console.WriteLine("obj is test.Employee = {0}", obj is test.Employee);
    Console.ReadKey();
    }
    }
    }

    运行此程序,它输出:

    hello String

    啊!难道我们可以随便改一个类的类型?仔细想一想,这只是一个障眼法。让我们来验证一下吧。稍微把代码改一改,去掉这几行前的注释:

                Object obj = emp;
                Console.WriteLine("obj is System.String = {0}", obj is System.String);
                Console.WriteLine("obj is test.Employee = {0}", obj is test.Employee);
    再运行, 输出就是:

    hello String

    obj is System.String = false

    obj is test.Employee = true

    这说明了什么?说明了emp的类型在CLR看来它还是Employee类型,并不会被你新写的GetType()方法改变。这是为什么呢?我们来看看Main函数的IL代码:

     

     

    .method public hidebysig static void Main() cil managed
    {
    .entrypoint
    .maxstack 3
    .locals init (
    [
    0] class test.Employee emp,
    [
    1] object obj)
    L_0000: nop
    L_0001: newobj instance void test.Employee::.ctor()
    L_0006: stloc.0
    L_0007: ldloc.0
    L_0008: stloc.1
    L_0009: ldstr "hello {0}"
    L_000e: ldloc.0
    L_000f: callvirt instance class [mscorlib]System.Type test.Employee::GetType()
    L_0014: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name()
    L_0019: call void [mscorlib]System.Console::WriteLine(string, object)
    L_001e: nop
    L_001f: ldstr "obj is System.String = {0}"
    L_0024: ldloc.1
    L_0025: isinst string
    L_002a: ldnull
    L_002b: cgt.un
    L_002d: box bool
    L_0032: call void [mscorlib]System.Console::WriteLine(string, object)
    L_0037: nop
    L_0038: ldstr "obj is test.Employee = {0}"
    L_003d: ldloc.1
    L_003e: isinst test.Employee
    L_0043: ldnull
    L_0044: cgt.un
    L_0046: box bool
    L_004b: call void [mscorlib]System.Console::WriteLine(string, object)
    L_0050: nop
    L_0051: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
    L_0056: pop
    L_0057: ret
    }

    注意L_000f这一行: 编译器生成的IL代码是callvirt指令。callvirt指令能正确地发现基类函数的虚方法和子类的重载方法,并正确地调用合适的方法。在这里被调用的函数是Employee类的GetType()方法。所以后来输出hello String。

    另外请注意L_0025这行: isinst string 这是判断栈中当前对象是否string类型。

    还有L_003e: isinst test.Employee,这是判断栈中当前对象是否test.Employee类型。
    这两条指令之前都是这个指令ldloc.1,指的都是将局部变量obj放入栈,所以obj就是isinst的栈中当前对象。

    那为什么我们自己写的新GetType()方法没有将对象原来的GetType()方法覆盖掉呢?

    这是因为每个方法都有自己的方法签名。对象原来的GetType()方法在metadata中的method 表中,而且有一个与众不同的方法签名。我们自己写的新GetType()方法也是在metadata中的method 表中,也有一个方法签名,这个方法签名唯一标识每一个方法。CLR在查看一个对象的类型时,是固定用对象原来的GetType()方法签名去找GetType()方法,这样就不会调用错误的方法。我们自己写的新GetType()方法在检查类型的时候是不会被调用的。我们自己写的新GetType()方法只有在callvirt指令中才能调用到。

  • 相关阅读:
    嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)
    嵌入式Linux驱动开发之helloword心得
    PJMEDIA之录音器的使用(capture sound to avi file)
    PJMEID学习之视频的捕捉与播放
    PJSIP-PJMEDIA【使用pjmedia 播放wav格式的音乐】
    PJSIP-PJLIB-Socket
    PJSIP-PJLIB(samples) (the usage of the pjlib lib) (eg:string/I/O)
    Start with PJSIP on windows
    Android实际开发之网络请求组件的封装(OkHttp为核心)
    Android实际开发中的首页框架搭建(二、首页框架实现)
  • 原文地址:https://www.cnblogs.com/mikelij/p/1779893.html
Copyright © 2011-2022 走看看