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

并不是所有的人都须要知道所有的事。也不是所有的类型须要是公共的。对于每个类型,在满足功能的情况下,应该尽可能的限制访问级别。而且这些访问级别往往比你想像的要少得多。在一个私有类型上,所有的用户都可以通过一个公共的接口来访问这个接口所定义的功能。

让我们回到最根本的情况上来:强大的工具和懒惰的开发人员。VS.net对于他们来说是一个伟大的高产工具。我用VS.net或者C# Builder轻松的开发我所有的项目,因为它让我更快的完成任务。其中一个加强的高产工具就是让你只用点两下按钮,一个类就创建了,当然如果这正是我想要的话。VS.net为我们创建的类就是这样的:

public class Class2
{
 public Class2()
 {
  //
  // TODO: Add constructor logic here
  //
 }
}

这是一个公共类,它在每个使用我的程序集的代码块上都是可见的。这样的可见级别太高了,很多独立存在的类都应该是内部(internal)的。你可以通过在已经存在的类里嵌套一个受保护的或者私有的类来限制访问。 越低的访问级别,对于今后的更新整个系统的可能性就越少。越少的地方可以访问到类型,在更新时就越少的地方要修改。

只暴露须要暴露的内容,应该通过尝试在类上实现公共接口来减少可见内容。你应该可以在.Net框架库里发现使用Enumerator模式的例子,System.ArrayList包含一个私有类,ArrayListEnumerator, 而就是它只实现了IEnumerator接口:

// Example, not complete source
public class ArrayList: IEnumerable
{
 private class ArraylistEnumerator : IEnumerator
 {
  // Contains specific implementation of
  // MoveNext( ), Reset( ), and Current.
 }
 public IEnumerator GetEnumerator()
 {
  return new ArrayListEnumerator( this );
 }
// other ArrayList members.
}

对于我们这样的使用者来说,不须要知道ArrayListEnumerator类,所有你须要知道的,就是当我们在ArrayList对象上调用GetEnumerator函数时,你所得到的是一个实现了IEnumerator接口的对象。而具体的实现则是一个明确的类。.Net框架的设计者在另一个集合类中使用了同样的模式:哈希表(Hashtable)包含一个私有的HashtableEnumerator, 队列(Queue)包含一个QueueEnumerator, 等等。私有的枚举类有更多的优势。首先,ArrayList类可以完全取代实现IEnumerator的类型,而且你已经成为一个贤明的程序员了,不破坏任何内容。其实,枚举器类不须要是CLS兼容的,因为它并不是公共的(参见原则30)。而它的公共接口是兼容的。你可以使用枚举器而不用知道实现的类的任何细节问题。

创建内部的类是经常使用的用于限制类型可见范围的概括方法。默认情况下,很多程序员都总是创建公共的类,从来不考虑其它方法。这是VS.net的事。我们应该取代这种不加思考的默认,我们应该仔细考虑你的类型会在哪些地方使用。它是所有用户可见的?或者它主要只是在一个程序集内部使用?

通过使用接口来暴露功能,可以让你更简单的创建内部类,而不用限制它们在程序集外的使用(参见原则19)。类型应该是公共的呢?或者有更好的接口聚合来描述它的功能?内部类可以让你用不同的版本来替换一个类,只要在它们实现了同样的接口时。做为一个例子,考虑这个电话号码验证的问题:

public class PhoneValidator
{
 public bool ValidateNumber( PhoneNumber ph )
 {
  // perform validation.
  // Check for valid area code, exchange.
  return true;
 }
}

时间: 2024-12-30 18:20:05

Effective C#原则33:限制类型的访问的相关文章

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

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

Effective C#原则41:选择DataSet而不是自定义的数据结构

因为两个原则,把DataSet的名声搞的不好.首先就是使用XML序列化的 DataSet与其它的非.Net代码进行交互时不方便.如果在Web服务的API中使用 DataSet时,在与其它没有使用.Net框架的系统进行交互时会相当困难.其次, 它是一个很一般的容器.你可以通过欺骗.Net框架里的一些安全类型来错误 DataSet.但在现代软件系统中,DataSet还可以解决很多常规的问题.如果你明 白它的优势,避免它的缺点,你就可以扩展这个类型了. DataSet类设计 出来是为了离线使用一些存储

