Effective C#原则19:选择定义和实现接口而不是继承

抽象类在类的继承中提供了一个常规的“祖先”。一个接口描述 了一个可以被其它类型实现的原子级泛型功能。各有千秋,却也不尽相同。接口 是一种合约式设计:一个类型实现了某个接口的类型,就必须实现某些期望的方 法。抽象类则是为一个相关类的集合提供常规的抽象方法。这些都是老套的东西 了:它是这样的,继承就是说它是某物(is a,),而接口就是说它有某个功能 (behaves like.)! 这些陈词滥调已经说了好久了,因为它们提供了说明,同时 在两个结构上描述它们的不同:基类是描述对象是什么,接口描述对象有某种行为。

接口描述了一组功能集合,或者是一个合约。你可以在接口里创建 任何的占位元素(placeholder,译注:就是指先定义,后面再实现的一些内容) :方法,属性,索引器以及事件。任何实现类型这个接口的类型必须为接口里的 每个元素提供具体的内容。你必须实现所有的方法,提供全部属性访问器,索引 器,以及定义接口里的所有事件。你在接口里标记并且构造了可重用的行为。你 可以把接口当成参数或者返回值,你也可以有更多的机会重用代码,因为不同的 类型可以实现相同的接口。更多的是,比起从你创建的基类派生,开发人员可以 更容易的实现接口。(译注:不见得!)

你不能在接口里提供任何成员的具 体实现,无论是什么,接口里面都不能实现。并且接口也不能包含任何具体的数 据成员。你是在定义一个合约,所有实现接口的类型都应该实现的合约。

抽象的基类可以为派生类提供一些具体的实现,另外也描述了一些公共 的行为。你可以更详细的说明数据成员,具体方法,实现虚函数,属性,事件以 及索引器。一个基类可以只提供部份方法的实现,从而只提供一些公共的可重用 的具体实现。抽象类的元素可以是虚的,抽象的,或者是非虚的。一个抽象类可 以为具体的行为提供一个可行的实现,而接口则不行。

重用这些实现还 有另一个好处:如果你在基类中添加一个方法,所有派生类会自动隐式的增加了 这个方法。这就是说,基类提供了一个有效的方法,可以随时扩展几个(派生)类 型的行为:就是向基类添加并实现方法,所有派生类会立即具有这些行为。而向 一个接口添加一个方法,所会破坏所有原先实现了这个接口的类。这些类不会包 含新的方法,而且再也通不过编译。所有的实现者都必须更新,要添加新的方法 。

这两个模式可以混合并重用一些实现代码,同时还可以实现多个接口 。System.Collections.CollectionBase就是这样的一个例子,它个类提供了一 个基类。你可以用这个基类你的客户提供一些.Net缺少的安全集合。例如,它已 经为你实现了几个接口:IList, ICollection,和IEnumerable。另外,它提供了 一个受保护的方法,你可以重载它,从而为不同的使用情况提供自己定义的行为 。IList接口包含向集合中添加新对象的Insert()方法。想自己更好的提供一个 Insert方法的实现,你可以通过重载CollectionBase类的OnInsert()或 OnInsertCcomplete()虚方法来处理这些事件:

public class IntList : System.Collections.CollectionBase
{
 protected override void OnInsert( int index, object value )
 {
   try
  {
   int newValue = System.Convert.ToInt32( value );
   Console.WriteLine( "Inserting {0} at position {1} ",
    index.ToString(), value.ToString());
     Console.WriteLine( "List Contains {0} items",
     this.List.Count.ToString());
  }
  catch( FormatException e )
  {
   throw new ArgumentException (
    "Argument Type not an integer",
    "value", e );
  }
 }
 protected override void OnInsertComplete( int index,
  object value )
 {
  Console.WriteLine( "Inserted {0} at position {1}",
   index.ToString( ), value.ToString( ));
   Console.WriteLine( "List Contains {0} items",
    this.List.Count.ToString( ) );
 }
}
public class MainProgram
{
 public static void Main()
 {
   IntList l = new IntList();
  IList il = l as IList;
   il.Insert( 0,3 );
  il.Insert( 0, "This is bad" );
 }
}

时间: 2024-09-23 20:24:53

Effective C#原则19:选择定义和实现接口而不是继承的相关文章

Effective C#原则40:根据需求选择集合

"哪种集合是最好的?"答案是:"视情况而定." 不同的集合有不同的性能,而且在不同的行为上有不同的优化..Net框架支持很 多类似的集合:链表,数组,队列,栈,以及其它的一些集合.C#支持多维的数 组,它的性能与一维的数组和锯齿数组都有所不同..Net框架同样包含了很多特 殊的集合,在你创建你自己的集合类之前,请仔细参阅这些集合.你可以发现很 多集合很快,因为所有的集合都实现了ICollection接口.在说明文档中列出了 所有实现了ICollection接口的集合

Effective C#原则32:选择小而内聚的程序集

