当前位置: 首页 > 编程日记 > 正文

艾伟_转载:探索.Net中的委托

废话

我本来以为委托很简单,本来只想简简单单的说说委托背后的东西,委托的使用方法。原本只想解释一下那句:委托是面向对象的、类型安全的函数指针。可没想到最后惹出一堆的事情来,越惹越多,罪过,罪过。本文后面一部分是我在一边用SOS探索一边记录的,写的非常糟糕,希望您的慧眼能发现一些有价值的东西,那我就感到无比的荣幸了。

委托前世与今生

大家可能还记得,在C/C++里,我们可以在一个函数里实现一个算法的骨架,然后在这个函数的参数里放一个“钩子”,使用的时候,利用这个“钩子”注入一个函数,注入的函数实现不同算法的不同部分,这样就可以达到算法骨架重用的目的。而这里所谓的“钩子”就是“函数指针”。这个功能很强大啊,但是函数指针却有它的劣势:不是类型安全的、只能“钩”一个函数。大家可能都知道微软对委托的描述:委托是一种面向对象的,类型安全的,可以多播的函数指针。要理解这句话,我们先来看看用C#的关键字delegate声明的一个委托到底是什么样的东西:

   1: namespace Yuyijq.DotNet.Chapter2
   2: {
   3:     public delegate void MyDelegate(int para);
   4: }

隐藏在背后的秘密

很简单的代码吧,使用ILDasm反编译一下:

wps_clip_image-0

奇怪的是,这么简单的一行代码,变成了一个类:类名与委托名一致,这个类继承自System.MulticastDelegate类,连构造器一起有四个成员。看看我们如何使用这个委托:

   1: public class TestDelegate
   2: {
   3:     MyDelegate myDelegate;
   4:  
   5:     public void AssignDelegate()
   6:     {
   7:         this.myDelegate = new MyDelegate(Test);
   8:     }
   9:  
  10:     public void Test(int para)
  11:     {
  12:         Console.WriteLine("Test Delegate");
  13:     }
  14: }

编译后用ILDasm看看结果:

.field private class Yuyijq.DotNet.Chapter2.MyDelegate myDelegate

发现,.Net把委托就当做一个类型,与其他类型一样对待,现在你明白了上面那句话中说委托是面向对象的函数指针的意思了吧。

接着看看AssignDelegate反编译后的代码:

   1: .method public hidebysig instance void  AssignDelegate() cil managed
   2: {
   3:   // Code size       19 (0x13)
   4:   .maxstack  8
   5: //将方法的第一个参数push到IL的运算栈上(对于一个实例方法来说,比如AssignDelegate,它的第一个参数就是“this”了)
   6:   IL_0000:  ldarg.0
   7: //这里又把this压栈了一次,因为下面一条指令中的Test方法是一个实例方法,需要一个this
   8:   IL_0001:  ldarg.0
   9: //ldftn就是把实现它的参数中的方法的本机代码的非托管指针push到栈上,在这里你就可以认为是获取实例方法Test的地址
  10:   IL_0002:  ldftn instance void Yuyijq.DotNet.Chapter2.TestDelegate::Test(int32)
  11: //调用委托的构造器,这个构造器需要两个参数,一个对象引用,就是第一次压栈的this,一个方法的地址。
  12:   IL_0008:  newobj instance void Yuyijq.DotNet.Chapter2.MyDelegate::.ctor(object,native int)
  13:   IL_000d:  stfld class Yuyijq.DotNet.Chapter2.MyDelegate Yuyijq.DotNet.Chapter2.TestDelegate::myDelegate
  14:   IL_0012:  ret
  15: }

通过上面的代码,我们会发现,将一个实例方法分配给委托时,委托不仅仅引用了方法的地址,还有这个方法所在对象的引用,这里就是所谓的类型安全。

我们再回过头来看看MyDelegate的继承链:MyDelegate->MulticastDelegate->Delegate。

奇妙的地方

而Delegate中有三个有趣的字段:

Internal object _target;

Internal IntPtr _methodPtr;

Internal IntPtr _methodPtr;

对这三个字段做详细说明

_target

1、如果委托指向的方法是实例方法,则_target的值是指向目标方法所在对象的指针

2、如果委托指向的是静态方法,则_target的值是委托实例自身

_methodPtr

1、如果委托指向的方法是实例方法,则_methodPtr的值指向一个JIT Stub(如果这个方法还没有被JIT编译,关于JIT Stub会在后面的章节介绍),或指向该方法JIT后的地址