一维数组,二维数组,三维数组,数组与指针,结构体数组,通过改变指针类型改变访问数组的方式

 打印数组中的每个元素,打印每个元素的地址: #include <stdio.h> #include <stdlib.h>   void main(void) {     int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };     for (int *p = a; p < a + 10;p++)  //指针类型决定4个字节     {         printf("\n%p,%d", p, *p);    

Effective C#原则25 让你的类型支持序列化

对象的持久是类型的一个核心功能.这是一个在你忽略对它的支持以前,没 有人会注意到的基本元素之一. 如果你的类型不能恰当的支持序列化,那么对 于把你类的做为基类或者成员的开发人员来说,你会给他们增加很多的工作量. 当你的类型不支持序列化时,他们不得不围绕这工作,自己添加实现这个标准的 功能.而对于不能访问类的私有成员的开发人来说,恰当的实现你的类型的序列 化是不太可能的.如果你的类型不支持序列化,那么对于你的用户来说,想再要 实现实它是很困难或者根本就不可能的事. 取而代之的是,为你的实际 类型添

Effective C#原则7: 选择恒定的原子值类型数据

恒定类型(immutable types)其实很简单,就是一但它们被创建,它们(的值) 就是固定的.如果你验证一些准备用于创建一个对象的参数,你知道它在验证状 态从前面的观点上看.你不能修改一个对象的内部状态使之成为无效的.在一个 对象被创建后,你必须自己小心翼翼的保护对象,否则你不得不做错误验证来禁 止改变任何状态.恒定类型天生就具有线程完全性的特点:多访问者可同时访问 相同的内容.如果内部状态不能修改,那么就不能给不同的线程提供查看不一致 的数据视图的机会.恒定类型可以从你的类上安全的暴露出

Effective C#原则6:区别值类型数据和引用类型数据

值类型数据还是引用类型数据?结构还是类?什么你须要使用它们呢?这不 是C++,你可以把所有类型都定义为值类型,并为它们做一个引用.这也不是 Java,所有的类型都是值类型.你在创建每个类型实例时,你必须决定它们以什 么样的形式存在.这是一个为了取得正确结果,必须在一开始就要面对的重要决 定.(一但做也决定)你就必须一直面对这个决定给你带来的后果,因为想在后 面再对它进行改动,你就不得不在很多细小的地方强行添加很多代码.当你设计 一个类型时,选择struct或者class是件简单的小事情,但是,一

Effective C#原则8:确保0对于值类型数据是有效的

.Net系统默认所有的对象初始化时都为0.这并没有提供一个方法来预防其他 程序员创建的值类型数据的实例在初始化是都是0.请让你的数据类型默认值也 是0. 一个特殊情况是在枚举类型数据中.决不要创建一个不包括0在内 的枚举类型.所有的枚举类型都是从System.ValueType派生的.枚举类型的值是 从0开始的,但你可以改变这一行为: public enum Planet { // Explicitly assign values. // Default starts at 0 otherwis

Effective C#原则43:请勿滥用反射

创建二进制的组件时,同时也意味着你要使用迟后绑定和反射来查找你所须 要的具有特殊功能代码.反射是一个很有力的工具,而且它让你可以写出可动态 配置的软件.使用反射,一个应用程序可以通过添加新的组件来更新功能,而这 些组件是在软件最开始发布时没有的.这是有利的. 这一伸缩性也带来 了一些复杂的问题,而且复杂问题的增加又会增加出现其它问题的可能.当你使 用反射时,你是围绕着C#的安全类型.然而,成员调用的参数和返回值是以 System.Object类型存在的.你必须在运行时确保这些类型是正确的.简单的

Effective C#原则42:使用特性进行简单的反射

当你创建了一个与反射相关的系统时,你应该为你自己的类型,方法,以及 属性定义一些自己的特性,这样可以让它们更容易的被访问.自定义的特性标示 了你想让这些方法在运行时如何被使用.特性可以测试一些目标对象上的属性. 测试这些属性可以最小化因为反射时可能而产生的类型错误. 假设你须 要创建一个机制,用于在运行时的软件上添加一个菜单条目到一个命令句柄上.这个须要很简单:放一个程序集到目录里,然后程序可以自己发现关于它的一些 新菜单条目以及新的菜单命令.这是利用反射可以完成的最好的工作之一:你的 主程序须