有趣的重写GetType()方法

简介: 最近和同事们聚在一起的时候聊起了.net面试题。有一个面试题是问GetType()能否被重载。答案是否定的。原因是GetType()不是Virtual的,所以是不能重载的。不让重载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指令中才能调用到。

目录
相关文章
|
Web App开发
生活小技巧:Tampermonkey 脚本(刷课,刷题)
生活小技巧:Tampermonkey 脚本(刷课,刷题)
798 0
|
算法 计算机视觉
傅里叶变换
傅里叶变换
553 2
|
3天前
|
存储 人工智能 安全
AI 越智能,数据越危险?
阿里云提供AI全栈安全能力,为客户构建全链路数据保护体系,让企业敢用、能用、放心用
|
6天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
5天前
|
数据采集 人工智能 自然语言处理
3分钟采集134篇AI文章!深度解析如何通过云无影AgentBay实现25倍并发 + LlamaIndex智能推荐
结合阿里云无影 AgentBay 云端并发采集与 LlamaIndex 智能分析,3分钟高效抓取134篇 AI Agent 文章,实现 AI 推荐、智能问答与知识沉淀,打造从数据获取到价值提炼的完整闭环。
401 93
|
6天前
|
SQL 人工智能 自然语言处理
Geo优化SOP标准化:于磊老师的“人性化Geo”体系如何助力企业获客提效46%
随着生成式AI的普及,Geo优化(Generative Engine Optimization)已成为企业获客的新战场。然而,缺乏标准化流程(Geo优化sop)导致优化效果参差不齐。本文将深入探讨Geo专家于磊老师提出的“人性化Geo”优化体系,并展示Geo优化sop标准化如何帮助企业实现获客效率提升46%的惊人效果,为企业在AI时代构建稳定的流量护城河。
399 156
Geo优化SOP标准化:于磊老师的“人性化Geo”体系如何助力企业获客提效46%