C#动静结合编程之一: 接口和委托的约束强度
程序世界有两种神秘的元素,它们无处不在,却常常未被察觉。它们一动一静,却又和谐相处。我给这对兄弟取上不太恰当的名字,一个叫“协议”,一个叫“约束”。我们常常看到的动态语言、静态语言背后,本质上就是“协议”与“约束”两种元素的作用。
C#是一门优美且处于快速发展中的语言,它融合了静态和动态的优势,如果运用得当,必能动静结合,呈现出一种和谐之美。本篇是C#动静结合编程的第一篇,希望这个系列能和大家一起探讨如何在C#中最大限度的发挥动静结合的潜能。
本人还只是C#初学者,对计算机理论的理解还很肤浅,文中错误欢迎批评指正,不足之处欢迎补充,谢谢!
PS:不加特殊说明时,本文提到的"动态"或“协议”是指程序的运行时协议,包括但不限于动态类型;“静态”或“约束”是指程序的编译时语法约束,而语义约束归为协议一类。
被忽略的协议
谈到“协议”,最先浮现在我们脑海中的可能是TCP/IP协议栈,但其实我们随处都在和协议打交道。下面的例子,你看出协议来了吗?
ArrayList lst = A();
foreach (string item in lst){
Console.WriteLine(item.Length);
}
}
方法B假定方法A遵守:返回的ArrayList内部都是string类型的元素。这就是它们之间的协议,这个协议不受编译器静态检查的约束。所以,协议意味着运行时的不确定性,方法A完全可能在返回结果中装入非string类型的元素,而这将导致B在运行时产生异常。
.NET2.0通过泛型集合增加了静态类型约束:
List<string> lst = A();
foreach (string item in lst){
Console.WriteLine(item);
}
}
约束有强弱
约束有强弱之分。越强的约束越安全,静态性越强,受编译器的支持越大;反之,越弱的约束,动态性越强,运行时灵活性越大。
接口和委托都是用于分离规范与实现从而达到面向抽象编程的重要手段,二者有共通之处,尤其单方法接口和委托更是如此。常常看到关于单方法接口和委托异同的讨论,不少朋友认为它们完全等价。其实,它们有明显不同的语法约束强度。接口是静态类型约束,而委托只是静态签名约束,二者的强度完全不同。换句话说,委托具有更多的协议性,只要是符合签名的方法或匿名委托,都可以被委托调用,而能被接口调用的对象必须实现该接口。
来看一个例子:需要编写一个类A,其内部需要日志功能;A采用IoC方式,不依赖于具体的Logger类,由使用者根据需要注入具体的实现;同时,A的使用者B,希望采用第三方的Logger类。
a. 基于接口的IoC
class A{
ILogger Logger { get; set; }
void F() {}
}
class B{
G(){
A a = new A();
a.Logger = new LogAdapter(); //注入依赖
a.F();
}
}
//对第3方Logger进行包装
class LogAdapter : ILogger{
Write(string msg){//这里调用第3方的Logger类}
}
b. 基于委托的IoC
Action<string> Logging { get; set; }
void F() {}
}
class B{
G(){
A a = new A();
a.Logging = delegate(string msg){ //调用第3方Logger类 };
a.F();
}
}
比较上面两个例子,我们就会发现委托比接口的约束要弱得多,使用起来灵活得多。基于接口的实现不得不增加一个Adapter去机械地适应接口的类型约束,而基于委托的实现只需要保证方法签名约束即可。
上面讨论接口与委托的语法约束强度,并不是对接口和委托的全方位比较,这里是希望大家能体会两种不同的OO多态哲学:接口的哲学是你继承什么你就是什么,委托的哲学是你能做什么你就是什么。这两种哲学反映在C#中就表现出接口的继承式约束的严谨但不灵活的静态特性,委托的灵活但不那么严谨的动态特性。这一点体悟对理解Duck Typing有很大的帮助。
后续
后续章节打算陆续介绍DuckTyping、泛型委托妙用、表达式树和lambda、C#4.0动态编程等相关内容,敬请关注,多谢批评!
修改历史:
1. 2009-03-11 初版
2. 2009-03-15 标题修改为“C#动静结合编程”;
根据读者点评,补充说明“动态”和“静态”的具体指什么;
补充对接口和委托的说明
posted on 2009-03-11 12:53 Todd Wei 阅读(3079) 评论(10) 编辑 收藏 举报