委托是一个类型。C#中的委托是面向对象的,并且它是类型安全的 当创建委托实例的时候,创建的实例会包含一个调用列表,在调用列表中可以包含多个方法。每个方法称作一个调用实体。调用实体可以是静态方法,也可以是实例方法。如果是实例方法,则该调用实体包含调用该实例方法的实例。委托并不关心它所调用方法所属的类,它只关心被调用方法与委托的类型是否兼容。 下面是代码实例:

 using System;
namespace LycheeTest{
public delegate void D(int a, int b);
public class Test {
public D myDelegate;
public Test() {
myDelegate = new D(Show1);
}
private static void Show1(int a, int b) {
Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
}
private void Show2(int a, int b) {
Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
}
private void Show3(int a, int b) {
Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
}
}
public class Program {
static void Main(string[] args) {
Test myT = new Test();
myT.myDelegate(, );
Console.ReadKey();
}
} }

  这段代码演示的是最简单的一种委托形式。委托类型可以定义在类的外部,也可以定义在类的内部。 本段代码是定义在类的外部。第 3 行代码定义的就是一个委托类型,委托类型的关键字是 delegate,关键字前是委托类型的访问权限修饰符。关键字后是委托类型的返回类型,这个返回类型规定与委托类型兼容 的方法的返回类型必须与之相同。返回类型之后是委托类型的名称。接下来是形参列表,它指定与委托类 型兼容的方法的参数类型和个数必须与之相同。第 5 行代码定义了一个委托类型的变量,它是一个实例字段,访问权限是 public 的。注意委托类型字段的访问权限一定要比委托类型的访问权限低或与委托类型的访问权限相同才可以。第 9 行、第 12 行和第 15 行代码定义了三个方法。其中第 9 行代码是一个静态方法。因为这段代码演示的是最简单的委托使用方法,所以只使用了其中的静态方法。在第 6 行的构造方法中,实例化了委托类型的变量,注意为委托变量的调用列表添加方法,只需要向其构造方法中传递方法名称即可。这是为委托添加调用方法的最基本的一种方法。第 21 行定义了 Test 类的一个实例,然后第 22 行调用了类的委托成员。在调用委托成员的时候,需要向其形参列表传递实参。这就是最基本的委托的使用方法。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:

下面再介绍一种委托类型的使用方法,实例代码如下:

 using System;
namespace LycheeTest {
public delegate void D(int a, int b);
public class Test {
public static void Show1(int a, int b) {
Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
}
public void Show2(int a, int b) {
Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
}
public void Show3(int a, int b) {
Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
}
}
public class Program {
static void Main(string[] args) {
Test myT = new Test();
D myDelegate = new D(Test.Show1);
D myDelegate1 = new D(myT.Show2);
D myDelegate2 = new D(myT.Show3);
myDelegate(, );
myDelegate1(, );
myDelegate2(, );
Console.ReadKey();
}
} }

  这段代码取消了类中的委托类型字段,而是将委托类型作为一个类来看待。在包含入口点方法的类中,首先第 17 行定义了 Test 类的一个变量并做了实例化。因为要向委托传递类的实例方法,所以必须有类的实 例存在,才能引用类的实例方法。第 18 行定义了一个委托类型的变量,并实例化,这里需要注意,因为委托并不是类中的一个成员了, 所以向其构造方法传递静态方法的时候,需要以类名引用。第 19 行也定义了一个委托类型的变量,在向其传递实例方法的时候,需要以类的实例来引用。第 20 行代码的情况同第 19 行代码一样。在向委托传递方法的时候,需要传递方法名,而不需要方法的形参列表。第 21 行到第 23 行是对委托的调用,这时要为其传递方法的实参。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:
方法 Show2 被调用,两个实参相加的值是:
方法 Show3 被调用,两个实参相加的值是:

委托的访问修饰符

  当委托位于类的外部时,可以使用的访问修饰符包括 public 和 internal。如果什么也不写,默认是internal 的。当委托位于类的内部时,可以使用的访问修饰符包括 public、protected、internal、protected

 using System;
