一起谈.NET技术,.NET中的委托

简介:   1.1.1 定义  委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,如下面的示例所示://Code in C#  public delegate int PerformCalculation(int x, int y);  与委托的签名(由返回类型和参数组成)匹配的任何方法都可以分配给该委托。

  1.1.1 定义

  委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,如下面的示例所示:

 
 
// Code in C#

  
public delegate int PerformCalculation( int x, int y);

  与委托的签名(由返回类型和参数组成)匹配的任何方法都可以分配给该委托。

  简单理解Delegate委托(或代理)是一种数据类型:它的变量可以引用到某一个符合要求的方法上,通过委托可以间接地调用该方法。

  其实.NET的委托类似于C语言的函数指针,区别在于.NET委托是类型安全的,这说明,C中的函数指针只不过是一个指向存储单元的指针,我们无法说出这个指针实际指向什么。

  1.1.2 委托使用

  • 使用委托的四部曲:
  • 定义一种委托类型
  • 委托执行时要调用方法
  • 定义一个委托实例
  • 委托实例的调用

  我们先定义一种委托类型如下:

 
 
// 自定义一种委托类型

public delegate void StringProcessor( string input);

然后我们再定义5中候选的委托方法如下:

void PrintString( string x)

void PrintInteger( int x)

void PrintTwoStrings( string x, string y)

int GetStringLength( string x)

void PrintObject( object x)

  大家猜猜看哪个和上面提供的委托类型签名匹配(签名匹配:参数类型,参数个数和返回类型匹配)。激动时刻到了马上公布答案,和委托类型匹配的方法是PrintString和PrintObject,如果有不明白的请细细考虑一下委托匹配的条件—签名匹配。

                                     

图1委托成功输出

 

  现在对委托有了一定的认识,接下来我们将介绍委托最经常使用的地方—事件。

  我们将从发送器和接受器的角度讨论事件,例如在UI编程中,鼠标单击或键盘按键,发送器就是.NET的CLR,注意事件发送器并不知道接收器是谁,这符合面向对象的原则,而且某个事件接收器有个方法处理该事件,这个时候就要委托,如前面所讲事件发送器对事件接收器一无所知,通过委托作为一个中介,接收器把事件处理方法注册到事件中,这样就实现了由发送器->委托->接收器的过程了。

  我们可以这样认为:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

  1.1.3 自定义委托

  前面话有点难以理解,接下来我们通过具体的例子分析一下何谓委托,该如何实现委托。现在不是很喜欢搞多国语言化的吗?看看如何让我们的程序会说多种语言吧!

 
 
/// <summary>
/// the English speaker.
/// </summary>
/// <param name="name"> The name. </param>
public void EnglishSpeaker( string name)
{
Console.WriteLine(
string .Format( " Hello my name is {0} and I am English speaker.\n " , name));
}

/// <summary>
/// the Chineses speaker.
/// </summary>
public void ChineseSpeaker(string name)
{
Console.WriteLine(
string .Format( " 您好我的名字叫{0},我是讲普通话的。\n " , name));
}

  好啦现在我们有两个方法分别是说普通话和英语,现在我们的程序会说普通话和英语啦。现在我们考虑究竟什么时候讲普通话什么时候讲英语,那不简单我们加个判断就OK啦,是的我们可以通过switch或者if else就可以实现啦。

 
 
/// <summary>
/// 根据上下文调用不同的方法
/// </summary>
/// <param name="name"> string </param>
/// <param name="lang"> enum </param>
private static void Say( string name, Language lang)
{
switch (lang)
{
case Language.Chinese:
Program.ChineseSpeaker(name);
break ;
case Language.English:
Program.EnglishSpeaker(name);
break ;
default :
break ;
}
}

  但假设我们现在又要增加新的语言西班牙语,同样我们可以增加西班牙语,但我们必须修改switch语句增加判断,这不符合OOP中的OCP(对扩展开放,对修改关闭原则),这时候委托该登场。

 
 
