delegate(委托)是C#中一项令我比较费解的东西,在网上查到以下代码用以学习,根据MSDN中的说明,使用delegate可以让一个类(A)访问另一个类(B)的函数而不必是类(B)的成员.
在以下的代码中,在名称空间中定义了一个delegate对象vvCallBack(),它的参数就是准备要调用的目标函数(PrintSearchResult)的参数(string s). 这里注意,目标函数(PrintSearchResult)是一个private函数.
在类A中,通过第52行(public void SearchData(string SearchCondition, vvCallBack ClientDefinedProcedure))调用目标函数,这里无视目标函数是在其他哪个类中或者目标函数是不是public,只要在第58行中(ClientDefinedProcedure(i.Name);)用对参数即可
在类B中,第75行(vvCallBack procedure01 = new vvCallBack(PrintSearchResult);)把目标函数填入新建的delegate对象中,准备输入其他类中调用.
在第81行(lst.SearchData("Red", procedure01);)进行调用,由此,类B(MainClass)的PrintSearchResult函数通过delegate被类A(DataHolder)的函数调用了.
由此可见,在类B中,要使类A的成员返调类B的函数时,需要使用delegate.
总结一下使用delegate的步骤:
- delegate在使用前一定要先定义好,而且此定义必须要让类A和类B都能可见
- delegate定义时的参数要和目标函数的参数保持一致
- 需要的话,在调用类A中可以把此delegate对象当作参数,以备回调用
- 在要调用目标函数前,必须先声明一个delegate实例,即把目标函数填入此delegate对象中,基本语法:vvCallBack newDeleInstance = new vvCallBack(TargetFunctionNameWithoutParameter);
- 把此delegate实例当作参数,被类A的函数调用;此delegate也可以直接执行,如文末的例子一样,就用 newdeleInstance.Invoke();
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Collections;
- namespace DelegateStudy1
- {
- /**//*This is declaration of new data type "CallBack" which is based
- * on C# data type "delegate"*/
- public delegate void vvCallBack(string s);
- class
- {
- private string m_ObjectName;
- private string m_Color;
- public DataItem(string Name, string Color)
- {
- m_ObjectName = Name;
- m_Color = Color;
- }
- public string Name
- {get {return m_ObjectName;}}
- public string Color
- {get {return m_Color;}}
- }//Class Item
- /**//*This class represents collection of data items.*/
- class DataHolder
- {
- private ArrayList m_List;
- public DataHolder()
- {
- m_List = new ArrayList();
- m_List.Add(new DataItem("Desk", "Black"));
- m_List.Add(new DataItem("Car", "Red"));
- m_List.Add(new DataItem("Train", "Yellow"));
- m_List.Add(new DataItem("Tomato", "Red"));
- m_List.Add(new DataItem("Cheese", "Yellow"));
- m_List.Add(new DataItem("Computer","Grey"));
- m_List.Add(new DataItem("Sausage", "Red"));
- m_List.Add(new DataItem("Cat", "Black"));
- }
- /**//* this method provides search functionality for caller. When an item
- * meets search condition is found, client defined procedure activates.*/
- public void SearchData(string SearchCondition, vvCallBack ClientDefinedProcedure)
- {
- foreach (DataItem i in this.m_List)
- {
- if (i.Color == SearchCondition)
- {
- ClientDefinedProcedure(i.Name);
- }
- }
- }
- }//Class DataHolder
- class MainClass
- {
- [STAThread]
- static void Main(string[] args)
- {
- /**//*Create list of Data Items*/
- DataHolder lst = new DataHolder();
- /**//* Cteate instance of delegate with type CallBack and bind it
- * to user defined procedure PrintSearchResults()*/
- vvCallBack procedure01 = new vvCallBack(PrintSearchResult);
- Console.WriteLine("Red color objects:");
- /**//*Call search method with search critheria "Red" and user defined
- * procedure which is nesessary to execute when data item meets to search
- * creteria is found.*/
- lst.SearchData("Red", procedure01);
- Console.WriteLine("---------------");
- Console.ReadLine();
- }
- /**//*This procedure is called by DataHolder any time when data item meets to search
- * criteria is found.*/
- private static void PrintSearchResult(string s)
- {
- Console.WriteLine(s);
- }
- }//MainClass
- }
- public class Class1
- {
- public delegate int myFunction(int min);
- public int MyOtherFunction(int mer)
- {
- int mal = 4;
- return mer * mal;
- }
- public static void Main()
- {
- Class1 mb = new Class1();
- myFunction myD = new myFunction(mb.MyOtherFunction);
- int returnValue = myD.Invoke(8);
- console.WriteLine(returnValue);
- Console.Read();
- }
- }
C++编写的DLL函数被C#所写的应用程序调用, 使用VS2005进行DEBUG时, 所设的断点无效, 系统信息显示"The breakpoint will not currently be hit. No symbols have been loaded for this document."