namespace LycheeTest{
public class Test {
protected delegate void D(int a, int b);
private delegate void D1(int a, int b);
protected internal delegate void D2(int a, int b);
internal delegate void D3(int a, int b);
private D myD;
private D1 myD1;
private D2 myD2;
private D3 myD3;
public Test() {
myD = new D(Show1);
myD1 = new D1(Show1);
myD2 = new D2(Show1);
myD3 = new D3(Show1);
}
public static void Show1(int a, int b) {
Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
}
public void Show2(int a, int b) {
Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
}
public void Show3(int a, int b) {
Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
}
public void Use() {
myD(, );
myD1(, );
myD2(, );
myD3(, );
}
}
class Test1: Test {
private D Test1D;
private D2 Test1D2;
private D3 Test1D3;
public Test1() {
Test1D = new D(Test.Show1);
Test1D2 = new D2(Test.Show1);
Test1D3 = new D3(Test.Show1);
}
public void Use1() {
Test1D(, );
Test1D2(, );
Test1D3(, );
}
}
public class Program {
static void Main(string[] args) {
Test1 myT1 = new Test1();
myT1.Use();
myT1.Use1();
Console.ReadKey();
}
}
}

  代码的第 4 行在类的内部定义了委托类型,它作为类的成员定义,访问权限是 protected,它可以被本类内部访问,也可以被派生类访问。代码的第 5 行定义的委托类型,访问权限是 private 的,它只可以被本类内部访问。代码的第 6 行定义的 protected internal 访问权限的委托类型,可以被本程序集访问, 还可以被派生类访问,而不管派生类位于哪个程序集。第 7 行定义的委托类型是 internal 的,它只可以被本程序集访问。因为所有这几种委托类型都可以被本类内部访问,所以第 10 行到第 13 行定义了它们的变量。第 12 行的实例构造方法中,对这四个委托类型的变量进行了实例化,并为它们的调用列表加入了方法 Show1。Show1 是一个静态方法,但是在类内部传入委托类型的构造方法时,不需要使用类名引用。第 27 行定义了实例方法,在方法内部调用了这四个委托,并为其传入实参。第 34 行代码又定义了一个类,它继承自基类 Test。因为基类中的委托类型只有 D、D2 和 D3 可以被派生类访问,所以第 35 行到第 37 行定义了它们的变量。注意,虽然它们和基类中的委托变量是同一种类型, 但是它们是不同的委托。在第 38 行的实例构造方法中,为这三个委托类型的变量创建实例,并为其调用列表加入方法,因为静态方法 Show1 也被派生类所继承,所以这里传入的方法名,可以使用类名引用,也可以不使用类名引用。 第 43 行定义了一个实例方法,方法内部调用了这三个委托,并为其传入实参。第 51 行定义了派生类的实例,然后调用实例方法Use和Use1。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是:

  因为 D 和 D2 的访问权限被定义成了 protected 和 protected internal。所以下面来验证在其它程序集中是否可以访问它们。首先要将本段代码中的包含 Main 方法的类去掉,然后在它的项目属性中将它改变为类库。接下来新建一个控制台项目,并物理上引用这个类库。控制台项目的代码如下:

 using System;
using LycheeTest;
namespace LycheeTest1{
class Program: Test {
private D pD;
private D2 pD2;
public Program() {
pD = new D(Show1);
pD2 = new D2(Show1);
}
public void Use3() {
pD(, );
pD2(, );
}
static void Main(string[] args) {
Program p = new Program();
p.Use3();
Console.ReadKey();
}
}
}

  因为第 3 行代码的命名空间和类库的命名空间是两个独立的命名空间,它们的成员不位于同一个命名空间内。所以在一个命名空间内引用另一个命名空间的成员时,需要加上另一个命名空间的名称进行引用。 为了代码编写的方便,第 2 行代码首先引用了类库的命名空间。第 4 行代码定义了一个类,它继承自基类 Test。因为是派生类,所以对于委托类型 D 和 D2 都可以访 问。第 5 行代码和第 6 行代码分别定义了 D 和 D2 的两个变量。第 7 行的实例构造方法对这两个变量进行了实例化,并为其传入方法 Show1。因为 Show1 方法被继承了下来,所以这里不需要类名引用。第 11 行代码定义了一个实例方法,它的作用是调用这两个委托,并为其传入实参。第 16 行代码定义了本类的一个实例,并调用了实例方法 Use3。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是:

  类Test中的委托类型D2和D3都具有internal权限,现在来验证一下,对于一个同一程序集中的非派生类是否可以访问它们。首先将类库更改回控制台项目,然后增加一个类,这个类对于Test类来说是独立的。它们之间只是位于一个程序集内,彼此没有继承关系。代码如下:

 using System;
