C#编程语言之委托与事件(一)—— C/C++函数指针和C#委托初步
相信正在学习C#的人都有学习过C或C++的经验,本文要讲的第一个要点是C#中的委托(delegate,有些资料也叫代表)。什么是委托,很多人都能自然而然地想到C/C++中的函数指针,事实上很多书和资料都以此来引出C#中委托的概念,在此我建议如果没有接触过C/C++的同学可以先了解一下相关的知识再来继续C#的学习,毕竟作为编程语言的基础,语言都是招式,思维想法才是内功。有了扎实的基础,后期学习起来才能够事半功倍。
首先我们通过一个简单的例子快速复习一下C/C++函数指针:
#include<iostream>
using namespace std;
int func(string name){
cout<<"My name is "<<name<<endl;
}
void call(int(*fun)(string)){
fun("Evan Lin");
}
int main(int args,char ** argv){
call(func);
}
重点是 int(*fun)(string) 这个语句,指定一个函数指针的形参,就如同我们定义一个变量 char ch 一样,但要求是此处函数指针的返回值和参数列表都必须与即将传进来的函数地址严格匹配,不然会产生[Invalid Conversion Error],且此处的声明方式只能用指针,即这个 (*fun) ,因为事实上函数指针只是通过一个函数的入口去操作一个函数,虽然可以通过 typedef int (fun)(string); fun *fp 的方式去表示一个函数,但最终也是要定义一个函数的指针,所以此处无法不通过指针而去调用一个函数,至少目前阶段我没有了解到,有了解的朋友可以说出来共同探讨。
C#中的委托和C/C++中函数指针的对比
1、C/C++函数指针是通过寻找函数的入口来调用一个函数,C#委托是把函数名当做一个参数传入一个委托对象当中,委托是类型,函数指针是指针。
2、C/C++函数指针的返回类型和参数列表是作为匹配函数参数的标志,而C#委托有签名(Signature)的概念。
3、C/C++函数指针直接操作内存的某个地址,而C#委托托管在.Net Framwork下,是一种强类型
委托的签名(Signature)由委托的返回类型和参数列表组成,看起来和C/C++函数指针的返回类型和参数列表并无多大区别,但作为一门强大的语言,委托的签名的作用不仅仅是作为一种限定作用,其他作用会由下文提及。下面我们按部就班地一步步来认识委托(delegate)
一、下面是一则例子用于介绍委托的使用方法
using System;
namespace ConsoleApplication {
class DelegateTest {
public delegate void myDelegate(string name);
public static void func(string name) {
Console.WriteLine("My name is " + name);
}
static void Main() {
myDelegate _myDe = new myDelegate(DelegateTest.func);
_myDe("Evan Lin");
}
}
}
使用关键字delegate声明一个委托类型,声明形式主要是【delegate + 返回类型 + 委托名 + 参数列表】
像普通类型一样定义一个委托变量,生成委托对象时必须把签名相应的函数作为参数传入委托对象当中,然后进行调用。
二、委托的快捷语法,可以直接把函数名赋值给委托变量
myDelegate _myDe = DelegateTest.func;
_myDe("Evan Lin");
委托和函数(与签名相应)之间存在着隐式转换
三、多播(Multicast)委托
多播委托表示可以通过+=和-=的运算符号来添加或者删除到委托队列当中,当执行这个委托的时候会按依次执行添加到委托队列当中的所有委托,当使用多播委托时,委托的返回类型必须为void,否则运行时只会执行最后一个添加到委托队列的委托。
此处需要注意一点,当添加两个相同的函数时,在委托队列当中实质上添加了两个委托,但当减去一个委托时,如果该委托实体在委托队列中存在时,则把这份委托删除,但如果该委托实体在委托队列中不存在时,委托队列不做任何改变,且不会发生编译时异常。当委托队列为空,然后执行这个多播委托时,会抛出NullReferenceException。
下面看一小段代码加以理解:
using System;
namespace ConsoleApplication1 {
class DelegateTest {
public delegate void AnimalDelegate();
public static void Cat() {
Console.WriteLine("Miao Miao");
}
public static void Dog() {
Console.WriteLine("Wang Wang");
}
static void Main() {
AnimalDelegate _aniD;
AnimalDelegate _catD = new AnimalDelegate(DelegateTest.Cat);
AnimalDelegate _dogD = new AnimalDelegate(DelegateTest.Dog);
_aniD = _catD + _dogD;
_aniD();//Miao Miao \n Wang Wang
_aniD -= _catD;
_aniD();//Wang Wang
}
}
}
运行会依次打印“Miao Miao”和“Wang Wang”两行结果,然后再打印“Wang Wang”。
四、匿名方法和Lambda表达式
可以注意到使用委托真正起到作用的仅仅是委托的签名,为了提高开发效率,于是有了匿名方法(= =纯属猜想,欢迎斧正),具体实现方法如下:
using System;
namespace ConsoleApplication1 {
class DelegateTest {
public delegate String MyDelegate(int arg);
static void Main() {
MyDelegate _myDe = delegate (int arg) {
return arg > ? "More than zero" : "Less than or equals zero";
};
Console.WriteLine(_myDe());
Console.WriteLine(_myDe());
}
}
}
如代码所示,用【delegate关键字+参数列表+方法体】构成一个委托匿名方法,此处隐藏了具体的函数名称,匿名方法的返回值可有可无(根据委托签名),函数体的反花括号后要加分号,然后使用正常方法调用委托。说到这里相信大家都可以猜想到实际的输出结果,分别是Less than or equals zero和More than zero两行结果。
Lambda表达式具有比较特殊的写法,同样是为了提高开发效率,降低函数名的重复率等原因,以下通过一个实例进行了解:
using System;
namespace ConsoleApplication {
class DelegateTest {
public delegate String MyDelegate(int arg);
static void Main() {
MyDelegate _myDe = (arg) => {
return arg > ? "More than zero" : "Less than or equals zero";
};
}
}
}
实际效果等同于上一个匿名方法,在Lambda表达式中连参数类型都省去了,因为在定义一个委托类型的时候已经限定了委托的参数类型,以以上代码为例,其中参数arg的类型必须是int,返回类型必须是String。
五、委托泛型
如果对应于不同的函数返回类型和函数参数列表,需要声明大量不同签名的委托。泛型委托的出现是为了能适应不同类型的函数,提高代码的复用率,以下通过一个简单的例子来加深理解。
using System;
namespace ConsoleApplication {
class DelegateTest {
public delegate T1 myDelegate<T1, T2>(T1 arg1, T2 arg2);
public static string func1(string name,int num) {
return "My name is " + name + ",and my favorite number is " + num;
}
static void Main() {
myDelegate<string, int> _myDe = func1;
Console.WriteLine(_myDe("Evan Lin",));
}
}
}
其中的<T1,T2>代表两种自定义类型,同时分别作为委托的两种类型的参数,并且该委托返回T1类型的返回值。通过 myDelegate<string, int> _myDe 来限定<T1,T2>的具体类型。
当想定义一个泛型委托,但又想在类型方面做一些限制,可以用到where关键字
泛型委托约束大约包括几种形式:
public delegate T1 myDelegate<T1,T2>(T1 arg1,T2 arg2)where T1:ClassA
public delegate T1 myDelegate<T1,T2>(T1 arg1,T2 arg2)where T1:ClassA,InterfaceA
public delegate T1 myDelegate<T1,T2>(T1 arg1,T2 arg2)where T1:ClassA where T2:ClassB
public delegate T1 myDelegate<T1,T2>(T1 arg1,T2 arg2)where T1:T2
where后面的表达式代表类型T1只能派生于ClassA类或者是ClassA本身,T2同理。而ClassA,ClassB,InterfaceA等也有一些限制条件,官方文档的解释是:A type used as a constraint must be an interface,a non-sealed class or a type parameter。也就是说作为限制条件的只能是某个接口,非密封(non-sealed)类或者某个参数的类型(即第四句语句所示),除此之外的类型都不能作为泛型约束的类型,否则回显示Invalid constraint错误。
至此,以上均是个人学习C#委托时候的拙见,难免会有纰漏和不妥之处,欢迎指出斧正。
C#编程语言之委托与事件(一)—— C/C++函数指针和C#委托初步的更多相关文章
- Atitit java方法引用(Method References) 与c#委托与脚本语言js的函数指针
Atitit java方法引用(Method References) 与c#委托与脚本语言js的函数指针 1.1. java方法引用(Method References) 与c#委托与脚本语言js ...
- C语言函数指针与 c#委托和事件对比
C语言: 函数指针可以节省部分代码量,写类似具有多态的函数,比如要比较最大值,如果不用函数指针就只能写比较某一类型比如int类型的max函数,这个max无法比较string的大小.函数指针的意义就不多 ...
- VB6/VBA中跟踪鼠标移出窗体控件事件(类模块成员函数指针CHooker类应用)
一.关于起因 前几天发了一篇博文,是关于获取VB类模块成员函数指针的内容(http://www.cnblogs.com/alexywt/p/5880993.html):今天我就发一下我的应用实例. V ...
- .NET基础拾遗(4)委托、事件、反射与特性
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- Silverlight项目笔记1:UI控件与布局、MVVM、数据绑定、await/async、Linq查询、WCF RIA Services、序列化、委托与事件
最近从技术支持转到开发岗,做Silverlight部分的开发,用的Prism+MVVM,框架由同事搭好,目前做的主要是功能实现,用到了一些东西,侧重于如何使用,总结如下 1.UI控件与布局 常用的主要 ...
- c# 关键字delegate、event(委托与事件)[MSDN原文摘录][1]
A delegate is a type that safely encapsulates a method, similar to a function pointer in C and C++. ...
- C#学习之初步理解委托、事件、匿名方法和Lambda
最经在学习LinqtoSql,然后扯到Lambda表达式,然后扯到匿名方法,然后扯到委托,最后扯到事件处理...后来发现对委托这个概念和事件处理这个过程理解得不是很清晰,遂得一下学习笔记.那里说得不对 ...
- C#委托及事件
转载:http://www.cnblogs.com/warensoft/archive/2010/03/19/1689806.html C#委托及事件 在C#中,委托(delegate)是一种引用类型 ...
- C# 基于委托的事件
事件基于多播委托的特性. 多播委托事实上就是一组类型安全的函数指针管理器,调用则执行顺序跳转到数组里所有的函数指针里执行. class Program { public class CarInfoEv ...
随机推荐
- mkfs -t ext3 错误/dev/sdxx is apparently in use by the system; 解决方法
在存储上共享了一个500G的空间,映射到Linux系统提供上,环境由2个节点组成. 一. 测试一: 直接mount 用fdisk 格式化之后如下: [root@rac1 u01]# fdisk ...
- WPF自学入门(一)WPF-XAML基本知识
一.基本概念 1.XAML是派生自XML的可扩展应用程序标记语言(Extensible Application Markup Language)由微软创造应用在WPF,Silverlight等开发技术 ...
- canvas动画:自由落体运动
经过前面的文章,我们已经能够在canvas画布上画出各种炫酷的图形和画面,但是这些画面都是禁止的,怎么样才能让他们动起来呢? 如何绘制基本图形可以参考:canvas基本图形绘制 如何对基本图形移动旋转 ...
- Xshell配色为ubuntu风格
背景 为了远程连接服务器,用Xshell作为连接工具,因为好(mian)用(fei),服务器是ubuntu的,因此看不习惯Xshell自带的黑白色,下面给出了ubuntu的配色方案,使用的时候直接导入 ...
- Keras FAQ: 常见问题解答
Keras官方中文版文档 如何引用 Keras? 如何在 GPU 上运行 Keras? 如何在多 GPU 上运行 Keras 模型? "sample", "batch&q ...
- 关于access_token过期的解决办法
最近在做微信的发送模版消息,在测试的时候发现有的时候能够发送,有时候无法发送,查了相关的日志(日志记录发送结果很重要!!),看到了微信返回的错误消息,发现是 invalid credential, a ...
- [BZOJ2049] [SDOI2008] Cave 洞穴勘测 (LCT)
Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...
- Hive 自定义函数
hive 支持自定义UDF,UDTF,UDAF函数 以自定义UDF为例: 使用一个名为evaluate的方法 package com.hive.custom; import org.apache.ha ...
- 一年iOS工作经验,如何一举拿下百度、美团、快手等Offer面经(附面试题)
前言: 先简单说说我最近的面试经历吧.面试的公司很多,大部分最后都能得到令人满意的结果,我将这些体会记录下来,面了这么多公司,如果不留下什么,那岂不是太浪费了.对于我来说,这也是一次自我检查,在这次面 ...
- VS中使用.NET Reactor进行代码混淆
.NET Reactor相信大家都不陌生,网上使用教程也很多.但绝大多数都只介绍到软件的使用,而对于在VS中使用介绍的不多. 首先,在.NET Reactor的Help中Add In,如下图. 重启V ...