/// <summary>
/// Define speak delegate.
/// </summary>
/// <param name="name"></param>
private delegate void SpeakDelegate( string name);

  首先我们定义了一种委托类型SpeakDelegate,然后我们通过修改Say方法看看该如何使用委托变量。

 
 
/// <summary>
/// The base say function.
/// </summary>
/// <param name="name"> The name. </param>
/// <param name="speaker"> The speaker. </param>
private static void Say( string name, SpeakDelegate speaker)
{
/// Inoke the speaker function.
speaker(name);
}

  现在我们的参数已经不是枚举类型了,而是一个委托类型变量,而且实现的具体代码也没有了switch语句了,比之前简单了许多。现在大家知道如何去调用Say方法吧!没错我们只需传递一个name和一个具体实现函数名就OK了。

 
 
/// 传递函数名进行委托方法绑定
Program.Say( " 钧航 " , ChineseSpeaker);
Program.Say(
" JK.Rush " , EnglishSpeaker);

  自定义委托相信大家都会了,接下来我将介绍一下.NET中委托实现,由于许多使用委托的例子都是事件,所以下面的例子也采用事件。但请大家要注意“可以使用委托,但却没有定义事件”的情况(例如:回调函数)。

  1.1.4 .NET中的事件委托

  举一个简单的例子,.NET中经常使用的控件Button,当我们把Button 控件 drap and drop到界面,然后双击界面的Button我们发现程序中自动生成了一个响应Button的事件方法,然后我们给事件方法添加Code之后,当我们点击该Button就响应该方法了,但我们没有看到代码中有任何的委托和事件之类的定义,其实这些.NET都已经做好了。我们可以查看如下文件。

              图2事件委托实现

  如上图所示我们打开Designer文件,事件委托的实现都在这里实现了。

  其中,EventHandler就是一个代理类型,可以认为它是一个“类”,是所有返回类型为void,具备两个参数分别是object sender和EventArgs e,第一个参数表示引发事件的控件,或者说它表示点击的那个按钮。通过以下的代码我们细细解析一下。

 
 
private void button1_Click( object sender, EventArgs e)
{
// 获取被点击Button的实例
Button objBotton = sender as Button;
if (objBotton != null )
{
objBotton.Text
= " Hello you click me. " ;
objBotton.AutoSize
= true ;
}
else
{
// Exception Handle.
}
}

图3点击产生效果

  OK现在明白了sender就是传递一个被点击对象的实例,第二个参数名叫e的EventArgs参数,用于      表示附加的事件关联的事件信息。当点击按钮时,没有附加任何关联的事件信息,如上的点击事件,第二参数并不表示任何有用的信息。但什么时候会用到呢?

  我们先介绍一下EventArgs这个的类型。其实这个类并没有太多的功能,它主要是作为一个基类让其他类去实现具体的功能和定义,当我们搜索EventArgs发现很多类是继承于它的。

 
 
public class EventArgs
{
// Fields
public static readonly EventArgs Empty;

// Methods
static EventArgs();
public EventArgs();
}

  举其中的ImageClickEventArgs为例,它继承于EventArgs,而且还添加了自己的字段用来基类X和Y的坐标值(这是一个ImageButton被点击时候响应的),然后获取该按钮的X和Y坐标。

 
 
public sealed class ImageClickEventArgs : EventArgs
{
// Fields
public int X;
public int Y;

// Methods
public ImageClickEventArgs( int x, int y)
{
this .X = x;
this .Y = y;
}
}
 
 
// ImageButton点击响应时间
protected void ibtnTest_Click( object sender, ImageClickEventArgs e)
{
this .lblCX.Text = e.X.ToString();
this .lblCY.Text = e.Y.ToString();
}

图4获取ImageClickEventArgs关联点击坐标

  前面提到其他事件关联信息类型都是通过继承EventArgs实现的,所以说我们自己也可以自定义一个事件关联信息类型,如下我们只需继承EventArgs就OK了。

 
 