namespace LycheeTest {
public class Test {
protected delegate void D(int a, int b);
private delegate void D1(int a, int b);
protected internal delegate void D2(int a, int b);
internal delegate void D3(int a, int b);
private D myD;
private D1 myD1;
private D2 myD2;
private D3 myD3;
public Test() {
myD = new D(Show1);
myD1 = new D1(Show1);
myD2 = new D2(Show1);
myD3 = new D3(Show1);
}
public static void Show1(int a, int b) {
Console.WriteLine("方法 Show1 被调用,两个实参相加的值是:{0}", a + b);
}
public void Show2(int a, int b) {
Console.WriteLine("方法 Show2 被调用,两个实参相加的值是:{0}", a + b);
}
public void Show3(int a, int b) {
Console.WriteLine("方法 Show3 被调用,两个实参相加的值是:{0}", a + b);
}
public void Use() {
myD(, );
myD1(, );
myD2(, );
myD3(, );
}
} class Test1 {
private Test.D2 tD2;
private Test.D3 tD3;
public Test1() {
tD2 = new Test.D2(Test.Show1);
tD3 = new Test.D3(Test.Show1);
}
public void Use3() {
tD2(, );
tD3(, );
}
}
public class Program {
static void Main(string[] args) {
Test1 myT1 = new Test1();
myT1.Use3();
Console.ReadKey();
}
}
}

  这段代码中,原来的类Test没有进行修改。在第35行上,定义了一个类,它是一个相对于Test类来说独立的类。它们的关系仅限于同在一个程序集内。第 36 行代码和第 37 行代码定义了委托类型D2和D3的两个变量。这里需要注意,因为这两个类不是继承关系,所以要引用Test类中的这两个委托类型需要使用Test类的类名进行引用。第 38 行代码是实例构造方法,在构造方法中将委托实例化。实例化委托类型的时候,仍然需要使用类名引用委托类型名,传递的方法名也是如此。第 行42 定义了一个实例方法,它调用了委托,并为其传入了实参。第 49 行代码定义了类Test1的一个实例,然后第 61 行调用类的实例方法。这段代码的执行结果如下:

方法 Show1 被调用,两个实参相加的值是:
方法 Show1 被调用,两个实参相加的值是: 