这一原则实际应该取这个名字:"应该创建大小合理而且包含少量公共 类型的程序集".但这太沉长了,所以就以我认为最常见的错误来命名: 开发人员总是把所有的东西,除了厨房里水沟以外(译注:夸张说法,kitchen sink可能是个口语词,没能查到是什么意思,所以就直译了.),都放到一个程 序集.这不利于重用其中的组件,也不利于系统中小部份的更新.很多以二进制 组件形式存在的小程序集可以让这些都变得简单. 然而这个标题对于程 序集的内聚来说也很醒目的.程序集的内聚性是指概念单元到单个组件的职责

Effective C#原则33:限制类型的访问

并不是所有的人都须要知道所有的事.也不是所有的类型须要是公共的.对于每个类型,在满足功能的情况下,应该尽可能的限制访问级别.而且这些访问级别往往比你想像的要少得多.在一个私有类型上,所有的用户都可以通过一个公共的接口来访问这个接口所定义的功能. 让我们回到最根本的情况上来:强大的工具和懒惰的开发人员.VS.net对于他们来说是一个伟大的高产工具.我用VS.net或者C# Builder轻松的开发我所有的项目,因为它让我更快的完成任务.其中一个加强的高产工具就是让你只用点两下按钮,一个类就创建了,

Effective C#原则23:避免返回内部类对象的引用

你已经知道,所谓的只读属性就是指调用者无法修改这个属性.不幸运的是 ,这并不是一直有效的.如果你创建了一个属性,它返回一个引用类型,那么调 用者就可以访问这个对象的公共成员,也包括修改这些属性的状态.例如: public class MyBusinessObject { // Read Only property providing access to a // private data member: private DataSet _ds; public DataSet Data { get

qt-自己定义的类MyItem控件继承于QGraphicsWidgt类,该如何布局求例子。

问题描述 自己定义的类MyItem控件继承于QGraphicsWidgt类,该如何布局求例子. 自己写了一个myItem:QGraphicsWidgetd的控件,现在想在main.cpp中完成布局,布局的样式就是顶部一个MyItem控件,下面是垂直分布的.两边可以添加控件这种样式 求大神讲解下 阿里嘎多.目前自己研究的是利用了QGraphicsLinearLayout. 解决方案 http://www.it165.net/pro/html/201308/6703.html

Effective C#原则45:选择强异常来保护程序

当你抛出异常时,你就在应用程序中引入了一个中断事件.而且危机到程序 的控制流程.使得期望的行为不能发生.更糟糕的是,你还要把清理工作留给最 终写代码捕获了异常的程序员.而当一个异常发生时,如果你可以从你所管理的 程序状态中直接捕获,那么你还可以采取一些有效的方法.谢天谢地,C#社区不 须要创建自己的异常安全策略,C++社区里的人已经为我们完成了所有的艰巨的 工作.以Tom Cargill的文章开头:"异常处理:一种错误的安全感觉, " 而且Herb Sutter,Scott Meyer

Effective C#原则35:选择重写函数而不是使用事件句柄

很多.Net类提供了两种不同的方法来控制一些系统的事件.那就是,要么添 加一个事件句柄:要么重写基类的虚函数.为什么要提供两个方法来完成同样的 事情呢?其实很简单,那就是因为不同的情况下要调用为的方法.在派生类的内 部,你应该总是重写虚函数.而对于你的用户,则应该限制他们只使用句柄来响 应一些不相关的对象上的事件. 例如你很了一个很不错的Windows应用程 序,它要响应鼠标点下的事件.在你的窗体类中,你可以选择重写OnMouseDown ()方法: public class MyForm :

Effective C#原则24:选择申明式编程而不是命令式编程

与命令式编程相比,申明式编程可以用更简单,更清楚的方法来描述软件的 行为.申明式编程就是说用申明来定义程序的行为,而不是写一些指令.在C#里 ,也和其它大多数语言一样,你的大多数程序都是命令式的:在程序中写一个方 法来定义行为.在C#中,你在编程时使用特性就是申明式编程.你添加一个特性 到类,属性,数据成员,或者是方法上,然后.Net运行时就会为你添加一些行为 .这样申明的目的就是简单易用,而且易于阅读和维护. 让我们以一个 你已经使用过的例子开始.当你写你的第一个ASP.Net Web服务时,

Effective C#原则22:用事件定义对外接口

可以用事件给你的类型定义一些外部接口.事件是基于委托的,因为委托可 以提供类型安全的函数签名到事件句柄上.加上大多数委托的例子都是使用事件 来说明的,以至于开发人员一开始都认为委托与事件是一回事.在原则21里,我 已经展示了一些不在事件上使用委托的例子.在你的类型与其它多个客户进行通 信时,为了完成它们的行为,你必须引发事件. 一个简单的例子,你正 在做一个日志类,就像一个信息发布机一样在应用程序里发布所有的消息.它接 受所有从程序源发布的消息,并且把这些消息发布到感兴趣的听众那里.这些听 众可