C#动静结合编程之三:Duck Typing

中庸

C#是静态类型语言,要使用类型必须引用该类型的定义。因此,从软件组织角度会发生组件间的引用依赖关系。常见的引用依赖关系有两种模式:

a. 正向依赖:组件A用到了组件B中定义的类T,组件A直接引用组件B,依赖关系是“组件A -> 组件B”。

b. 反向依赖:组件A通过接口I定义功能规范,针对抽象编程;组件B反过来引用组件A,并定义类T实现接口I;由另一组件C将I与T粘合起来,依赖关系是“组件A <- 组件B”。这就是著名的IoC方式。

简单说来,IoC是“谁制定规范,谁就拥有控制权;谁执行规范,谁就被控制”。如果规范借助于C#的静态类型检查,比如接口或抽象类,那么规范就表现出较强的语法约束性,使得组件A的编写比较独立,而组件B则受制与组件A。

本系列的第一篇举了一个基于接口的IoC例子,我们看到当需要采用第三方组件时,为了适用接口的静态类型约束,不得不增加一个adapter去实现接口并包装对第三方组件的调用。这表现出基于接口的IoC在粘合规范与实现时不太灵活。

但是,规范和类型约束没有必然的联系。在基于委托的IoC例子中,我们不需要任何的adapter,就能轻松的粘合规范与实现,表现出较强的灵活性。这就是通过委托定义规范,不会造成组件B对组件A的依赖,组件A和组件B的实现都显得比较独立。

实际上,我们还可以有比委托更灵活的规范表达方式,比如:通过HTTP + XML来表达规范,这样甚至是语言无关的,完全可能组件A由C#编写,组件B由Java编写。

上面列举的3种规范定义方式:基于接口、基于委托、基于HTTP + XML分别代表了由约束到协议,由严格到灵活的3种风格。当然,还有更多的方式,但这里只列举这三种作为代表。动与静之间需要把握一个分寸,接口过于死板;而HTTP + XML的方式则完全是基于运行时协议的,需要自己做很多检查工作;委托的好处在于既消除了组件A、B的依赖关系,又能享受IDE智能提示和编译器检查(签名检查)等好处。因此,委托是把动与静结合得恰到好处的中庸之道。

Duck Typing

但可惜委托还无法覆盖接口或类的所有功能,有朋友提到“接口是对象功能的抽象,而委托是方法功能的抽象”就是这个意思。那么我们自然会想,有没有一种方式,能将委托的思想应用于对象呢?有!它就是:duck typing。前文已经谈到,duck typing关注“对象能做什么”或者说“如何使用对象”,对象继承自什么类,或者实现什么接口并不重要。duck typing的本意为“如果一只动物,走起来像鸭子,叫起来像鸭子,我就可以把它当作鸭子”。与继承性多态相对应,duck typing可以实现非继承多态。按duck typing的本意,那么更纯正的duck typing看起来应该是这个样子:

static void Main(string[] args)

{

    object person= new Person();

    IPerson duck= Duck.Create<IPerson>(person);//创建鸭子对象

    Console.WriteLine(duck.Name + " will be " + (duck.Age + 1) + "next year");

    duck.Play("basketball");

    Console.WriteLine(duck.Mother);//为null

    //duck无法调用duck.Sing()

}

interface IPerson
{
    string Name { get; }
    int Age { get; }
    string Mother { get; }
    void Play(string ball);
}

class Person
{
    public string Name { get { return "Todd"; } }
    public int Age { get { return 26; } }

    public void Play(string ball) { Console.WriteLine("Play " + ball); }