c#核心基础-委托的更多相关文章

  1. C#核心基础--类(2)

    C#核心基础--类的声明 类是使用关键字 class 声明的,如下面的示例所示: 访问修饰符 class 类名 { //类成员: // Methods, properties, fields, eve ...

  2. C#核心基础--类的声明

    C#核心基础--类的声明 类是使用关键字 class 声明的,如下面的示例所示: 访问修饰符 class 类名 { //类成员: // Methods, properties, fields, eve ...

  3. C#复习笔记(2)--C#1所搭建的核心基础

    通过对C#1所搭建的核心基础的深入了解,可以知道之后的C#版本在C#1的基础上做了很多扩展,而这些扩展都是基于C#搭建的核心基础而来的. 委托 一.编写委托的过程 委托经常和C语言的“函数指针”挂钩. ...

  4. Androd核心基础01

    Androd核心基础01包含的主要内容如下 Android版本简介 Android体系结构 JVM和DVM的区别 常见adb命令操作 Android工程目录结构 点击事件的四种形式 电话拨号器Demo ...

  5. css核心基础总结篇

    今日这篇是整合前面的css补充知识的. 我觉得前面的关于css的知识补充进去有点乱,今日整理整理一下. 层叠样式表 层叠是什么意思?为什么这个词如此重要,以至于要出现在它的名称里. 层叠可以简单地理解 ...

  6. Android应用的核心基础

    Android4开发入门经典 之 第二部分:Android应用的核心基础 Android应用中的组件 Application Components Android应用中最主要的组件是: 1:Activ ...

  7. (1) css的核心基础

     css的核心基础 1.css的基本语法在具体使用css之前,请各位兄弟姐妹先思考一个生活中的问题,一般情况下我们是如何描述一个人的呢? 小明{ 民族:汉族: 性格:温柔: 性别:男: 体重:68kg ...

  8. Servlet---JavaWeb技术的核心基础,JavaWeb框架的基石(一)

    初学JavaWeb开发,请远离各种框架,从Servlet开始.         Web框架是开发者在使用某种语言编写Web应用服务端是关于架构的最佳实践.很多Web框架是从实际的Web项目抽取出来的, ...

  9. CSS 设计彻底研究(一)(X)HTML与CSS核心基础

    第1章 (X)HTML与CSS核心基础 这一章重点介绍了4个方面的问题.先介绍了 HTML和XHTML的发展历程以及需要注意的问题,然后介绍了如何将CSS引入HTML,接着讲解了CSS的各种选择器,及 ...

随机推荐

  1. [Swift]LeetCode843. 猜猜这个单词 | Guess the Word

    This problem is an interactive problem new to the LeetCode platform. We are given a word list of uni ...

  2. 看完Andoird9.0 Pie的隐藏特性,我买了SSL证书

    今年 8 月,Google 正式公布了 Android 9.0 ,新的甜点名称也正式揭晓——Pie.这次的大版本升级中,藏着一个不起眼的特性:默认使用 HTTPS 为了将所有网络流量从明文(未加密的 ...

  3. nginx切割日志脚本

    nginx切割日志脚本 #!/bin/bash #cut nginx log #2018年9月26日14:26:44 #by jiajiezhao ########################## ...

  4. ThreadPoolExecutor线程池任务执行失败的时候会怎样

    接上一篇 <JDK1.8中的线程池> 1.  任务执行失败时的处理逻辑 1.1.  Worker Worker相当于线程池中的线程 可以看到,Worker有几个重要的属性: thread ...

  5. IdentityServer4实战 - 谈谈 JWT 的安全策略

    一.前言 众所周知,IdentityServer4 默认支持两种类型的 Token,一种是 Reference Token,一种是 JWT Token .前者的特点是 Token 的有效与否是由 To ...

  6. 【Android】打开本地的html文件

    网上好多说法 但实际上说到点上的没有 不想写太长 直接进入正题 Intent intent = new Intent(Intent.ACTION_VIEW); intent.addCategory(I ...

  7. shell实战之tomcat看门狗

    1.脚本简介 tomcat看门狗,在tomcat进程异常退出时会自动拉起tomcat进程并记录tomcat运行的日志. 函数说明: log_info:打印日志的函数,入参为需要在日志中打印的msg s ...

  8. J2EE规范总结

    概述 J2ee是我们步入java学习的一个開始.它将开启这趟奇幻之旅,Java是一种简单的,跨平台的,面向对象的,分布式的.解释的.健壮的安全的.结构的中立的,可移植的.性能非常优异的多线程的,动态的 ...

  9. 强大的数据库工具 dbForge Studio ForMySql

    优点: 1.可以将MySql数据库操作仿 sqlserver 的操作方式,便于操作 2.强大的比较拷贝能力.菜单栏上的 Comparison 的功能,可以比较两个数据库的差别,同时可以将数据库Copy ...

  10. 从零开始学安全(三十六)●利用python 爆破form表单

    import sys import requests from requests.auth import HTTPBasicAuth def Brute_Force_Web(postData): re ...