查阅无数资料, 大多写的是关于PDB文件如何如何, 试过都没有作用.
后来发现,其实只要在project properties中更改一下debugger type 就可以了. 如下图所示, 把debugger type从AUTO改到MIXED, 断点就可以用了, 不过要注意改完并DEBUG刚开始时, 断点还是空心的, 只有当DLL被调用时, 断点标记才会变成实心并被激活.

VS2005出现编译错误: fatal error C1902: Program database manager mismatch; please check your installation
复制3个文件(mspdb80.dll, mspdbsrv.exe and mspdbcore.dll), 从C:Program FilesMicrosoft Visual Studio 8Common7IDE 到 C:Program FilesMicrosoft Visual Studio 8VCbin
即可.其中mspdb80.dll可能会已经存在,无所谓了.
Procedure型变量: 在DELPHI中,函数、过程的地址可以赋给一个特殊类型的变量,变量可用如下方式声明: var p : procedure(num:integer); //过程 或: var f : function(num:integer):integer; //函数 也可定义一个过程(或函数)类型,再定义该类型的变量,如: type TMyProc = Procedure(num:integer); var p : TMyProc; example: type TMyProc= procedure (filename : string); //定义过程类型 procedure clearfile(filename : string); begin {….} end ; … var p:TMyProc; //定义过程变量 … p := clearfile; //将具体实现的过程地址赋给过程变量 … p(‘log.dat’); //调用过程 … 可以将该类型的变量赋nil表示清除。 通过这种方式定义函数或过程的变量十分有用,如可用于装入动态链接库、根据不同的条件调用不同的过程等。 Method型变量: 在DELPHI中还有一种特殊的过程,叫做方法(method),在定义方法类型的时候要在过程定义的后面加上 of object,如: type TMyMethod=Procedure(num:integer) of object; 同样也可定义过程类型的变量: var m : TMyMethod; 可以将该类型的变量赋nil表示清除。 方法类型常用于类定义中的事件定义,如控件中常用的OnClick事件,它的类型定义如下: type TNotifyEvent=Procedure(Sender : TObject) of object; 区别: procedure与method的定义虽然很相似,但两者并不一样,也不通用,不能互相赋值。procedure型变量是一个指针指向实际的代码地址,而method型变量实际是一对指针,第一个指向代码的地址,第二个指向包含该代码的类的一个实例。区别一个过程是否是方法就看它是否属于一个类的成员。 Example: Type TMyProc=Procedure(str : String); //定义普通过程类型 TMyMethod=Procedure(str : String) of object; //定义方法类型 … //下面的代码是一个普通的过程 Procedure Proc1(str : String); Begin ShowMessage(str); End; … //下面的代码是一个方法 Procedure TForm1.Proc2(str : String); Begin ShowMessage(str); End; … var p : TMyProc; m : TMyMethod; … p := Proc1; //正确 m := Proc2; //正确 p := Proc2; //错误! m := Proc1; //错误! 在类型定义中,类型的属性(如事件)常用以下方式定义在published关键字后,如: property OnClick : TNotifyEvent read FOnClick write FOnClick; //FOnClick为类的方法型私有成员变量 但published关键字后的属性不能是普通的过程类型。普通过程类型只能在类的private、public、protected成员中。而方法类型可以是类的任何类型成员。 Example: Type TMyClass = class(TObject) Private FOnMyMethod: TMyMethod; FOnMyProc : TMyProc; Public Property OnMyProc1 : TMyProc read FOnMyProc write FOnMyProc; //正确 Published Property OnMyMethod : TMyMethod read FOnMyMethod write FOnMyMethod; //正确 Property OnMyProc2 : TMyProc read FOnMyProc write FOnMyProc; //错误! End;
在Delphi中,也有与C相似的预编译指令,虽然该类指令只在当前的单个文件有效(也有可能是笔者未全面了解该类指令的真正用法),但是这一类指令对于进行多版本的制作工作(如从标准版中出学习版),确实有着相当不错的用途。
一.指令介绍:
1. DEFINE指令:
格式:{$DEFINE 名称}
说明 :用于定义一个在当前单元有效的符号(Symbol)。定义了
之后可以使用IF DEF和IFNDEF指令来判断该符号是否存在。
2. UNDEF指令:
格式:{$UNDEF 名称}
说明:用于取消一个在当前单元已经定义的符号(Symbol)。该指令和DEFINE
配合使用。
3. IFDEF指令:
格式:{$IFDEF 名称}
说明:如果该指令后的名称已经定义,则编译该指令后直到{$ELSE}或{$ENDIF}之间的代码段。
4. IFNDEF指令:
格式:{$IFNDEF 名称}
说明:如果该指令后的名称没有定义,则编译该指令后直到{$ELSE}或{$ENDIF}之间的代码段。
5. IFOPT指令:
格式:{$IFOPT 开关}
说明:如果该指令后的开关已经设立,则编译该指令后直到{$ELSE}或{$ENDIF}之间的代码段。
举例:{$IFOPT R+}
Writeln('编译时打开范围检查开关');
{$ENDIF}
6. ELSE指令:
格式:{$ELSE}
说明:通过判断前缀Ifxxx的条件式来确定该指令到{$ENDIF}之间的代码段是否应该被编译或者忽略掉。
7. ENDIF指令:
格式:{$ENDIF}
说明:和Ifxxx配合,指明条件预编译段源代码段的结束位置。
二.范例:
编写例子,通过预先定义不同的编译符号,进行不用代码段的编译工作。
1. 新建一个Delphi项目,在Unit1单元的窗体上添加一个Button按钮。
2. 编写程序如下:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
a : String;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{$DEFINE AAA} // 定义行。
procedure TForm1.FormCreate(Sender: TObject);
begin
a := 'Other';
{$IFDEF AAA}
a := 'AAA';
{$ENDIF}
{$IFDEF BBB}
a := 'BBB';
{$ENDIF}
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Caption := a;
end;
end.
{注:粗体字部分为输入的代码}
3. 编译后运行,按下Button,则看到窗体标题栏显示“AAA”。程序编译了a := ’AAA’的语句。
4. 改变定义行的程序段:
当改为
{$DEFINE BBB}
时,再次编译运行,则看到窗体标题栏显示“BBB”。程序编译了a := ’BBB’的语句。
当取消定义行或改为
{$DEFINE NOTHING}
或其他名称时,再次编译运行,则看到窗体标题栏显示“Other”。程序只编译了a := ’Other’的语句。
三.如何快速的制作和更改版本:
使用预编译指令,在制作同一个程序的多个版本时,只需找出各版本中有区别的单元,依次定义统一的版本符号(Symbol),然后在程序段中加入条件预编译指令,就可以在实际编译中取舍编译不同的程序部分,这样对于程序的规范性(定义统一的版本符号)和保密性(不同的版本编译不同的程序部分)有很好的作用。
然而,由于该类预编译指令只能作用于当前单元,所以不便之处在于不能在一个公共单元定义一次版本符号,而必须在各单元中定义统一版本符号才行,故此,在更换版本时,需要确定所有的版本符号都已改变,这样才能保证各版本的正确性,对此,可以使用Delphi IDE的“Find in Files…”(多个文件中查找字符串)的功能,找出所有定义版本符号的文件和位置,然后依次更改,保证所有位置已经改正。