2、如果委托指向的方法是静态方法,则_methodPtr指向的是一个Stub(一段小代码,这段代码的作用是_target,然后调用_methodPtrAux指向的方法),而且所有签名相同的委托,共享这个Stub。为什么要这样一个Stub?我想是为了让通过委托调用方法的流程一致吧,不管指向的是实例方法还是静态方法,对于外部来说,只需要调用_methodPtr指向的地址,但是对于调用实例方法而言,它需要this,也就是这里的_target,而静态方法不需要,为了让这里的过程一直,CLR会偷偷的在委托指向静态方法时插入一小段代码,用于去掉_target,而直接jmp到_methodPtrAux指向的方法。

_methodPtrAux

1、如果委托指向的是实例方法,则_methodPtrAux就是0。

2、如果委托指向的是静态方法,则这时_methodPtrAux起的作用与_mthodPtr在委托指向实例方法的时候是一样的。

实际上通过反编译Delegate的代码发现,Delegate有一个只读属性Target,该Target的实现依靠GetTarget方法,该方法的代码如下:

   1: internal virtual object GetTarget()
   2: {
   3:     if (!this._methodPtrAux.IsNull())
   4:     {
   5:         return null;
   6:     }
   7:     return this._target;
   8: }

实了当委托指向静态方法时,Target属性为null。

我们来自己动手,分析一下上面的结论是否正确。

_target和_methodPtr真的如上面所说的么?何不自己动手看看。

建立一个Console类型的工程,在项目属性的“调试(Debug)”选项卡里选中“允许非托管代码调试(Enable unmanaged code debuging)”。

   1: namespace Yuyijq.DotNet.Chapter2
   2: {
   3:     public delegate void MyDelegate(int para);
   4:     public class TestDelegate
   5:     {
   6:         public void Test(int para)
   7:         {
   8:             Console.WriteLine("Test Delegate");
   9:         }
  10:         public void CallByDelegate()
  11:         {
  12:             MyDelegate myDelegate = new MyDelegate(this.Test);
  13:             myDelegate(5);
  14:         }
  15:  
  16:         static void Main()
  17:         {
  18:             TestDelegate test = new TestDelegate();
  19:             test.CallByDelegate();
  20:         }
  21:     }
  22: }

上面是作为实验的代码。

在CallByDelegate方法的第二行设置断点

F5执行,命中断电后,在Visual Studio的立即窗口(Immediate Window)里输入如下命令(菜单栏->调试(Debug)->立即窗口(Immediate)):

//.load sos.dll用于加载SOS.dll扩展

.load sos.dll

extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

//Dump Stack Objects的缩写,输出栈中的所有对象

//该命令的输出有三列,第二列Object就是该对象在内存中的地址

!dso

PDB symbol for mscorwks.dll not loaded

OS Thread Id: 0x1588 (5512)

ESP/REG Object Name

0037ec10 019928a4 Yuyijq.DotNet.Chapter2.TestDelegate

0037ed50 019928a4 Yuyijq.DotNet.Chapter2.TestDelegate

0037ed5c 019928b0 Yuyijq.DotNet.Chapter2.MyDelegate

0037ed60 019928b0 Yuyijq.DotNet.Chapter2.MyDelegate

0037ef94 019928b0 Yuyijq.DotNet.Chapter2.MyDelegate

0037ef98 019928b0 Yuyijq.DotNet.Chapter2.MyDelegate

0037ef9c 019928a4 Yuyijq.DotNet.Chapter2.TestDelegate

0037efe0 019928a4 Yuyijq.DotNet.Chapter2.TestDelegate

0037efe4 019928a4 Yuyijq.DotNet.Chapter2.TestDelegate

//do命令为Dump Objects缩写,参数为对象地址,输出该对象的一些信息

!do 019928b0

Name: Yuyijq.DotNet.Chapter2.MyDelegate

MethodTable: 00263100

EEClass: 002617e8

Size: 32(0x20) bytes

(E:\Study\Demo\Demo\bin\Debug\Demo.exe)

//该对象的一些字段

Fields:

MT Field Offset Type VT Attr Value Name

704b84dc 40000ff 4 System.Object 0 instance 019928a4 _target

704bd0ac 4000100 8 ...ection.MethodBase 0 instance 00000000 _methodBase

704bb188 4000101 c System.IntPtr 1 instance 0026C018 _methodPtr