/// <summary>
/// 自定义事件关联类
/// </summary>
public class ColorChangedEventArgs : EventArgs
{
private Color color;

/// <summary>
/// Initializes a new instance of the <see cref="ColorChangedEventArgs"/> class.
/// </summary>
/// <param name="c"> The c. </param>
public ColorChangedEventArgs(Color c)
{
color
= c;
}

/// <summary>
/// Gets the color of the get.
/// </summary>
/// <value>
/// The color of the get.
/// </value>
public Color GetColor
{
get { return color; }
}

}

  1.1.5自定义事件委托

  多播委托

  前面使用的每个委托都只包含一个方法调用。调用一个委托就调用一个方法调用。如果要通过一个委托调用多个方法,那就需要使用委托的多播特性。如果调用多播委托,就可以按委托添加次序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果,接下来看看多播实现。

 
 
namespace Multi_Delegate
{
delegate void StringProcessor();
public class Person
{
private string _Name;
public Person( string name)
{
this ._Name = name;
}

public void Say()
{
Console.WriteLine(
" Hello my name is {0}, what's your name.\n " , this ._Name);
}

public void Reply()
{
Console.WriteLine(
" Hello my name is {0} and nice to meet you.\n " , this ._Name);
}
}

class Program
{
static void Main( string [] args)
{
Person Jack
= new Person( " Jack " );
Person Oliver
= new Person( " Oliver " );
StringProcessor sp
= null ;
// 绑定多播方法调用
sp += Jack.Say;
sp
+= Oliver.Reply;
sp();
Console.ReadKey();
}
}
}

  也许有人觉得很简单,实现的确简单明了,就是通过“+”把方法调用绑定到委托变量中,如果我们用“-”就可以移除绑定到委托变量方法了。

   事件

  前面一直没有解释什么是事件,现在让我用一句话解释事件和委托的关系吧!

  事件和委托关系就像是属性和字段的关系,为了刚好的实现OOP的编程原则,事件对委托进行了封装。

  现在我们修改前面的代码,使用事件对委托进行封装。

 
 
/// 使用事件对委托进行封装
/// </summary>
public class Say
{
/// <summary>
/// 封装委托字段
/// </summary>
public static event SpeakDelegate speakDelegate;

/// <summary>
/// 调用委托具体实现方法
/// </summary>
/// <param name="name"></param>
public static void SayManager( string name)
{
speakDelegate(name);
}
}


/// <summary>
/// 客户端调用委托
/// </summary>
/// <param name="args"></param>
static void Main( string [] args)
{
Say.speakDelegate
+= Program.ChineseSpeaker;
Say.speakDelegate
+= Program.EnglishSpeaker;
Say.SayManager(
" Jackson " );
Console.ReadKey();
}

图5自定义委托

  现在让我们看看编译后Say类就可以充分证明我们的结论:事件是对委托封装。

图6自定义事件编译后的代码

  大家看到在编译后的代码中出现了一个私有的委托变量,然后接下是一个公用事件委托变量,这进一步说明了事件是对委托的封装。

图7自定义事件编译后MSIL代码

  1.1.6事件委托实现观察者模式

  前面我们介绍按钮事件响应是从发送者和接收者的角度出发的,现在我们以设计模式中的观察者模式为例。

图8GoF观察者架构

 
 
