前言:《Essential.NET》这本书是.NET中的经典之作,个人感觉和《Thinking in C++》有的一拼,在我床头除了《设计模式》就只有它了:)。正在学习其方法一节,发现方法(Method)才是本书的重点,全书花了不少纸在写这一块。在下记录一些个人的学习心得,一方面和大家分享,另一方面也请大家指点本人在理解上的错误和不足。
按书上介绍的顺序来。
using
System;
using
System.Collections;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
public
class
MyClass
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
public static void Main()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
ReallyDerived r1 = new ReallyDerived();
Derived r2 = r1;
Base r3 = r1;
ICommon r4 = r1;
r1.DoIt();
r2.DoIt();
r3.DoIt();
r4.DoIt();
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
RL();
}
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
Helper methods#region Helper methods
private static void WL(object text, params object[] args)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.WriteLine (text.ToString(), args);
}
private static void RL()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
Console.ReadLine ();
}
private static void Break()
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
System.Diagnostics.Debugger.Break();
}
#endregion
}
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public
interface
ICommon
{
void DoIt();
}
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public
class
Base : ICommon
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
void ICommon.DoIt()
{ System.Console.WriteLine("Base : void ICommon.DoIt()"); }
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
public virtual void DoIt()
{ System.Console.WriteLine("Base : public virtual void DoIt()"); }
}
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public
class
Derived : Base, ICommon
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
void ICommon.DoIt()
{ System.Console.WriteLine("Derived: void ICommon.DoIt()");}
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
public new virtual void DoIt()
{ System.Console.WriteLine("Derived: public new virtual void DoIt()"); }
}
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public
class
ReallyDerived : Derived
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
public override void DoIt()
{ System.Console.WriteLine("ReallyDerived: public override void DoIt()");}
}
按书上介绍的顺序来。
首先讲到
JIT
的问题。值得引用的一段:“CLR只执行本机代码。如果一个方法体由CIL组成,那么它就必须在调用之前被转换为本机代码。”这儿转换方法有两种,一是当组件被首次部署在目标机器上时,通过预编译生成本机映像;另一个就是JIT了。在此作者推荐的方法是JIT,理由:
1、本机映像的代码尺寸过大(有多大?不太清楚),如果业务逻辑只使用组件的一到两个方法,岂不浪费。
2、跨组件的约定,个人理解是如果有代码涉及到COM之类的组件,本机映像对其的“链接”是静态的,非本机映像的组件如果有所变更那本机映像就会出问题。
1、本机映像的代码尺寸过大(有多大?不太清楚),如果业务逻辑只使用组件的一到两个方法,岂不浪费。
2、跨组件的约定,个人理解是如果有代码涉及到COM之类的组件,本机映像对其的“链接”是静态的,非本机映像的组件如果有所变更那本机映像就会出问题。
其次,关于方法表。记的以前看C++的书籍时也听过C++中方法表的介绍,.NET中也有方法表。在.NET中每个类型都会有一个CORINFO_CLASS_STRUCT的数据结构,有Equals、GetHashCode之类的方法,也有用户自定义的方法在里边。这个结构为方法提供了入口点。而默认的,每个入口点都指向了一个stub routine(书上是叫“存根例程”),即是对JIT编译器进行调用。所以在JIT进行编译之前我们可以知道每一个方法调用的不是某段代码,而是JIT,具体代码的位置要等JIT来给定。在JIT之前我们看到stub routine中的数据应该都是call xxxxxxxx这样的东东。等JIT生成本机代码后,stub routine中的内容会变为jmp XXXXXXXX,跳转到要执行的代码。我想这样带来的好处是,如果某方法不再被使用(unreachable in GC),那JIT可以忽略它以减少系统开销;或是对方法表进行优化,让常用的方法被放在同一个内存页面中。
第三,虚方法
以上我们看到的都是一般的方法,虚方法与其有所区别。我们知道如果一个方法是虚方法,那么它有可能会被子类重写,所以如果按上边我们介绍的jmp到某地址在父类中是不行的。(写到这儿肚子饿了,标记一下。)其实CLR是把虚方法和一般方法分开来放的,两种方法的入口项都不一样,方便子类对虚方法进行索引。虚方法的入口操作码不是上边我们看到的call,而是callvirt。虚方法具体的方法表是依靠目标对象的RuntimeTypeHandle来决定。参考下列代码:
Mov ecx, esi
Mov eax, dword ptr [ecx]
Call dword ptr [eax + 38h]
对此书上有代码的详细介绍。其中第二个mov是对第三句的call进行准备的,call的时候是利用句柄+offset来进行虚方法的寻址,offset在类型的生命周期中是静态的,这也是虚方法的一大特色。书上同时提到了一些元数据特性,比较强调的是newslot,代码中与其对应的就是用new关键字声明的方法。从语法上来看是用来禁用(或是说屏蔽)父类的方法,消除编译警告,而对底层来说它的作用是重新分配虚方法表的索引。想想其性能如何,各位不妨写代码看看?
虚方法还有其他用处,比如声明成sealed之类的。
第四、接口
.NET有自己的接口偏移表。CLR在初始化每个接口类型时(每一个接口类型,那范围多大?Modules?Assembly?AppDomain?这是本人的一个疑问。)都会在这个偏移表中有所对应。而对于具体某个类来说,又有自己的一个接口偏移表。同时要注意的是接口表中的方法在CORINFO_CLASS_STRUCT中的被连续地存放的。对于基于接口的方法的调用,系统是先找到所属接口,在找到后再确定方法的offset,然后进行调用,也有提到对性能有所影响。不过也有说到有相同的对象引用如果使用频繁,则优化就是可能的,个人猜想是将常用的接口尽量安排在一个连续的区域。
对于接口的使用:
一般而言我们认为接口中的方法都可以是public的,因为接口暴露出方法如void f()。然后我们的某个类可以实现这个接口,比如class a: ISomeThing这样,再在类代码中写类似public void f()这样的代码,这样我们在new这个方法后就可以使用这个f()了。但.NET还有另一种方式,不知道是不是为面向接口编程专门留出来的,即在类代码中声明f()的形式是void ISomeThing.f(){……}这样。这样的代码通过类是不能访问到是,也就是说f()对于该类而言是private,但对接口ISomeThing却是public的,这样我们可以通过接口访问而不是类。书中也提出一种可能的情况,即一个类可以实现N个接口,但多个接口之间可能出现方法名冲突,这时不直接使用类来执行接口方法而是用接口来执行,会方便很多。除了Interface.MethodName的东东以外,还有一些其它内容,本人根据书中代码用Snippet Compiler
写出一小段代码以示区别:
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0be121fa5b8988fbabbbc526af3b0fc0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/eec4c0236afc26744c9c4e910bc34958.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0be121fa5b8988fbabbbc526af3b0fc0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0be121fa5b8988fbabbbc526af3b0fc0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0be121fa5b8988fbabbbc526af3b0fc0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0be121fa5b8988fbabbbc526af3b0fc0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f2671b7f42ce505d9bf55a7a0ca257fb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
运行出来情况如图:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/vipxiaotian/method_1.jpg)
对此本人有一个疑问:为什么结果的第二行和第一行一样?请高手解答。
第五、显式方法调用
书中没有对显式方法的调用作过于精细的底层分析,主要讲了MethodInfo中Invoke方法的两个原型。大部分的内容我们可以参考书的第一节Reflector。