    public void Sing(string song) { Console.WriteLine("Sing " + song");}

}

上面的例子中,虽然person对象没有实现IPerson接口,我们一样可以通过Duck.Create<IPerson>(person)创建鸭子对象调用person的属性和方法。这种把接口和对象粘合的方式与委托和方法的粘合方式非常接近,真正达到了我们所谓把委托思想应用于对象的想法。

C#中要实现Duck.Create<T>的功能,可以通过Emit动态创建实现T接口的代理类,在代理类中拦截方法调用,并将方法调用转换成target对象上的反射调用。Castle开源项目的DynamicProxy是一个很好用的工具,在它的帮助下很容易实现代理类的创建和方法调用的拦截。

时间: 2024-08-04 05:03:12

C#动静结合编程之三:Duck Typing的相关文章

实战JSP进阶编程之三:在Tomcat下配置Hibernate的开发环境

这是实战JSP进阶编程之三. 今天花了几个小时,终于将机房里面的Tomcat+Hibernate的开发.学习环境配置好了. 应用场景:Tomcat 5.5, Hibernate 2.1.7, Mysql 3.23.43, Mysql Driver:3.0.14, JDK: 1.4.2 OS: TurboLinux Server 8.0 用户环境:普通学生帐户--j2ee,位置: /home/j2ee/public_html 为了方便初学者,本教程特意作了简化处理. 1.将hibernate2.j

C#动静结合编程之一: 接口和委托的约束强度

程序世界有两种神秘的元素,它们无处不在,却常常未被察觉.它们一动一静,却又和谐相处.我给 这对兄弟取上不太恰当的名字,一个叫"协议",一个叫"约束".我们常常看到的动态语言.静态语言 背后,本质上就是"协议"与"约束"两种元素的作用.静态语言和动态语言本身没有一个明确的界限, 它们各有优势,又各有不足. C#是一门优美的语言,它融合了静态和动态的优势,如果运用得当,必能动静结合,呈现出一种和谐 之美.特别是.NET平台和语言的

iOS多线程编程之三——GCD的应用

iOS多线程编程之三--GCD的应用 一.引言 在软件开发中使用多线程可以大大的提升用户体验度,增加工作效率.iOS系统中提供了多种分线程编程的方法,在前两篇博客都有提及: NSThread类进行多线程编程:http://my.oschina.net/u/2340880/blog/416524. NSOperation进行多线程操作编程:http://my.oschina.net/u/2340880/blog/416782. 上两个进行多线程编程的机制都是封装于Object-C的类与方法.这篇博

iOS网络编程之三——NSURLConnection的简单使用

iOS网络编程之三--NSURLConnection的简单使用 一.引言     在iOS7后,NSURLSession基本代替了NSURLConnection进行网络开发,在iOS9后,NSURLConnection相关方法被完全的弃用,iOS系统有向下兼容的特性,尽管NSURLConnection已经被弃用,但在开发中,其方法依然可以被使用,并且如果需要兼容到很低版本的iOS系统,有时就必须使用NSURLConnection类了. 二.使用NSURLConnection进行同步请求     

C语言控制台窗口图形界面编程之三 设置文本属性的函数

在这里介绍一个设置文本属性的函数,原型如下 BOOL SetConsoleTextAttribute( // 设置WriteConsole等函数的字符属性 HANDLE hConsoleOutput, // 句柄 WORD wAttributes // 文本属性 ); 本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/C/ 顺便提一下文本属性,其实就是颜色属性,有背景色和前景色(就是字符的颜色)两类,每一类只提供

javascript面向对象编程之三 function是方法(函数)

在进行编程时,必免不了要碰到复杂的功能.初学者最怕复杂的功能,因为不能够很好的进行功能边界划分,只能一大串if.循环加case堆叠在一起,结果出来的程序自己看着晕,别人看着更晕.好程序不是写给computer的,而是写给human的.遇到复杂功能,应该想着把它简化.组件化,把小功能封装成小组件,小功能块可以任意的组合得到千变万化的复杂功能.function就可以帮助我们把功能进行封装.那什么是封装呢.要我说,只要把具体实现给打包,对外提供调用接口那就是封装,方法也好.类也好就做了这些事. jav

多线程编程之三——线程间通讯

七.线程间通讯 一般而言,应用程序中的一个次要线程总是为主线程执行特定的任务,这样,主线程和次要线程间必定有一个信息传递的渠道,也就是主线程和次要线程间要进行通信.这种线程间的通信不但是难以避免的,而且在多线程编程中也是复杂和频繁的,下面将进行说明. 使用全局变量进行通信 由于属于同一个进程的各个线程共享操作系统分配该进程的资源,故解决线程间通信最简单的一种方法是使用全局变量.对于标准类型的全局变量,我们建议使用volatile 修饰符,它告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄

Haskell函数式编程之三-纯函数式编程特点

函数式编程的定义是: In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids stateand mutable data. 即:函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了引入状态及可变数据. 它更强调函数的应用,而不像命令式编

C#动静结合编程之四:泛型委托

多态 多态是什么?一句话:接口和实现的1:n映射.多态让程序能通过统一的接口(广义的接口,意指规范 )调用不同的实现,从而增强程序的表达能力和灵活性.我们最为熟悉的多态形式是包括接口继承在内的 类型多态: var animals = new List<IAnimal>() { new Cat("Missy"), new Cat("Mr. Bojangles"), new Dog("Lassie") }; foreach (var an