在C#中没有C++中的函数指针的概念,但是有委托的概念,功能与函数指针类似。

C# 委托(Delegate)

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:

1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。

2. 创建delegate对象,并"将你想要传递的函数作为参数传入"。

3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。

声明委托(Delegate)

委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。

例如,假设有一个委托:

public delegate int MyDelegate (string s);

上面的委托可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量。

声明委托的语法如下:

delegate <return type> <delegate-name> <parameter list>

实例化委托(Delegate)

一旦声明了委托类型,委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。例如:

public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);

委托的多播(Multicasting of a Delegate)

委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。

使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。下面的程序演示了委托的多播:

using System;

delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
} public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
} static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
// 调用多播
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}

委托的多播实例二

// 小张类
public class MrZhang
{
// 其实买车票的悲情人物是小张
public static void BuyTicket()
{
Console.WriteLine("NND,每次都让我去买票,鸡人呀!");
} public static void BuyMovieTicket()
{
Console.WriteLine("我去,自己泡妞,还要让我带电影票!");
}
} //小明类
class MrMing
{
// 声明一个委托,其实就是个“命令”
public delegate void BugTicketEventHandler(); public static void Main(string[] args)
{
// 这里就是具体阐述这个命令是干什么的,本例是MrZhang.BuyTicket“小张买车票”
BugTicketEventHandler myDelegate = new BugTicketEventHandler(MrZhang.BuyTicket); myDelegate += MrZhang.BuyMovieTicket;
// 这时候委托被附上了具体的方法
myDelegate();
Console.ReadKey();
}
}

委托(Delegate)的用途

下面的实例演示了委托的用法。委托 printString 可用于引用带有一个字符串作为输入的方法,并不返回任何东西。

我们使用这个委托来调用两个方法,第一个把字符串打印到控制台,第二个把字符串打印到文件:

using System;
using System.IO;
namespace delegate1
{
class printString
{
static FileStream fs;
static StreamWriter sw;
//委托声明
public delegate void printstring(string str); //将方法打印到控制台
public static void WriteToScreen(string str)
{
Console.WriteLine("The String is:{0}",str);
} //将方法打印到文件
public static void WriteToFile(string s)
{
fs=new FileStream("c:\\message.txt",FileMode.Append,FileAccess.Write);
sw=new StreamWriter(fs);
sw.WriteLine(s);
sw.Flush();
sw.Close();
fs.Close();
}
//该方法把委托作为参数,并使用它调用方法
public static void sendString(printstring ps)
{
ps("helloworld");
}
static void Main(string []args)
{
printstring ps1=new printstring(WriteToScreen);
printstring ps2=new printstring(WriteToFile);
sendString(ps1);
sendString(ps2);
Console.ReadLine();
}
}
}

# 事件(Event)

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。事件是用于进程间通信。

通过事件使用委托

事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

声明事件(Event)

在类的内部声明事件,首先必须声明该事件的委托类型。例如:

public delegate void BoilerLogHandler(string status);

然后,声明事件本身,使用 event 关键字:

// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;

上面的代码定义了一个名为 BoilerLogHandler 的委托和一个名为 BoilerEventLog 的事件,该事件在生成的时候会调用委托。

举例一

using System;

namespace event1
{
class classA
{
public void dispMethod()
{
Console.WriteLine("classA!");
}
}
class classB
{
public void dispMethod()
{
Console.WriteLine("classB!");
}
}
class myClass
{
//定义委托
public delegate void meDelegate();
public event meDelegate notifyEveryOne;
public void Notify()
{
//如果事件不为空
if(notifyEveryOne!=null)
{
//触发事件
notifyEveryOne();
}
}
}
class Program
{
public static void Main(string[] args)
{
myClass my=new myClass();
classA a=new classA();
classB b=new classB();
my.notifyEveryOne+=new myClass.meDelegate(a.dispMethod);
my.notifyEveryOne+=new myClass.meDelegate(b.dispMethod);
my.Notify();
Console.ReadLine();
}
}
}

举例二

