本文是博客园中近日关于C#委托的讨论所衍生出来的一个系列中的第一篇,文章作者麒麟.NET对C#委托的内涵和外延进行了讨论。用麒麟.NET的话来说:“委托是.NET Framework中一个非常炫的特性,绝不会向有些评论里说的那样,根本没有机会接触。恰恰相反,我们几乎每天都会接触委托,使用委托。”
那么,先从示例开始,了解什么是C#委托吧。
从示例开始
假设一个系统的用户登录模块有如下所示的代码
- classUser
- {
-
publicstringName{get;set;}
-
publicstringPassword{get;set;}
- }
-
classUserService
- {
-
publicvoidRegister(Useruser)
- {
-
if(user.Name=="Kirin")
- {
-
Log("注册失败,已经包含名为"+user.Name+"的用户");
- }
-
else
- {
-
Log("注册成功!");
- }
- }
-
privtevoidLog(stringmessage)
- {
- Console.WriteLine(message);
- }
- }
UserService类封装用户登录的逻辑,并根据不同的登录情况向控制台打印不同的日志内容。当程序关闭时,所记录的日志自然也随之消失。
客户端的代码为
- classProgram
- {
-
staticvoidMain(string[]args)
- {
-
Useruser=newUser{Name="Kirin",Password="123"};
-
UserServiceservice=newUserService();
- service.Register(user);
- Console.ReadLine();
- }
- }
使用策略模式
然而这样的设计肯定是无法满足用户的需求的,用户肯定希望能够查看以前的日志记录,而不仅仅是程序打开以后的内容。如果我们仅仅修改Log方法的实现,那么用户需求再次改变时我们该如何处理呢?难道要无休止地修改Log方法吗?
既然日志记录的方式是变化的根源,我们自然会想到将其进行封装。我们创建一个名为ILog的接口。
- interfaceILog
- {
-
voidLog(stringmessage);
- }
并创建两个实现了ILog的类,ConsoleLog和TextLog,分别用来向控制台和文本文件输出日志内容。
- classConsoleLog:ILog
- {
-
publicvoidLog(stringmessage)
- {
- Console.WriteLine(message);
- }
- }
-
-
classTextLog:ILog
- {
-
publicvoidLog(stringmessage)
- {
-
using(StreamWritersw=File.AppendText("log.txt"))
- {
- sw.WriteLine(message);
- sw.Flush();
- sw.Close();
- }
- }
- }
在UserService类中添加一个ILog类型的属性LogStrategy。
- classUserService
- {
-
publicILogLogStrategy{get;set;}
-
publicUserService()
- {
-
LogStrategy=newConsoleLog();
- }
-
publicvoidRegister(Useruser)
- {
-
if(user.Name=="Kirin")
- {
-
LogStrategy.Log("注册失败,已经包含名为"+user.Name+"的用户");
- }
-
else
- {
-
LogStrategy.Log("注册成功!");
- }
- }
- }
客户端代码变为如下形式。
- classProgram
- {
-
staticvoidMain(string[]args)
- {
-
Useruser=newUser{Name="Kirin",Password="123"};
-
UserServiceservice=newUserService{LogStrategy=newTextLog()};
- service.Register(user);
- Console.ReadLine();
- }
- }
在声明UserService的时候,还可以将将LogStrategy设置为TextLog。这样在UserService进行逻辑处理时,使用的LogStrategy即为TextLog,日志将输出到文本文件中。
我们在干什么?我们在重构。重构的结果是什么?重构的结果是实现了一个简单的策略模式。
使用委托
然而策略模式仍然不能满足客户的需求,这是为什么呢?
1. 用户也许会希望自定义Log的实现。当然,你可以通过在客户代码处扩展ILog来实现自己的日志记录方式。如
- classTextBoxLog:ILog
- {
-
privateTextBoxtextBox;
-
publicTextBoxLog(TextBoxtextBox)
- {
-
this.textBox=textBox;
-
this.textBox.Multiline=true;
- }
-
publicvoidLog(stringmessage)
- {
- textBox.AppendText(message);
- textBox.AppendText(Environment.NewLine);
- }
- }
但这种方案是否过于复杂呢?如果用户希望在ListView或其他控件上显示,是否需要逐个创建新类呢?并且这样的实现是否与客户端的耦合过于紧密呢?比如用户希望在ListView的各个列中显示日志内容、时间、来源等不同内容,那么在ListViewLog中对ListView硬编码是否很难重用呢?
2. 用户也许会希望同时使用多种日志记录方式。比如,同时向控制台、文本文件、客户端控件和事件查看器中输出日志。你当然可以在UserService中维护一个List<ILog>,但这时UserService的职责过多,显然违反了SRP。
下面介绍本文的主角:委托。
我们首先来创建一个名为Log的委托,它接收一个string类型的参数。
- publicdelegatevoidLog(stringmessage);
然后在UserService类中添加一个Log委托类型的属性LogDelegate。
- classUserService
- {
-
publicLogLogDelegate{get;set;}
-
- }
在客户端,我们直接声明两个静态方法,它们都包含一个string类型的参数,并且没有返回值。
- staticvoidLogToConsole(stringmessage)
- {
- Console.WriteLine(message);
- }
-
staticvoidLogToTextFile(stringmessage)
- {
-
using(StreamWritersw=File.AppendText("log.txt"))
- {
- sw.WriteLine(message);
- sw.Flush();
- sw.Close();
- }
- }
客户端声明UserService的代码变为
- staticvoidMain(string[]args)
- {
-
Useruser=newUser{Name="Kirin",Password="123"};
-
UserServiceservice=newUserService();
- service.LogDelegate=LogToConsole;
- service.LogDelegate+=LogToTextFile;
- service.Register(user);
-
- Console.ReadLine();
- }
在构造委托时,我们还可以使用匿名方法和Lambda表达式,在老赵的文章中详细阐述了这些写法的演变。
对于何时使用委托,何时使用接口(即策略模式),MSDN中有明确的描述:
在以下情况下,请使用委托:
◆当使用事件设计模式时。
◆当封装静态方法可取时。
◆当调用方不需要访问实现该方法的对象中的其他属性、方法或接口时。
◆需要方便的组合。
◆当类可能需要该方法的多个实现时。
在以下情况下,请使用接口:
◆当存在一组可能被调用的相关方法时。
◆当类只需要方法的单个实现时。
◆当使用接口的类想要将该接口强制转换为其他接口或类类型时。
◆当正在实现的方法链接到类的类型或标识时:例如比较方法。
您可能觉得上面的例子阐述委托和接口有些过于牵强,事实上有些时候的确很难选择使用接口还是委托。Java中没有委托,但所有委托适用的情况同样可以使用包含单一方法的接口来实现的。在某种程度上,可以说委托是接口(仅定义了单一方法)的一种轻量级实现,它更灵活,也更方便。
以上就是对于C#委托中一些基本概念的介绍。
分享到:
相关推荐
C# C#语言基础和程序结构 数据类型:数组、结构和枚举 运算符、表达式和结构化程序 类的构造方法,继承,接口和抽象类 委托与事件 WindowsForm应用程序 XML操作等
泛型类、泛型方法、泛型接口、泛型委托 泛型类、泛型方法、泛型接口、泛型委托
c#接口、发射、委托 细细品味C#(抽象接口委托反射)
c#泛型类、泛型方法、泛型接口、泛型委托
C# 使用委托方式Usercontrol与主窗体进行消息传递
这是我整理的网上关于接口、委托、反射以及抽象类的资料。非常的经典。
接口,事件,委托,驱动监听 1.面向接口的驱动监听 2.面向委托的驱动监听 3.事件驱动监听 4.事件轮询 5.事件异步轮询
命名空间,转换,接口,委托,程序结构和数据类型,异常,枚举
细细品味C#(抽象接口委托反射)转自博客园,原帖地址如下: http://www.cnblogs.com/xia520pi/archive/2011/10/07/2200793.html
C# 抽象类 接口 委托 反射,通过大量实例与代码来详解各种在C#中难掌握的知识点
详细解说c#的基本语法,如委托,不安全代码等
此文件主要介绍了C#中类的应用、接口的使用、委托事件的等介绍
使用IComparable接口,IComparer接口实现排序功能
当然大家想到了传递接口方式来实现,咱先不讨论接口,因为微软为我们提供了一个叫做 【委托】 的类型。 (一)、委托基础: 1. 先看看代码: (1).定一个方法:void SayHi(string name){Console.WriteLine(“Hi~”+...
依次讲解了C#开发基础、搭建C#开发环境、变量与常量、复杂的变量类型和...基础、接口和类转换、C#字符串和正则表达式、C#委托和事件、C#泛型、C#文件操作和流文件操作、XML文件操作处理、C#的Windows编程基础、菜单、...
C# 委托15.doc C#变量5.doc C#表达式7.doc C#不安全代码18.doc C#程序结构和数据类型1.doc C#词法结构2.doc C#基本概念3.doc C#接口13.doc C#结构11.doc C#类10.doc C#类型4.doc C#枚举14.doc C#属性17.doc C#数组12...
抽象类可以包含抽象方法和抽象访问器。 不能用 sealed 修饰符修改抽象类,这意味着该类不能被继承。 从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实实现。 在方法或属性声明中使用 ...
C# 委托15.doc C#变量5.doc C#表达式7.doc C#不安全代码18.doc C#程序结构和数据类型1.doc C#词法结构2.doc C#基本概念3.doc C#接口13.doc C#结构11.doc C#类10.doc C#类型4.doc C#枚举14.doc C#属性17.doc C#数组12...
针对易盛北斗星系统封装的C#接口,接口与采用统一的委托形式,简化的开发,初始化完成后,开始交易或者行情,等待服务状态通知,当为Ready时,即表示准备完成,用户可以进行下撤改操作。所有回调均以委托形式回调给...
将C#代理和lambdas转换为Java接口和lambdas 使用Java泛型转换C#ref参数 当代码中定义事件委托时转换C#事件,或者是System.Action或System.Func代理之一 将所有类型的数组从C#转换成Java 对于继承和接口的所有...