什么是委托?                                                                                      

  委托类似于C语言中的函数指针,更类似于C++11中的std::function,但更安全也更强大。委托如同类一样是一个类型,只不过类的实例指向一个对象,而委托的实例指向一个方法,该方法可以是成员方法也可以是静态方法。

委托的基本使用

  下面介绍委托的两种初始化和调用方式

using System;

namespace Delegate01
{
class Program
{
public delegate string MyDelegate(string s); //定义一个委托,其返回值是string,参数为空
static void Main(string[] args)
{
//委托初始化和调用的第一种方式。该例子中绑定的方法为成员方法
Program self = new Program();
MyDelegate d1 = new MyDelegate(self.ShowSelf);
Console.WriteLine(d1.Invoke("tianya")); //委托初始化和调用的第二种方式,这种方式因为更简便,所以更常用。该例子中绑定的方法为静态方法
MyDelegate d2 = Program.Show;
Console.WriteLine(d2("feng"));
} string ShowSelf(string str)
{
Console.WriteLine("ShowSelf str:" + str);
return "ShowSelf Ret";
} static string Show(string str)
{
Console.WriteLine("Show str:" + str);
return "Show Ret";
}
}
}

Action委托和Func委托

  Ation 委托可以用来替代返回值是 void 的委托,泛型参数表示委托的参数,至多可以传递16个参数,其中没有泛型参数的Action类可调用没有参数的方法。

  Func 委托可以用来替代返回值不是 void 的委托,最后一个泛型参数表示返回类型,其它参数表示委托的参数,至多可以传递16个参数和一个返回类型,Func委托至少包括一个泛型参数,可调用没有参数的方法。

  Action 和 Func 委托并没有特殊之处,只是为了方便使用而提出的,尤其是把委托做为函数的某个参数时,不需要显式定义一个委托类型。

using System;

namespace Delegate02
{
class Program
{
public delegate int MyDelegate(int i, int j); //定义一个委托
static void Main(string[] args)
{
CallDelegate(Add); //方法参数是一个委托定义
CallDelegateWithFunc(Add); //方法参数是一个Func委托
CallDelegateWithAction(Show); //方法参数是一个Action委托
} //如果不使用Action和Func,将委托做为参数,必须要先定义一个委托类型
static void CallDelegate(MyDelegate d)
{
d(,);
} //如果使用Action和Func,将委托做为参数,就不需要先行定义。明显这种写法更简洁清晰
static void CallDelegateWithFunc(Func<int,int,int> d)
{
d(,);
} static void CallDelegateWithAction(Action d)
{
d();
} static int Add(int i, int j)
{
Console.WriteLine("sum:" + (i + j).ToString());
return i + j;
} static void Show()
{
Console.WriteLine("Show");
}
}
}

匿名方法和lambda表达式

using System;

namespace Delegate03
{
class Program
{
static void Main(string[] args)
{
//匿名方法使用 delegate关键字修饰,主要用于一些只需要使用一次的方法,这样就不需要显式去定义。
Func<int,int,int> f1= delegate(int i,int j) { return i + j; };
Console.WriteLine(f1(, )); //Lambda表达式是匿名方法的一种更简化写法
Func<int, int, int> f2 = (i, j) => { return i + j; };
Console.WriteLine(f2(, )); //Lambda的参数只有一个的时候,可以不需要小括号,当函数体中的语句只有一句的时候,可以不加上大括号和return关键字
Action<string> a = s => Console.WriteLine(s); //等同于 Action<string> a = (s) => { Console.WriteLine(s); };
a("Action");
}
}
}

多播委托

  所谓多播委托,是指一个委托可以注册多个方法,可以使用 +,+=,-,-= 操作符,在调用委托时,可能触发多个方法调用。

using System;