704bb188 4000102 10 System.IntPtr 1 instance 00000000 _methodPtrAux

704b84dc 400010c 14 System.Object 0 instance 00000000 _invocationList

704bb188 400010d 18 System.IntPtr 1 instance 00000000 _invocationCount

在最后Fields一部分,我们看到了_target喝_methodPtr,_target的值为019928a4,看看上面!dso命令的输出,这个不就是Yuyijq.DotNet.Chapter2.TestDelegate实例的内存地址么。

在上面的!do命令的输出中,我们看到了MethodTable:00263100,这就是该对象的方法表地址(关于方法表更详细的讨论会在后面的章节介绍到,现在你只要把他看做一个记录对象所有方法的列表就行了,该列表里每一个条目就是一个方法)。现在我们要看看Yuyijq.DotNet.Chapter2.TestDelegate..Test方法的内存地址,看起是否与_methodPtr的值是一致的,那么首先就要获得Yuyijq.DotNet.Chapter2.TestDelegate.的实例中MethodTable的值:

!do 019928a4

Name: Yuyijq.DotNet.Chapter2.TestDelegate

MethodTable: 00263048

EEClass: 002612f8

Size: 12(0xc) bytes

(E:\Study\Demo\Demo\bin\Debug\Demo.exe)

Fields:

None

现在知道了其方法表的值为00263048,然后使用下面的命令找到Yuyijq.DotNet.Chapter2.TestDelegate..Test方法的地址:

!dumpmt -md 00263048

EEClass: 002612f8

Module: 00262c5c

Name: Yuyijq.DotNet.Chapter2.TestDelegate

mdToken: 02000003 (E:\Study\Demo\Demo\bin\Debug\Demo.exe)

BaseSize: 0xc

ComponentSize: 0x0

Number of IFaces in IFaceMap: 0

Slots in VTable: 9

--------------------------------------

MethodDesc Table

Entry MethodDesc JIT Name

.......

0026c010 00262ffc NONE Yuyijq.DotNet.Chapter2.TestDelegate.AssignDelegate()

0026c018 0026300c NONE Yuyijq.DotNet.Chapter2.TestDelegate.Test(Int32)

......

Entry这一列就是一个JIT Stub。看看,果然与_methodPtr的是一致的,因为这时Test方法还没有经过JIT(JIT列为NONE),所以_methodPtr指向的是这里的JIT Stub。

如果给委托绑定一个静态方法呢?现在我们把Test方法改为静态的,那实例化委托的时候,就不能用this.Test了,而应该用TestDelegate.Test。还是在原位置设置断点,使用与上面相同的命令,查看_target与_methodPtr的值。

MT Field Offset Type VT Attr Value Name

704b84dc 40000ff 4 System.Object 0 instance 01e928b0 _target

704bb188 4000101 c System.IntPtr 1 instance 007809C4 _methodPtr

704bb188 4000102 10 System.IntPtr 1 instance 0025C018 _methodPtrAux

你会发现这里的_target字段的值就是MyDelegate的实例myDelegate的地址。然后我们通过上面的方法,找到Test方法的地址,发现_methodPtrAux的值与该值是相同的。

实际上你还可以再编写一个与MyDelegate相同签名的委托,然后也指向一个静态方法,使用相同的方法查看该委托的_methodPtr的值,你会发现这个新委托与MyDelegate的_methodPtr的值是一致的。

刚才不是说这个时候_methodPtr指向的是一个Stub么,既然如此那我们反汇编一下代码:

!u 007809C4

Unmanaged code

007809C4 8BC1 mov eax,ecx

007809C6 8BCA mov ecx,edx

007809C8 83C010 add eax,10h

007809CB FF20 jmp dword ptr [eax]

........

.Net里JIT的方法的调用约定是Fast Call,对于Fast Call来说,方法的前两个参数会放在ECX和EDX两个寄存器中。那么mov eax,ecx实际上就是将_target传递给eax,再看看

704bb188 4000102 10 System.IntPtr 1 instance 0025C018 _methodPtrAux

_methodPtrAux的偏移是10,这里的add eax,10h就是将eax指向_methodPtrAux,然后jmp dword ptr[eax]就是跳转到_methodPtrAux所指向的地址了,就是委托指向的那个静态方法。

通过委托调用方法

如何通过委托调用方法呢:

   1: public void CallByDelegate()
   2: {
   3:    MyDelegate myDelegate = new MyDelegate(this.Test);
   4:  
   5:    myDelegate(5);
   6: }

