C# 委托基础
不知不觉,又开始委托的学习了,感觉这个东西我能学一辈子,哈哈哈!这次看的是官方的参考书(C#高编9),每次看不同的资料,总能学到不同的知识!言归正传
1、为什么要使用委托?
我们习惯于将数据作为参数传递给方法,但是很多时候我们需要将一个方法传递给另一个方法。所以委托就是干这个的,将方法作为参数,传递给另一方法.
举个例子:在C#中,可以告诉计算机并行执行某些新的执行序列,同时运行当前任务,这个序列就称之为线程,如果要告诉计算机启动一个新的线程,就必须告诉线程入口方法在哪里,并告诉计算机开始启动方法的细节,所以线程(Thread)的构造函数必须带有一个参数,该参数定义了线程调用的方法。这里就使用到了委托的概念,将方法传递给线程!
2、委托的特性
在C和C++中,只能提取函数的地址,该地址可以作为参数传递给任何需要它的函数,这种方法会导致类型安全问题。但是在.Net Framework中,当我们进行面向对象编程的时候,几乎没有方法是孤立存在的,当我们吊用一个方法前,必须确保这个方法与类实例关联,所以在.Net Framework中不允许直接使用方法,或者传递方法,如果需要传递方法,那么必须把该方法的细节和参数封装在一个类对象中,通过传递对象实例的方式,来传递方法。但是用类对象来传递方法,有点大材小用了,所以.Net Framework中定义了一种新的类型对象-委托,这种对象专门用来传递方法,如果我们要传递一个方法,就必须把方法的细节封装到委托中。通过委托实例来传递方法,所以委托和类其实在某种程度上说是一样的,委托是一种特殊的类。这种类,专门负责传递方法。
3、声明委托
2中,提到了委托其实是一种特殊的类,所以它的申明方式和类其实是差不多的,只不过类用class来修饰,且类有方法体,而委托使用delegate来修饰没有方法体,委托必须声明委托所代表的一类方法的返回值,并且声明这类方法的参数,代码如下:
public delegate void MyDeleagte(int a);
一个无返回值,带一个int参数的委托申明好了。
如果要定一个委托,不带参数,返回一个string类型的值,代码如下:
public delegate string MyDelegate();
4、委托的结构
当我们定义完一个委托,实际上是定义了一个类,委托实现为派生自System.MulticastDelegate抽象类的类,System.MulticastDelegate又派生字System.Delegate,C#编译器能识别这个类,会使用其委托语法.
5、委托中注册的方法的使用
委托中方法的调用有两种方式:
a、委托变量加括号的方式,代码如下:
public delegate void GetString();
class Program
{
static void Main(string[] args)
{
var a = new GetString(aa);
a += aa;
a += aa;
a += aa;
a += aa;
a += aa;
a();
Console.ReadLine();
}
static void aa()
{
Console.WriteLine("1");
}
}
b、使用Invoke()方法
public delegate void GetString();
class Program
{
static void Main(string[] args)
{
var a = new GetString(aa);
a += aa;
a += aa;
a += aa;
a += aa;
a += aa;
a.Invoke();
Console.ReadLine();
}
static void aa()
{
Console.WriteLine("1");
}
}
上面是最常规的委托使用方法,当然还有其他的如:BeginInvoke()等,这里不做解释,如想要了解,请参考C# 委托进阶
6、多播委托
上面的实例中,我给一个委托绑定了多个方法,如果要多次调用这些方法,就需要显示多次吊用。我们把一个委托,如果这个委托绑定了多个方法,称为多播委托,如果调用多播委托,就可以按顺序连续吊用多个方法。为此,委托的签名必须返回void,否则就只能得到最后一个方法的返回值。
class Program
{
static void Main(string[] args)
{
Action<double> options = new Action<double>(MutiplyByTwo);
options += Square;
options.Invoke(3);
Console.ReadLine();
}
static void MutiplyByTwo(double a)
{
Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
}
static void Square(double a)
{
Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
}
}
上面是最基本的使用多播委托的方法,但是它存在以下问题
a、对同一个委托调用方法链的顺序并未正式定义,因此应避免编写依赖于特定顺序的调用方法的代码。
b、通过一个委托调用多个方法还可能导致大问题,当一个方法抛出异常,整个迭代就会停止
b的实现代码如下:
class Program
{
static void Main(string[] args)
{
Action<double> options = new Action<double>(MutiplyByTwo);
options += Square;
options.Invoke(3);
Console.ReadLine();
}
static void MutiplyByTwo(double a)
{
Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
throw new Exception("我出异常啦");
}
static void Square(double a)
{
Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
}
}
第二行代码并没有正常输出,ok,那么将异常捕获,看它会不会执行第二行代码,代码如下:
class Program
{
static void Main(string[] args)
{
Action<double> options = new Action<double>(MutiplyByTwo);
options += Square;
try
{
options.Invoke(3);
}
catch (Exception ex)
{
Console.WriteLine("Exception Caught");
}
Console.ReadLine();
}
static void MutiplyByTwo(double a)
{
Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
throw new Exception("我出异常啦");
}
static void Square(double a)
{
Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
}
}
异常被捕获,但是第二行代码还是没有执行,这是因为第一个方法抛出了异常,所以委托的迭代会停止,不再调用下面的方法。
b的解决方法:
为了解决上面的问题,Delegate类定义了GetInvocationList方法,他返回一个Delegate[]数组。现在可以使用对象吊用委托对应的方法,捕获异常,并进行下一次迭代,代码如下:
class Program
{
static void Main(string[] args)
{
Action<double> options = new Action<double>(MutiplyByTwo);
options += Square;
Delegate[] delegates = options.GetInvocationList();
foreach (Action<double> a in delegates)
{
try
{
a.Invoke(3);
}
catch (Exception)
{
Console.WriteLine("Exception Caught");
}
}
Console.ReadLine();
}
static void MutiplyByTwo(double a)
{
Console.WriteLine("Mutiplying by 2:{0} gives {1}", a, a * 2);
throw new Exception("我出异常啦");
}
static void Square(double a)
{
Console.WriteLine("Squareing :{0} gives {1}", a, a * a);
}
}
ok,问题解决,异常顺利的捕获,且委托的迭代正常完成!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- MySQL information_schema详解 ndb_transid_mysql_connection_map
- MySQL information_schema详解 OPTIMIZER_TRACE
- 用 Python 画一个奸笑(滑稽)表情
- Python 进阶(十):网络编程
- Python 数据分析(二):Matplotlib 绘图
- 用 Python 制作一个艺术签名小工具,给自己设计一个优雅的签名
- 你认可《后浪》吗
- Python 数据分析(三):初识 Pandas
- MySQL information_schema详解 PARAMETERS
- Python 数据分析(四):Pandas 进阶
- 用 Python 写一个颜值测试小工具
- 当 Python 遇到微信
- CenterNet之loss计算代码解析
- 爬取林丹和赵雅淇的微博评论,看看网友都说了些什么
- 静默安装单机Oracle数据库软件