using System;
using System.IO; namespace BoilerEventAppl
{ // boiler 类
class Boiler
{
private int temp;
private int pressure;
public Boiler(int t, int p)
{
temp = t;
pressure = p;
} public int getTemp()
{
return temp;
}
public int getPressure()
{
return pressure;
}
}
// 事件发布器
class DelegateBoilerEvent
{
public delegate void BoilerLogHandler(string status); // 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog; public void LogProcess()
{
string remarks = "O. K";
Boiler b = new Boiler(100, 12);
int t = b.getTemp();
int p = b.getPressure();
if(t > 150 || t < 80 || p < 12 || p > 15)
{
remarks = "Need Maintenance";
}
OnBoilerEventLog("Logging Info:\n");
OnBoilerEventLog("Temparature " + t + "\nPressure: " + p);
OnBoilerEventLog("\nMessage: " + remarks);
} protected void OnBoilerEventLog(string message)
{
if (BoilerEventLog != null)
{
BoilerEventLog(message);
}
}
}
// 该类保留写入日志文件的条款
class BoilerInfoLogger
{
FileStream fs;
StreamWriter sw;
public BoilerInfoLogger(string filename)
{
fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
}
public void Logger(string info)
{
sw.WriteLine(info);
}
public void Close()
{
sw.Close();
fs.Close();
}
}
// 事件订阅器
public class RecordBoilerInfo
{
static void Logger(string info)
{
Console.WriteLine(info);
}//end of Logger static void Main(string[] args)
{
BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt");
DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent();
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(Logger);
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(filelog.Logger);
boilerEvent.LogProcess();
Console.ReadLine();
filelog.Close();
}//end of main }//end of RecordBoilerInfo
}

C#与C++的区别(三) 委托与事件的更多相关文章

  1. C#委托和事件

    委托和事件都可以用来调用跟自己方法签名一样的方法,两者在使用中主要有以下区别: 委托和事件没有可比性,因为委托是类型,事件是对象: 委托可以在声明它的类外部进行调用,而事件只能在类的内部进行调用: 委 ...

  2. C#一些知识点:委托和事件的区别

    在C#中,委托和事件是比较容易混淆的两个知识点,本篇博客就记录一下委托和事件之间的区别. 定义上的区别 委托:委托实际上是一个类,用来表示一个函数,可以理解为C++中的函数指针. 事件:事件是一个修饰 ...

  3. C#中委托和事件的区别实例解析

    这篇文章主要介绍了C#中委托和事件的区别,并分别以实例形式展示了通过委托执行方法与通过事件执行方法,以及相关的执行流程与原理分析,需要的朋友可以参考下 本文实例分析了C#中委托和事件的区别,分享给大家 ...

  4. c# 委托与事件的区别

    委托与事件的区别 委托和事件没有可比性,因为委托是数据类型,事件是对象(可以理解为对委托变量的封装.),下面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实现)事件的区别.事件的内部 ...

  5. [转]大白话系列之C#委托与事件讲解(三)

    本文转自:http://www.cnblogs.com/wudiwushen/archive/2010/04/21/1717378.html [我希望大家在看完文章的时候,多做做练习,自己也可以想个场 ...

  6. c#之委托和事件的区别

    1.什么是委托,这里就不做介绍了,如果想了解可以查看博客:http://www.cnblogs.com/xiaoxiaogogo/p/3571494.html 下面开始对事件进行介绍 1.定义事件以及 ...

  7. OC与Swift的区别三(条件语句)

    11.swift中的switch结构 区别一: oc中switch条件只可以放整数 swift中switch条件可以放几乎任何数据类型 区别二: oc中每一个case中应有break,如果没有brea ...

  8. C#中委托和事件的区别

    大致来说,委托是一个类,该类内部维护着一个字段,指向一个方法.事件可以被看作一个委托类型的变量,通过事件注册.取消多个委托或方法.本篇分别通过委托和事件执行多个方法,从中体会两者的区别. □ 通过委托 ...

  9. c#中委托和事件区别

    委托和事件相同的功能 class Dem5 { public Action deHandler; public event Action eveHa; public Dem5() { deHandle ...

  10. 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别

    在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...

随机推荐

  1. codeforces 330b

    #include<stdio.h> #include<string.h> #include<stdlib.h> #define N 1100 int map[N]; ...

  2. MySQL workbench8.0 CE基本用法(创建数据库、创建表、创建用户、设置用户权限、创建SQL语句脚本)

    原文地址:https://blog.csdn.net/zgcr654321/article/details/82156277 安装完成MySQL后,打开MySQL workbench8.0. 可以看到 ...

  3. Jquery根据JSON生成Table

    先说下背景 本人属于juqery小白中的极品小白.基本对于JS jquery这些不懂.用到时候基本百度下 拿过来改改OK. 上面这东西让我弄了三天.可能对于其他人来说 一天就搞定了 .看来还真得去学一 ...

  4. Git Cheat Sheet 中文版

    Git Cheat Sheet 中文版 索引 配置 配置文件 创建 本地修改 搜索 提交历史 分支与标签 更新与发布 合并与重置 撤销 Git Flow 配置 列出当前配置: $ git config ...

  5. ELK pipeline

    https://www.felayman.com/articles/2017/11/24/1511527532643.html?utm_medium=hao.caibaojian.com&ut ...

  6. 动态替换logback FileAppender/RollingFileAppender 配置- Programmatically configure logback FileAppender/RollingBackAppender

    一.本文实际解决的问题 如何在代码中修改logback的RollingFileAppender配置(本文代码实例为修改日志文件路径) 二.针对的场景: 本文解决的问题属于一个大需求的一部分,需求为:需 ...

  7. sql语句在Mysql中如何执行?

    1.MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器.查询缓存.分析器.优化器.执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redo ...

  8. 洛谷 P3456 [POI2007]GRZ-Ridges and Valleys

    P3456 [POI2007]GRZ-Ridges and Valleys 题意翻译 给定一个地图,为小朋友想要旅行的区域,地图被分为n*n的网格,每个格子(i,j) 的高度w(i,j)是给定的.若两 ...

  9. MongoDB小结24 - 索引简介2

    索引的名字 集合中每个索引都有一个字符串类型的名字,来唯一标识索引. 服务器通过名字来操作或者删除索引. 要注意的是,索引名有字符个数限制,所以索引创建时一定要用自定义的名字,如 db.user.en ...

  10. Myeclipse的优化方法

    近期在实习,公司给分配了新的电脑,可是不知道怎么弄得,总是弄得非常卡,没办法仅仅有自己好好整理一下电脑了,另外.为了提高编程的效率.顺便也把Myeclipse也优化了一下. 第一步: 取消自己主动va ...