namespace GoFObserver
{
/// <summary>
/// 充当Subject角色
/// </summary>
public class GofTelecom
{
public delegate void GofNews();
public static event GofNews NewEvent;

/// <summary>
/// 发布通知方法
/// </summary>
/// <returns></returns>
public static bool Notify()
{
if (NewEvent != null )
{
NewEvent();
return false ;
}
return true ;
}
}

public interface IObserver
{
void Update();
}

/// <summary>
/// 观察者
/// </summary>
public class Programmer : IObserver
{

#region IObserver 成员

public void Update()
{
Console.WriteLine(
" I am a greenhand programmer.\n " );
}

#endregion

}

/// <summary>
/// 观察者
/// </summary>
public class Architect : IObserver
{
#region IObserver 成员

public void Update()
{
Console.WriteLine(
" OH...I am a top banana.\n " );
}

#endregion
}



public class Program
{
static void Main( string [] args)
{
IList
< IObserver > objObserver = new List < IObserver > ();
objObserver.Add(
new Programmer());
objObserver.Add(
new Architect());
foreach (IObserver ob in objObserver)
{
GofTelecom.NewEvent
+= ob.Update;
}

if ( ! GofTelecom.Notify())
{
Console.WriteLine(
" Notify successful.\n " );
}
else
{
Console.WriteLine(
" Notify failed.\n " );
}
Console.ReadKey();
}
}
}
目录
相关文章
|
28天前
|
自然语言处理 物联网 图形学
.NET 技术凭借其独特的优势和特性,为开发者们提供了一种高效、可靠且富有创造力的开发体验
本文深入探讨了.NET技术的独特优势及其在多个领域的应用,包括企业级应用、Web应用、桌面应用、移动应用和游戏开发。通过强大的工具集、高效的代码管理、跨平台支持及稳定的性能,.NET为开发者提供了高效、可靠的开发体验,并面对技术更新和竞争压力,不断创新发展。
45 7
|
27天前
|
开发框架 安全 .NET
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱。它不仅加速了应用开发进程,提升了开发质量和可靠性,还促进了创新和业务发展,培养了专业人才和技术社区,为软件开发和数字化转型做出了重要贡献。
24 5
|
27天前
|
传感器 人工智能 供应链
.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。
本文深入探讨了.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。通过企业级应用、Web应用及移动应用的创新案例,展示了.NET在各领域的广泛应用和巨大潜力。展望未来,.NET将与新兴技术深度融合,拓展跨平台开发,推动云原生应用发展,持续创新。
28 4
|
27天前
|
开发框架 .NET C#
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位。从企业应用到电子商务,再到移动开发,.NET 均展现出卓越性能,助力开发者提升效率与项目质量,推动行业持续发展。
27 4
|
28天前
|
机器学习/深度学习 人工智能 物联网
.NET 技术:引领未来开发潮流
.NET 技术以其跨平台兼容性、高效的开发体验、强大的性能表现和安全可靠的架构,成为引领未来开发潮流的重要力量。本文深入探讨了 .NET 的核心优势与特点,及其在企业级应用、移动开发、云计算、人工智能等领域的广泛应用,展示了其卓越的应用价值和未来发展前景。
57 5
|
27天前
|
机器学习/深度学习 人工智能 Cloud Native
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台。本文深入解析 .NET 的核心优势,探讨其在企业级应用、Web 开发及移动应用等领域的应用案例,并展望未来在人工智能、云原生等方面的发展趋势。
32 3
|
27天前
|
敏捷开发 缓存 中间件
.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素
本文深入探讨了.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素,并通过企业级应用和Web应用开发的实践案例,展示了如何在实际项目中应用这些模式,旨在为开发者提供有益的参考和指导。
23 3
|
27天前
|
开发框架 安全 Java
.NET技术的独特魅力与优势,涵盖高效的开发体验、强大的性能表现、高度的可扩展性及丰富的生态系统等方面,展示了其在软件开发领域的核心竞争力
本文深入探讨了.NET技术的独特魅力与优势,涵盖高效的开发体验、强大的性能表现、高度的可扩展性及丰富的生态系统等方面,展示了其在软件开发领域的核心竞争力。.NET不仅支持跨平台开发,具备出色的安全性和稳定性,还能与多种技术无缝集成,为企业级应用提供全面支持。
29 3
|
1月前
|
人工智能 开发框架 前端开发
C#/.NET/.NET Core技术前沿周刊 | 第 12 期(2024年11.01-11.10)
C#/.NET/.NET Core技术前沿周刊 | 第 12 期(2024年11.01-11.10)
|
29天前
|
人工智能 开发框架 安全
C#/.NET/.NET Core技术前沿周刊 | 第 13 期(2024年11.11-11.17)
C#/.NET/.NET Core技术前沿周刊 | 第 13 期(2024年11.11-11.17)