再来看看其对应的IL代码:

   1: .method public hidebysig instance void  CallByDelegate() cil managed
   2: {
   3:   // Code size       21 (0x15)
   4:   .maxstack  3
   5:   .locals init ([0] class Yuyijq.DotNet.Chapter2.MyDelegate myDelegate)
   6:   IL_0000:  ldarg.0
   7:   IL_0001:  ldftn instance void Yuyijq.DotNet.Chapter2.TestDelegate::Test(int32)
   8:   IL_0007:  newobj instance void Yuyijq.DotNet.Chapter2.MyDelegate::.ctor(object, native int)
   9:   IL_000c:  stloc.0
  10:   IL_000d:  ldloc.0
  11:   IL_000e:  ldc.i4.5
  12:   IL_000f:  callvirt   instance void Yuyijq.DotNet.Chapter2.MyDelegate::Invoke(int32)
  13:   IL_0014:  ret
  14: }

前面的代码我们已经熟悉,最关键的就是

callvirt instance void Yuyijq.DotNet.Chapter2.MyDelegate::Invoke(int32)

我们发现,通过委托调用方法,实际上就是调用委托的Invoke方法。

多播的委托

好了,既然已经解释了面向对象和类型安全,那么说委托是多播的咋解释?

你可能已经发现,MyDelegate继承自MulticastDelegate,看这个名字貌似有点意思了。来看看下面这两行代码:

   1: MyDelegate myDelegate = new MyDelegate(this.Test);
   2: myDelegate += new MyDelegate(this.Test1);

通过IL我们可以发现,这里的+=最后就是调用System.Delegate的Combine方法。而Combine的真正实现时在MulticastDelegate的CombineImpl方法中。在MulticastDelegate中有一个_invocationList字段,从CombineImpl中可以看出这个字段是一个object[]类型的,而委托链就放在这个数组里。

后记

文章是想到哪儿写到哪儿,写的比较乱,也比较匆忙。非常抱歉。对于中间那段奇妙的事情,我原来真的不知道,我一直以为当委托指向一个静态方法时,_target指向null就完事儿了,没想到还有这么一番景象。看来很多东西还是不能想当然,亲身尝试一下才知道真实的情况。

转载于:https://www.cnblogs.com/waw/archive/2011/08/29/2157109.html

相关文章:

OO第三阶段总结

1、 规格化设计的发展 我认为规格化设计的需求主要来源于在软件与互联网行业飞速发展下,工程随着代码量的增长,往往会显得异常的臃肿,难以阅读。这为多人合作的工程创造了巨大的不便。而在这样的背景下,大家都认为代码风格的统一和…

ubuntu18.04 -- 创建第一个Django项目

step1: 安装虚拟环境: sudo pip3 install virtualenv # 安装虚拟环境sudo pip3 install virtualenvwrapper # 安装虚拟环境扩展包# 编辑家目录下的 .bashrc 文件,在最下面添加下面三行代码 export WORKON_HOME$HOME/.virtualenvs #指定…

单链表逆序生成及逆置的完整实现

单链表逆序生成及逆置的完整实现 本例中单链表数据类型定义成int型,可更改 头文件1(1.h) 宏定义及Status类型定义 头文件2(2.h) 单链表基本操作函数与逆置函数 include"1.h" using namespace std;typedef int ElemType; typedef struct LNode{ElemTyp…

html frameset

两个frame <frameset cols"25%,75%"> <frame name "frame1" src"frame_a.php" /><frame name "frame2" /></frameset> 如果在PHP中要实现在frame刷新 frame echo "<meta http-equiv\"Refre…

黄聪:Python 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)...

去空格及特殊符号 s.strip().lstrip().rstrip(,) 复制字符串 #strcpy(sStr1,sStr2) sStr1 strcpy sStr2 sStr1 sStr1 strcpy2 print sStr2 连接字符串 #strcat(sStr1,sStr2) sStr1 strcat sStr2 append sStr1 sStr2 print sStr1 查找字符 #strchr(sStr1,sStr2) # < 0 …

Java图形化界面设计——容器(JFrame)

Java图形化界面设计——容器&#xff08;JFrame&#xff09; 程序是为了方便用户使用的&#xff0c;因此实现图形化界面的程序编写是所有编程语言发展的必然趋势&#xff0c;在命令提示符下运行的程序可以让我们了解java程序的基本知识体系结构&#xff0c;现在就进入java图形化…

分库分表之后,主键的处理方法