namespace Delegate04
{
class Program
{
static void Main(string[] args)
{
//多播委托
Action<string> a = Run;
a += Jump;
a("Cat"); //使用Lambda实现的多播委托
Action<string> a1 = name => Console.WriteLine(name + "->Run");
a1 += name => Console.WriteLine(name + "->Jump");
a1("Mouse"); //使用有返回值的多播委托,因为有返回值,这种多播委托实际上没有效果,只会调用最后一个注册的方法。这个要格外注意
Func<int, int, int> f = Add;
f += Mul;
Console.WriteLine(f(, )); //多播委托在调用时,如果其中一个方法调用抛出异常,则后续的方法将不会执行。
//可以通过 GetInvocationList 方法获取该委托对象上已注册的方法列表,然后依次执行
Delegate[] delegates = a.GetInvocationList();
foreach (Action<string> action in delegates)
{
action("Rabbit");
}
//使用 DynamicInvoke 方法可以在委托的类型不可知的情况下通过反射来调用,但对性能有影响。
foreach (var action in a.GetInvocationList())
{
action.DynamicInvoke("Rabblit");
}
} static void Run(string name)
{
Console.WriteLine(name + "->Run");
} static void Jump(string name)
{
Console.WriteLine(name +"->Jump");
} static int Add(int i, int j)
{
return i + j;
} static int Mul(int i,int j)
{
return i * j;
} }
}

事件

  事件是一种受约束的委托,通过 event 关键字修饰,事件只能定义为类的成员,且只能在当前类中被调用,你可以理解它为私有的(但其实不是,而且必须不是,因为其它类需要注册该委托)。那么为什么要弄出事件这个东西来呢?主要是为了实现发布\订阅机制,在发布者类中定义一个事件,并由发布者类中的一个公有方法调用该事件,而诸多订阅者则可以为事件注册方法,注册好之后,调用发布者中的公有方法即可触发事件。如果使用普通的委托,会带来一个问题,即订阅者也可以直接调用委托。

using System;

namespace Delegate05
{ class Cat
{
public event Action action; //当该方法调用后,会回调定义好的委托,继续执行已经注册的方法
public void Shout()
{
Console.WriteLine("Cat Shout!");
action();
}
} class Mouse
{
private string name;
public Mouse(string name)
{
this.name = name;
}
public void Run()
{
Console.WriteLine("name:" + name + " Run!");
} public void Jump()
{
Console.WriteLine("name:" + name + " Jump!");
}
} class Program
{
public static void Main()
{
Mouse mouse1 = new Mouse("mouse1");
Mouse mouse2 = new Mouse("mouse2");
Cat cat = new Cat();
cat.action += mouse1.Run;
cat.action += mouse1.Jump;
cat.action += mouse2.Run;
cat.action += mouse1.Jump;
cat.Shout();
//cat.action(); //使用事件时,无法在类外调用委托。
}
}
}

C#委托全解析的更多相关文章

  1. Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能

    我们的Glide系列文章终于要进入收尾篇了.从我开始写这个系列的第一篇文章时,我就知道这会是一个很长的系列,只是没有想到竟然会写这么久. 在前面的六篇文章中,我们对Glide的方方面面都进行了学习,包 ...

  2. Google Maps地图投影全解析(3):WKT形式表示

    update20090601:EPSG对该投影的编号设定为EPSG:3857,对应的WKT也发生了变化,下文不再修改,相对来说格式都是那样,可以到http://www.epsg-registry.or ...

  3. C#系统缓存全解析(转载)

    C#系统缓存全解析 对各种缓存的应用场景和方法做了很详尽的解读,这里推荐一下 转载地址:http://blog.csdn.net/wyxhd2008/article/details/8076105

  4. 【凯子哥带你学Framework】Activity界面显示全解析

    前几天凯子哥写的Framework层的解析文章<Activity启动过程全解析>,反响还不错,这说明“写让大家都能看懂的Framework解析文章”的思想是基本正确的. 我个人觉得,深入分 ...

  5. iOS Storyboard全解析

    来源:http://iaiai.iteye.com/blog/1493956 Storyboard)是一个能够节省你很多设计手机App界面时间的新特性,下面,为了简明的说明Storyboard的效果, ...

  6. 【转载】Fragment 全解析(1):那些年踩过的坑

    http://www.jianshu.com/p/d9143a92ad94 Fragment系列文章:1.Fragment全解析系列(一):那些年踩过的坑2.Fragment全解析系列(二):正确的使 ...

  7. (转)ASP.NET缓存全解析6:数据库缓存依赖

    ASP.NET缓存全解析文章索引 ASP.NET缓存全解析1:缓存的概述 ASP.NET缓存全解析2:页面输出缓存 ASP.NET缓存全解析3:页面局部缓存 ASP.NET缓存全解析4:应用程序数据缓 ...

  8. jQuery&nbsp;Ajax&nbsp;实例&nbsp;全解析

    jQuery Ajax 实例 全解析 jQuery确实是一个挺好的轻量级的JS框架,能帮助我们快速的开发JS应用,并在一定程度上改变了我们写JavaScript代码的习惯. 废话少说,直接进入正题,我 ...

  9. ARM内核全解析,从ARM7,ARM9到Cortex-A7,A8,A9,A12,A15到Cortex-A53,A57

    转自: ARM内核全解析,从ARM7,ARM9到Cortex-A7,A8,A9,A12,A15到Cortex-A53,A57 前不久ARM正式宣布推出新款ARMv8架构的Cortex-A50处理器系列 ...

