风清云淡

愚钝的我将不懈追求着我的目标

导航

公告

随笔分类

随笔档案

最新评论

统计

常用链接

阅读排行榜

评论排行榜

2011年1月13日 #

委托构造原理浅析

  

   委托是一种可用于封装命名或匿名方法的引用类型。委托类似于 C++ 中的函数指针;但是,委托是类型安全和可靠的。

  本文将对委托的实现机制进行简单的探讨。下面是使用委托的一个实例:  

使用委托的一个实例
//委托的使用实例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Resources;
using System.Windows.Forms;

namespace DelegateDemo
{
class DelegateDemo
{
internal delegate void FeedBack(Int32 value); //申明委托
static void Main(string[] args)
{
StaticDelegateDemo();
InstanceDelegateDemo();
ChainDelegateDemo1(
new DelegateDemo());
ChainDelegateDemo2(
new DelegateDemo());

}
public static void StaticDelegateDemo()
{
Console.WriteLine(
"--static delegate demo:--");
Counter(
1,3,null);
Counter(
1,3,new FeedBack(DelegateDemo.FeedBackToConsole));
Counter(
1,3,new FeedBack(DelegateDemo.FeedBackToMsgBox));
Console.WriteLine();
}


private static void InstanceDelegateDemo()
{
Console.WriteLine(
"--Instance delegate demo--");
DelegateDemo del
= new DelegateDemo();

Counter(
1, 3 ,new FeedBack (del.FeedBackToFile));
Console.WriteLine();
}

public static void ChainDelegateDemo1(DelegateDemo del)
{
Console.WriteLine(
"--Chain delegate demo 1 --");
FeedBack fb1
= new FeedBack(FeedBackToConsole);
FeedBack fb2
= new FeedBack(FeedBackToMsgBox);
FeedBack fb3
= new FeedBack(del.FeedBackToFile);

FeedBack fbChain
= null;
fbChain
= (FeedBack)Delegate.Combine(fbChain, fb1);
fbChain
= (FeedBack)Delegate.Combine(fbChain, fb2);
fbChain
= (FeedBack)Delegate.Combine(fbChain, fb3);
Counter(
1, 3, fbChain);
Console.WriteLine();
fbChain
=(FeedBack)Delegate.Remove(fbChain,new FeedBack(FeedBackToMsgBox));
Counter(
1,2,fbChain);
}
public static void ChainDelegateDemo2(DelegateDemo del)
{
Console.WriteLine(
"--Chain delegate demo 2--");
FeedBack fb1
= new FeedBack(FeedBackToConsole);
FeedBack fb2
= new FeedBack(FeedBackToMsgBox);
FeedBack fb3
= new FeedBack(del.FeedBackToFile);

FeedBack fbChain
= null;
fbChain
+= FeedBackToConsole;
fbChain
+= FeedBackToMsgBox;
fbChain
+= del.FeedBackToFile;
Counter(
1, 2,fbChain);
fbChain
-= FeedBackToMsgBox;
Counter(
1, 2, fbChain);
}
private static void Counter(Int32 from,Int32 to,FeedBack fb)
{
for (Int32 val = from; val <= to;val++ )
{
if (fb != null)
fb(val);
}
}
private static void FeedBackToConsole(Int32 value)
{
Console.WriteLine(
"Items="+value);
}
private static void FeedBackToMsgBox(Int32 value)
{
MessageBox.Show(
"Items"+value);
}
private void FeedBackToFile(Int32 value)
{
StreamWriter sw
= new StreamWriter("status", true);
sw.WriteLine(
"Items" + value);
sw.Close();
}
}
}

 

  下面我们来分析下这个实例,首先是委托的声明语句: internal delegate void FeedBack(Int32 value); 

  看其声明跟方法非常类似,委托类型的声明与方法签名相似, 有一个返回值和任意数目任意类型的参数。然而,实际上编译器会像下面这样定义一个完整的类:

    internal class FeedBack:System.multicastDelegate

    {

        //构造方法

        public FeedBack();

        public virtual void Invoke(Int32 value);

        //允许回调的方法

        public virtual IAsyncResult BeginInvoke(Int32 value,AsyncCallback callback,Object object);

        public virtual void EndInvoke(IAsyncResult result);

    }

 
     此类包含一个构造函数和三个虚函数。我们可以使用VS集成平台的ILDasm.exe工具来查看上述源码所编译生成的文件,可得出下图: 

  从图中可以看到声明的委托FeedBack实际上是一个类,它是继承自 System.Delegate的。

MulticastDelegate类有三个重要的字段,当然它也同样为派生类FeedBack类所拥有,如下表所列:            

字段

类型

描述

_target

System.Object

当委托对象封装一个静态方法时,这个字段为NULL,当委托对象封装一个实例时,这个字段应用的是回调方法要操作的对象。

_methodPtr

System.IntPtr

一个内部的整数值,CLR用它标识要回调的方法

_invocationList

System.Object

构造委托链时使用,通常为NULL

  每个委托对象实际上封装了一个方法和调用该方法时要操作的一个对象,如果我们写出下面两行代码:

    FeedBack fb1 = new FeedBack(DelegateDemo.FeedBackToConsole);

    FeedBack fb2 = new FeedBack(new DelegateDemo().FeedBackToFile);

  fb1fb2会引用两个相对独立的,已经被初始化的FeedBack 委托对象,如下图所示:

 

 

 

 

  到目前为止,我们了解了委托对象的构造原理,及其内部结构,接下来我们来了解回调方法是如何调用的。

我们先引用前面代码中Counter方法的实现代码:

 private static void Counter(Int32 from,Int32 to,FeedBack fb)

        {

            for (Int32 val = from; val <= to;val++ )

            {

                if (fb != null)

                    fb(val);

            }

        }

  这段代码表面上像是调用了一个名叫fb的函数,然后向它传递一个参数(val),其实,编译器知道fb是一