面试题 分库分表之后&#xff0c;id 主键如何处理&#xff1f; 面试官心理分析 其实这是分库分表之后你必然要面对的一个问题&#xff0c;就是 id 咋生成&#xff1f;因为要是分成多个表之后&#xff0c;每个表都是从 1 开始累加&#xff0c;那肯定不对啊&#xff0c;需要一个全…

用队列实现形如a+b@b+a#的中心对称字符的检验

用队列实现形如abba#的中心对称字符的检验 我用网上提供的一种思想&#xff0c;用循环队列实现了这个操作&#xff0c;具体代码如下。 /*函数名match,严格来说它并不是Status型*/ Status match(char *a){SqQueue q; //定义循环队列q ch…

如何使用JPA注解标注多对多的关系

假设应用场景如下&#xff1a;Teacher与Student是多对多的关系&#xff0c;其中&#xff0c;Teacher类对应teacher表如下&#xff1a; CREATE TABLE teacher (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(50) DEFAULT NULL,PRIMARY KEY (id)) ENGINEInnoDB AUTO_INCRE…

艾伟也谈项目管理,敏捷教练的工具箱

学习并不是简简单单的阅读和浏览&#xff0c;而是一个积累的过程&#xff0c;一个通过持续的学习&#xff0c;对自己的知识体系不断丰富、索引的过程。接下来我会从四个方面入手分享我的经验。 高质量的信息源和高效的学习 Google是一个很好的工具&#xff0c;通过它&#xff…

7.Odoo产品分析 (二) – 商业板块(3) –CRM(1)

查看Odoo产品分析系列—-目录 CMR&#xff1a;Customer Relationship Management。企业为提高核心竞争力&#xff0c;利用相应的信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互&#xff0c;从而提升其管理方式&#xff0c;向客户提供创新式的个性化的客户交…

用栈实现形如a+bb+a@的中心对称字符的检验

用栈实现形如ab&ba的中心对称字符的检验 将&前字符依次入栈与前字符进行比较即可&#xff0c;下面是方法 Status match(char *a){ //match方法 SqStack s; char c; char *pa; InitStack(s); while(*p!&){ …

Typedef用法(转载)

在C的学习过程中&#xff0c;现在才发现&#xff0c;以前有那么多被忽略的重点&#xff1b;现在是慢慢拾起这些重点的时候&#xff0c;通过百度和博客&#xff0c;我感觉我学到了很多东西&#xff0c;自己只是在别人说的基础上&#xff0c;按照自己学习的过程在这里记录一下&am…

JavaScript基本知识

数组的排序 JavaScript可以实现多维数组,对象数组等排序,语法如下 arrayobj.sort(sortfunction) 参数 arrayObj 必选项。任意 Array 对象。 sortFunction 可选项。是用来确定元素顺序的函数的名称。如果这个参数被省略&#xff0c;那么元素将按照 ASCII 字符顺序进行升序排列…

七基于Fourinone实现MQ demo

2019独角兽企业重金招聘Python工程师标准>>> FourInOne也可以当成简单的mq来使用&#xff0c;该demo演示了队列和主题订阅两种模式的实现 一、队列 将domain视为mq队列&#xff0c;每个node为一个队列消息&#xff0c;检查domain的变化来获取队列消息。 Sender&…

Windows下安装XAMPP,Wordpress

配置XAMPP&#xff1a; 1、下载&#xff1a;https://www.apachefriends.org/zh_cn/download.html&#xff08;下载速度日了狗&#xff01;&#xff09; 2、安装XAMPP; 3、启动apache&#xff0c;MySQL&#xff1a; Apache启动错误&#xff1a; …

原生js实现复制

最后我的解决方案是&#xff0c;在页面中添加一个 div&#xff0c;手动写入内容innerHTML&#xff0c;然后把它隐藏掉 function copy(targetDom) {let range document.createRange();range.selectNode(hiddenErrcode);window.getSelection().removeAllRanges();window.getSele…

C#条件判断-根据条件判断要走的路-if结构

什么时候要用到if结构语句呢?如果有一个班的学生期末成绩不是很理想&#xff0c;原因是考题太难&#xff0c;教师希望根据学生平时的表现给不同学生加平时成绩分&#xff0c;条件如下&#xff1a; 如果平时每次都交作业&#xff0c;加20分&#xff1b;如果平时交了超过所有作业…

既往出现中性粒细胞减少的患者可以重新应用依那西普