随机推荐

  1. Enum遇到下拉框

    package com.zj.tool; public enum WeekDay { Mon(), Tue(), Wed(), Thu(), Fri(), Sat(), Sun(); /**定义枚举类 ...

  2. asp.net项目发布打包研究

    有几种思路: 1.[推荐]直接发布,然后手动打包成压缩包,需要的时候直接上传到服务器,或者在本地解压出来手动上传到虚拟空间(支持绝大多数的虚拟空间,自由度高,DZ也是采用这样的打包,FTP上传操作比较 ...

  3. python 生成器和递归

    生成器 1.定义 问题:python会把对象放到内存中,我们每次定义变量.列表等都会在内存中占用对应的地址块,所以当内存容量一定时,列表的容量会受到内存的限制,而且假如我们创建了一个包含200万个元素 ...

  4. 第一章、欢迎进入C#编程世界

    1.GUI:图形用户界面. 2.在C#中,所有可执行代码都必须在方法中定义,而方法必须从属于类或结构. 3.程序集中可能包含多个命名空间的类,而一个命名空间可能跨越多个程序集. 4.解决方案文件使用. ...

  5. iOS - 沙盒中,如何判断存在文件、目录

    在iOS开发中,在沙盒中创建沙盒一些存储各个功能的文件目录或者文件. 使用: [NSFileManager defaultManager] 1.判断目录,用她可以. 2.判断文件,用她可以. 3.创建 ...

  6. 捉襟见肘之gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer

    -- :::60b] *** -[ZFModalTransitionAnimator gestureRecognizer:shouldBeRequiredToFailByGestureRecogniz ...

  7. linux忘记mysql密码找回方法

    linux忘记mysql教程密码找回方法 今天我们主要是讲一下关于linux忘记mysql密码处理方法,下面提供了5种linux忘记mysql密码找回方法哦.    方法一: # /etc/init. ...

  8. 适可而止:YAGNI原则

    适可而止:You Ain't Gonna Need It YAGNI原则指的是只需要将应用程序必需的功能包含进来,而不要试图添加任何其他你认为可能需要的功能. 在一个软件项目中,往往80%的时间花费在 ...

  9. js009-客户端检测

    js009-客户端检测 本章内容: 1.使用能力检测 2.用户代理检测的历史 3.选择检测方式 一般不使用客户端检测. 9.1 能力检测 也称特性检测.基本模式如下: if(object.proper ...

  10. Java线程:Timer和TimerTask

    Timer和TimerTask可以做为实现线程的第三种方式,前两中方式分别是继承自Thread类和实现Runnable接口. Timer是一种线程设施,用于安排以后在后台线程中执行的任务.可安排任务执 ...