个引用委托对象的实例,所以会生成代码调用该委托对象的invoke方法。即会将fb(val)解析成fb.Invoke(val),

换而言之,我们将源码中的fb(val)改成fb.Invoke(val),生成的IL应该是一样的,事实上也确实是这样。

 参考《clr via c#》

 

posted @ 2011-01-13 21:00 五行缺火 阅读(297) 评论(0) 编辑

2010年10月22日 #

VC 制作标准圆盘时钟实例


  初学VC,利用网上的一些资料,开发了一个圆盘时钟实例。以下为详细步骤:

第一步:新建一个MFC单文档的应用程序,工程名称为Clock.

第二步:在生成的CClockView类添加OnInitialUpdate()虚函数;并于函数内设置定时器;具体代码如下:

void CClockView::OnInitialUpdate()      //添加虚函数
{
 CView::OnInitialUpdate();
 
 // TODO: Add your specialized code here and/or call the base class
 SetTimer( 1, 1000, NULL );
}


第三步:设置定时器响应函数。为CClockView类添加WM_TIMER消息及OnTime()的消息响应函数,函数体代码为:

void CClockView::OnTimer(UINT nIDEvent)    //添加定时器响应消息
{
 // TODO: Add your message handler code here and/or call default

 Invalidate(TRUE);  //使窗口无效
  UpdateWindow();    //使窗口立即重绘,刷新窗口
 CView::OnTimer(nIDEvent);
}

 

第四步:在OnDraw()函数中添加如下代码:

RECT Rect; 
GetClientRect( &Rect );   //得到客户区矩形
	
// 得到圆心坐标
int nCenterX = Rect.right / 2;
int nCenterY = Rect.bottom / 2;
	
// 获取当前时间
CTime Time = CTime::GetCurrentTime();
	
CString strDigits;
int i, x, y;
CSize size;	
pWnd->GetMenuID() =ID_TYPE_ANALOG;
// 设置圆盘钟的外圈为黄色
CPen Pen( PS_SOLID, 5, RGB( 255, 255, 0 ) );		
CPen *pOldPen = pDC->SelectObject( &Pen );		
pDC->Ellipse(nCenterX+5-nCenterY,5,nCenterX+nCenterY-5,Rect.bottom - 5);	//画圆
double Radians;			
// 时间数字为红色
pDC->SetTextColor( RGB( 255, 0, 0 ) );			
for(i=1; i<=12; i++ ){
				
	// 格式化时钟对象
	strDigits.Format( "%d", i );
				
	size =pDC->GetTextExtent( strDigits,     //获得所选字体中指定字符串的高度和宽度
	strDigits.GetLength() );
				
	Radians = (double) i * 6.28 / 12.0;    //表示角度  6.28=2*PAI				
	x=nCenterX-( size.cx / 2 )+(int)((double)(nCenterY-5)*sin(Radians)  );
	y=nCenterY-( size.cy / 2 )-(int)((double)(nCenterY-5)*cos(Radians)  );		
	pDC->TextOut( x, y, strDigits );				
}
			
	// Calculate the radians for the hour hand.
Radians =(double) Time.GetHour() +
	(double) Time.GetMinute() / 60.0 +
	(double) Time.GetSecond() / 3600.0;
Radians *= 6.28 / 12.0;
			
// 时针为绿色
CPen HourPen( PS_SOLID, 5, RGB( 0, 255, 0 ) );
			
// Select the newly-created CPen object into the DC.
pDC->SelectObject( &HourPen );
			
// Move to the center of the clock, then draw the hour hand line.
pDC->MoveTo( nCenterX, nCenterY );
pDC->LineTo(
nCenterX + (int) ( (double) ( nCenterY / 3 ) *sin( Radians ) ),
nCenterY - (int) ( (double) ( nCenterY / 3 ) *cos( Radians ) ) );
// Calculate the radians for the minute hand.
Radians = (double) Time.GetMinute() +
	 (double) Time.GetSecond() / 60.0;
	Radians *= 6.28 / 60.0;
			
// 分针为蓝色
CPen MinutePen( PS_SOLID, 3, RGB( 0, 0, 255 ) );
			
// Select the newly-created CPen object into the DC.
pDC->SelectObject( &MinutePen );
// Move the to center of the clock, then draw the minute hand line.
pDC->MoveTo( nCenterX, nCenterY );
pDC->LineTo(
	nCenterX + (int) ( (double) (
	( nCenterY * 2 ) / 3 ) * sin( Radians ) ),
	nCenterY - (int) ( (double) (
	( nCenterY * 2 ) / 3 ) * cos( Radians ) ) );
			
// Calculate the radians for the second hand.
Radians = (double) Time.GetSecond();
Radians *= 6.28 / 60.0;
			
// 秒针为黑色
CPen SecondPen( PS_SOLID, 1, RGB( 0, 0, 0 ) );
			
// Select the newly-created CPen object into the DC.
pDC->SelectObject( &SecondPen );
			
// Move the to center of the clock, then draw the second hand line.
pDC->MoveTo( nCenterX, nCenterY );
pDC->LineTo(
	nCenterX + (int) ( (double) (
	( nCenterY * 4 ) / 5 ) * sin( Radians ) ),
	nCenterY - (int) ( (double) (
	( nCenterY * 4 ) / 5 ) * cos( Radians ) ) );
			
	// Select the old CPen object back into the DC.
	pDC->SelectObject( pOldPen );


 第五步:运行即可得到如下界面:

 

 

 

 

 

 

 

 

 

posted @ 2010-10-22 22:27 五行缺火 阅读(264) 评论(0) 编辑