原文 译文 Clin Rheumatol. 2011 Aug 5. [Epub ahead of print] Re-challenge with Etanercept in patients with Etanercept-induced Neutropenia. Haroon M, Daly M, Harney S. Source Department of Rheumatology, Cork University Hospital, Cork, Irela…

RTTI(三)相关函数1【转自大富翁】

第三部分RTTI相关函数 GetTypeData 函数 GetPropInfo 函数 FindPropInfo 函数 GetPropInfos 函数 SortPropList 函数 GetPropList 函数 GetObjectPropClass 函数 PropType / PropIsType 函数 IsPublishedProp 函数 IsStoredProp 函数 FreeAndNilProperties 函数 SetToString /…

中序非递归遍历二叉树

二叉树的递归算法虽然简单&#xff0c;但是会导致时间复杂度比较高&#xff0c;下面给大家带来用栈实现的二叉树非递归算法 首先定义好二叉树&#xff0c;和元素类型为二叉树的栈 typedef struct BiTNode{TElemType data;struct BiTNode *lchild, *rchild; }BiTNode,*BiTr…

修改属性使按钮处于无验证状态

.net 页面中如果有验证不为空的控件&#xff0c;而且有重置按钮。此时需要将重置按钮的属性设置为无验证状态 如图将CausesValidation属性设置为false转载于:https://www.cnblogs.com/xiaopanlyu/archive/2012/06/28/2568823.html

poj3253

本文地址&#xff1a;https://www.cnblogs.com/maplefighting/p/9116850.html 题目名称&#xff1a;Fence Repair 链接&#xff1a;http://poj.org/problem?id3253 题意&#xff1a;农夫准备把木板切成n块&#xff0c;每块长度为Li&#xff0c;每次切木板时需要花费切时木板的…

一起谈.NET技术,C#中int和System.Int32理解总结

最近园里的TeamOne写了一篇《[C#] int与System.Int32有什么区别》&#xff0c;发现里面有不少精彩的评论&#xff0c;所以忍不住想这篇文章总结一下: 本文的主要参考资料&#xff1a; 1.《理解C#中的System.Int32和int&#xff1a;并非鸡和鸡蛋》Author:Dixin 2.《[C#] int与Sy…

java多线程编程01---------基本概念

一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分&#xff0c;尤其是对于小白&#xff0c;次一系列文章的将会对多线程编程及其原理进行介绍&#xff0c;希望对正在多线程中碰壁的小伙伴有所帮助。 &#xff08;一&#xff09;进程、线…

Linux下查看Nginx,tomcat等的并发连接数和连接状态

1、查看Web服务器&#xff08;Nginx Apache&#xff09;的并发请求数及其TCP连接状态&#xff1a; netstat -n | awk /^tcp/ {S[$NF]} END {for(a in S) print a, S[a]}或者&#xff1a; netstat -n | awk /^tcp/ {state[$NF]} END {for(key in state) print key,"t"…

Java笔记整理-02.Java基础语法

1&#xff0c;标识符 由英文字母、数字、_(下划线)和$组成&#xff0c;长度不限。其中英文字母包含大写字母&#xff08;A&#xff5e;Z&#xff09;和小写字母&#xff08;a&#xff5e;z&#xff09;&#xff0c;数字包含0到9。 标识符的第一个字符不能是数字&#xff08;即…

android中The connection to adb is down,问题和解决 AndroidEclipseAntXML

1.报错&#xff1a;BUILD FAILEDD:\workspace\ganji\build.xml:144: The following error occurred while executing this line:D:\workspace\ganji\build.xml:271: Unable to delete file D:\workspace\ganji\tmp\proguard\tmp.jar解决&#xff1a;已经开了一个模拟器了&#…

建立可扩展的silverlight应用框架 step-4

通过外部配置文件加载模块module 在上一节中为项目引入了“Prism”框架&#xff0c;并建立了一个Hello Prism做测试。这里要把项 目好好的整理一下。使其更加的合理和具有可扩展性。 我的目的是&#xff0c;在左侧的导航栏目里点击按钮&#xff0c;相应的右侧的主体部分显示不同…

ntp时间同步服务

前言 NTP 网络时间协议用来同步网络上不同主机的系统时间。你管理的所有主机都可以和一个指定的被称为 NTP 服务器的时间服务器同步它们的时间。而另一方面&#xff0c;一个 NTP 服务器会将它的时间和任意公共 NTP 服务器&#xff0c;或者你选定的服务器同步。由 NTP 管理的所有…