C#knowledge

1 转到定义: F12;
2 设置书签:Ctr+K+K;
3 设置任务: //TODO:something,查看任务Ctrl+W+T;
4 查找:Ctrl+ F, Ctrl+Shift+F;
5 强迫智能感知:Ctrl+J;
6 强迫智能感知显示参数信息:Ctrl-Shift-空格;
7 格式化整个块:Ctrl+K+F;
8 全屏幕:Alt+Shift+Enter;
9 设置书签:Ctrl+B+T,跳转书签:Ctrl+B+N
10 检查括号匹配(在左右括号间切换): Ctrl +]
11 选中从光标起到行首(尾)间的代码: Shift + Home(End)
12 在方法定义和调用之点切换:Ctrl+Shift+7(8)
13 设置断点:F9
14 查找所有引用: Shift + F12
15 注释代码,助记方法,Edit + Comments:Ctrl + E,C
16 取消注释, 助记方法:Edit + UnComments:Ctrl + E,U
17 格式代码, 助记方法:Edit + Document(只能在代码能编绎的情况下起使用):Ctrl + E,D
18 收拢代码:Ctrl+M, O
19 选中自己圈中的长方块:Alt+Shift+鼠标
20 调试模式下,“调试——窗口——反汇编”,或者ctrl + alt + d
21 按下Ctrl+Enter会在上面插入一个空行,Ctrl+Shift+Enter则会在下面插入一个空行。光标会移至新行的开始处。
22 使用Tab增加缩进,Shift+Tab减少缩进(相应的菜单命令在Edit - Advanced 中)
23 格式化整篇代码: Ctrl+K, D
24 用Ctrl+W选中当前字
25 单个节点折叠与打开开关: Ctrl+M, M
26 使用Ctrl+G跳至指定行
27 使用Ctrl+Delete和Ctrl+Backspace分别删除后继和前驱的词
28 使用Ctrl+L剪切当前行,Ctrl+Shift+L删除当前行
29 如何创建书签并在其中进行跳转?(推荐)按下Ctrl+K, Ctrl+K 可以创建/取消一个书签,该命令绑定至Edit.ToggleBookmark,如果你的快捷键与此不同,可通过命令来查看具体的快捷键。
30 使用Ctrl+J来帮助语句完成。

C#.NET中关于结构与类之间的区别
 类与结构的实例比较
 类与结构的差别
 如何选择结构还是类

  一.类与结构的示例比较:

  结构示例:

  publicstructPerson
  {
   stringName;
   intheight;
   intweight;

  publicbooloverWeight()
   {
    //implementsomething
   }
  }

  类示例:
  publicclassTestTime
  {
   inthours;
   intminutes;
   intseconds;

   publicvoidpasstime()
   {
    //implementationofbehavior
   }
  }

  调用过程:

  publicclassTest
  {
   publicstaticovidMain
   {
    PersonMyperson=newPerson//声明结构
    TestTimeMytime=NewTestTime//声明类
   }
  }

  从上面的例子中我们可以看到,类的声明和结构的声明非常类似,只是限定符后面是struct还是class的区别,而且使用时,定义新的结构和定义新的类的方法也非常类似。那么类和结构的具体区别是什么呢?

二.类与结构的差别

  1.值类型与引用类型

  结构是值类型:值类型在堆栈上分配地址,所有的基类型都是结构类型,例如:int对应System.int32结构,string对应system.string结构,通过使用结构可以创建更多的值类型

  类是引用类型:引用类型在堆上分配地址

  堆栈的执行效率要比堆的执行效率高,可是堆栈的资源有限,不适合处理大的逻辑复杂的对象。所以结构处理作为基类型对待的小对象,而类处理某个商业逻辑

  因为结构是值类型所以结构之间的赋值可以创建新的结构,而类是引用类型,类之间的赋值只是复制引用

  注:

  1.虽然结构与类的类型不一样,可是他们的基类型都是对象(object),c#中所有类型的基类型都是object

  2.虽然结构的初始化也使用了New操作符可是结构对象依然分配在堆栈上而不是堆上,如果不使用“新建”(new),那么在初始化所有字段之前,字段将保持未赋值状态,且对象不可用

2.继承性

  结构:不能从另外一个结构或者类继承,本身也不能被继承,虽然结构没有明确的用sealed声明,可是结构是隐式的sealed.

  类:完全可扩展的,除非显示的声明sealed否则类可以继承其他类和接口,自身也能被继承

  注:虽然结构不能被继承可是结构能够继承接口,方法和类继承接口一样

  例如:结构实现接口

  interfaceIImage
  {
   voidPaint();
  }

  structPicture:IImage
  {
   publicvoidPaint()
   {
      //paintingcodegoeshere
   }
   privateintx,y,z; //otherstructmembers
  }  

  3.内部结构:

  结构:

  没有默认的构造函数,但是可以添加构造函数

  没有析构函数

  没有abstract和sealed(因为不能继承)

  不能有protected修饰符

  可以不使用new初始化

  在结构中初始化实例字段是错误的

  类:

   有默认的构造函数
 有析构函数
 可以使用abstract和sealed
 有protected修饰符
 必须使用new初始化

  三.如何选择结构还是类

  讨论了结构与类的相同之处和差别之后,下面讨论如何选择使用结构还是类:

  1.堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些

  2.结构表示如点、矩形和颜色这样的轻量对象,例如,如果声明一个含有1000个点对象的数组,则将为引用每个对象分配附加的内存。在此情况下,结构的成本较低。

  3.在表现抽象和多级别的对象层次时,类是最好的选择

 

  4.大多数情况下该类型只是一些数据时,结构时最佳的选择


面向对象设计思想(C#)
有了翅膀才能飞,欠缺灵活的代码就象冻坏了翅膀的鸟儿。不能飞翔,就少了几许灵动的气韵。我们需要给代码带去温暖的阳光, 让僵冷的翅膀重新飞起来。结合实例,通过应用OOP、设计模式和重构,你会看到代码是怎样一步一步复活的。 为了更好的理解设计思想,实例尽可能简单化。但随着需求的增加,程序将越来越复杂。此时就有修改设计的必要, 重构和设计模式就可以派上用场了。最后当设计渐趋完美后,你会发现,即使需求不断增加,你也可以神清气闲,不用为代码设计而烦恼了。

      假定我们要设计一个媒体播放器。该媒体播放器目前只支持音频文件mp3和wav。如果不谈设计,设计出来的播放器可能很简单:

public class MediaPlayer
{  
   private void PlayMp3()
   {
      MessageBox.Show("Play the mp3 file.");
   }

   private void PlayWav()
   {
      MessageBox.Show("Play the wav file.");
   }

   public void Play(string audioType)
   {     
      switch (audioType.ToLower())
      {
          case ("mp3"):
             PlayMp3();
             break;
          case ("wav"):
             PlayWav();
             break;            
      }     
   }
}

       自然,你会发现这个设计非常的糟糕。因为它根本没有为未来的需求变更提供最起码的扩展。如果你的设计结果是这样,那么当你为应接不暇的需求变更而焦头烂额的时候,你可能更希望让这份设计到它应该去的地方,就是桌面的回收站。仔细分析这段代码,它其实是一种最古老的面向结构的设计。如果你要播放的不仅仅是mp3和wav,你会不断地增加相应地播放方法, 然后让switch子句越来越长,直至达到你视线看不到的地步。

       好吧,我们先来体验对象的精神。根据OOP的思想,我们应该把mp3和wav看作是一个独立的对象。那么是这样吗?

public class MP3
{
   public void Play()
   {
       MessageBox.Show("Play the mp3 file.");
   }
}

       好样的,你已经知道怎么建立对象了。更可喜的是,你在不知不觉中应用了重构的方法,把原来那个垃圾设计中的方法名字改为了 统一的Play()方法。你在后面的设计中,会发现这样改名是多么的关键!但似乎你并没有击中要害, 以现在的方式去更改MediaPlayer的代码,实质并没有多大的变化。
        既然mp3和wav都属于音频文件,他们都具有音频文件的共性,为什么不为它们建立一个共同的父类呢?

public class AudioMedia
{
   public void Play()
   {
       MessageBox.Show("Play the AudioMedia file.");
   }
}

        现在我们引入了继承的思想,OOP也算是象模象样了。得意之余,还是认真分析现实世界吧。其实在现实生活中,我们播放的只会是某种具体类型的音频文件,因此这个AudioMedia类并没有实际使用的情况。对应在设计中,就是:这个类永远不会被实例化。所以,还得动一下手术,将其改为抽象类。好了,现在的代码有点OOP的感觉了:

 

        看看现在的设计,即满足了类之间的层次关系,同时又保证了类的最小化原则,更利于扩展(到这里,你会发现play方法名改得多有必要)。 即使你现在又增加了对WMA文件的播放,只需要设计WMA类,并继承AudioMedia,重写Play方法就可以了,
MediaPlayer类对象的Play方法根本不用改变。

       是不是到此就该画上圆满的句号呢?然后刁钻的客户是永远不会满足的,他们在抱怨这个媒体播放器了。因为他们不想在看足球比赛的时候,只听到主持人的解说,他们更渴望看到足球明星在球场奔跑的英姿。也就是说,他们希望你的媒体播放器能够支持视频文件。你又该痛苦了,因为在更改硬件设计的同时,原来的软件设计结构似乎出了问题。因为视频文件和音频文件有很多不同的地方,你可不能偷懒,让视频文件对象认音频文件作父亲啊。你需要为视频文件设计另外的类对象了,假设我们支持RM和MPEG格式的视频:

public abstract class VideoMedia
{
   public abstract void Play();
}

public class RM:VideoMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the rm file.");
   }
}

public class MPEG:VideoMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the mpeg file.");
   }
}

        糟糕的是,你不能一劳永逸地享受原有的MediaPlayer类了。因为你要播放的RM文件并不是AudioMedia的子类。

        不过不用着急,因为接口这个利器你还没有用上(虽然你也可以用抽象类,但在C#里只支持类的单继承)。虽然视频和音频格式不同,别忘了,他们都是媒体中的一种,很多时候,他们有许多相似的功能,比如播放。根据接口的定义,你完全可以将相同功能的一系列对象实现同一个接口:

public interface IMedia
{
   void Play();
}

public abstract class AudioMedia:IMedia
{
   public abstract void Play();
}

public abstract class VideoMedia:IMedia
{
   public abstract void Play();
}

       再更改一下MediaPlayer的设计就OK了:

public class MediaPlayer

   public void Play(IMedia media)
   {     
       media.Play();
   }
}

        现在可以总结一下,从MediaPlayer类的演变,我们可以得出这样一个结论:在调用类对象的属性和方法时,尽量避免将具体类对象作为传递参数,而应传递其抽象对象,更好地是传递接口,将实际的调用和具体对象完全剥离开,这样可以提高代码的灵活性。

        不过,事情并没有完。虽然一切看起来都很完美了,但我们忽略了这个事实,就是忘记了MediaPlayer的调用者。还记得文章最开始的switch语句吗?看起来我们已经非常漂亮地除掉了这个烦恼。事实上,我在这里玩了一个诡计,将switch语句延后了。虽然在MediaPlayer中,代码显得干净利落,其实烦恼只不过是转嫁到了MediaPlayer的调用者那里。
例如,在主程序界面中:
 
Public void BtnPlay_Click(object sender,EventArgs e)
{
    switch (cbbMediaType.SelectItem.ToString().ToLower())
    {
        IMedia media;
        case ("mp3"):
             media = new MP3();
             break;
        case ("wav"):
             media = new WAV();
             break;  
        //其它类型略;
    }
    MediaPlayer player = new MediaPlayer();
    player.Play(media);
}
       用户通过选择cbbMediaType组合框的选项,决定播放哪一种文件,然后单击Play按钮执行。

       现在该设计模式粉墨登场了,这种根据不同情况创建不同类型的方式,工厂模式是最拿手的。先看看我们的工厂需要生产哪些产品呢?虽然这里有两种不同类型的媒体AudioMedia和VideoMedia(以后可能更多),但它们同时又都实现IMedia接口,
所以我们可以将其视为一种产品,用工厂方法模式就可以了。首先是工厂接口:

public interface IMediaFactory
{
   IMedia CreateMedia();
}

       然后为每种媒体文件对象搭建一个工厂,并统一实现工厂接口:

public class MP3MediaFactory:IMediaFactory
{
   public IMedia CreateMedia()
   {
       return new MP3();
   }
}
public class RMMediaFactory:IMediaFactory
{
   public IMedia CreateMedia()
   {
       return new RM();
   }
}
//其它工厂略;

写到这里,也许有人会问,为什么不直接给AudioMedia和VideoMedia类搭建工厂呢?很简单,因为在AudioMedia和VideoMedia中,分别还有不同的类型派生,如果为它们搭建工厂,则在CreateMedia()方法中,仍然要使用Switch语句。而且既然这两个类都实现了IMedia接口,可以认为是一种类型,为什么还要那么麻烦去请动抽象工厂模式,来生成两类产品呢? 可能还会有人问,即使你使用这种方式,那么在判断具体创建哪个工厂的时候,不是也要用到switch语句吗?我承认这种看法是对的。不过使用工厂模式,其直接好处并非是要解决switch语句的难题,而是要延迟对象的生成,以保证的代码的灵活性。当然,我还有最后一招杀手锏没有使出来,到后面你会发现,switch语句其实会完全消失。

还有一个问题,就是真的有必要实现AudioMedia和VideoMedia两个抽象类吗?让其子类直接实现接口不更简单?对于本文提到的需求, 我想你是对的,但不排除AudioMedia和VideoMedia它们还会存在区别。例如音频文件只需要提供给声卡的接口,而视频文件还需要提供给显卡的接口。如果让MP3、WAV、RM、MPEG直接实现IMedia接口,而不通过AudioMedia和VideoMedia,在满足其它需求的设计上也是不合理的。当然这已经不包括在本文的范畴了。

现在主程序界面发生了稍许的改变:
Public void BtnPlay_Click(object sender,EventArgs e)
{
IMediaFactory factory = null;
    switch (cbbMediaType.SelectItem.ToString().ToLower())
    {
        case ("mp3"):
             factory = new MP3MediaFactory();
             break;
        case ("wav"):
             factory = new WAVMediaFactory();
             break;  
        //其他类型略;
    }
    MediaPlayer player = new MediaPlayer();
    player.Play(factory.CreateMedia());
}

写到这里,我们再回过头来看MediaPlayer类。这个类中,实现了Play方法,并根据传递的参数,调用相应媒体文件的Play方法。在没有工厂对象的时候,看起来这个类对象运行得很好。如果是作为一个类库或组件设计者来看,他提供了这样一个接口,供主界面程序员调用。然而在引入工厂模式后,在里面使用MediaPlayer类已经多余了。所以,我们要记住的是,重构并不仅仅是往原来的代码添加新的内容。当我们发现一些不必要的设计时,还需要果断地删掉这些冗余代码。
Public void BtnPlay_Click(object sender,EventArgs e)
{
IMediaFactory factory = null;
    switch (cbbMediaType.SelectItem.ToString().ToLower())
    {       
        case ("mp3"):
             factory = new MP3MediaFactory();
             break;
        case ("wav"):
             factory = new WAVMediaFactory();
             break;  
        //其他类型略;
    }
    IMedia media = factory.CreateMedia();
    media.Play();
}

如果你在最开始没有体会到IMedia接口的好处,在这里你应该已经明白了。我们在工厂中用到了该接口;而在主程序中,仍然要使用该接口。使用接口有什么好处?那就是你的主程序可以在没有具体业务类的时候,同样可以编译通过。因此,即使你增加了新的业务,你的主程序是不用改动的。

不过,现在看起来,这个不用改动主程序的理想,依然没有完成。看到了吗?在BtnPlay_Click()中,依然用new创建了一些具体类的实例。如果没有完全和具体类分开,一旦更改了具体类的业务,例如增加了新的工厂类,仍然需要改变主程序,何况讨厌的switch语句仍然存在,它好像是翅膀上滋生的毒瘤,提示我们,虽然翅膀已经从僵冷的世界里复活,但这双翅膀还是有病的,并不能正常地飞翔。

是使用配置文件的时候了。我们可以把每种媒体文件类类型的相应信息放在配置文件中,然后根据配置文件来选择创建具体的对象。
并且,这种创建对象的方法将使用反射来完成。首先,创建配置文件:

<appSettings>
  <add key="mp3" value="WingProject.MP3Factory" />
  <add key="wav" value="WingProject.WAVFactory" />
  <add key="rm" value="WingProject.RMFactory" />
  <add key="mpeg" value="WingProject.MPEGFactory" />
</appSettings>

然后,在主程序界面的Form_Load事件中,读取配置文件的所有key值,填充cbbMediaType组合框控件:
public void Form_Load(object sender, EventArgs e)
{
cbbMediaType.Items.Clear();
foreach (string key in ConfigurationSettings.AppSettings.AllKeys)
{
   cbbMediaType.Item.Add(key);
}
cbbMediaType.SelectedIndex = 0;
}

最后,更改主程序的Play按钮单击事件:
Public void BtnPlay_Click(object sender,EventArgs e)
{
string mediaType = cbbMediaType.SelectItem.ToString().ToLower();
string factoryDllName = ConfigurationSettings.AppSettings[mediaType].ToString();
//MediaLibray为引用的媒体文件及工厂的程序集;
IMediaFactory factory = (IMediaFactory)Activator.CreateInstance("MediaLibrary",factoryDllName).Unwrap();
IMedia media = factory.CreateMedia();
media.Play();
}

现在鸟儿的翅膀不仅仅复活,有了可以飞的能力;同时我们还赋予这双翅膀更强的功能,它可以飞得更高,飞得更远!

享受自由飞翔的惬意吧。设想一下,如果我们要增加某种媒体文件的播放功能,如AVI文件。那么,我们只需要在原来的业务程序集中创建AVI类,并实现IMedia接口,同时继承VideoMedia类。另外在工厂业务中创建AVIMediaFactory类,并实现IMediaFactory接口。假设这个新的工厂类型为WingProject.AVIFactory,则在配置文件中添加如下一行:
<add key="AVI" value="WingProject.AVIFactory" />。
而主程序呢?根本不需要做任何改变,甚至不用重新编译,这双翅膀照样可以自如地飞行!


 

public abstract class AudioMedia
{
   public abstract void Play();
}

public class MP3:AudioMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the mp3 file.");
   }
}

public class WAV:AudioMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the wav file.");
   }
}

public class MediaPlayer

   public void Play(AudioMedia media)
   {     
       media.Play();
   }
}

 

public class WAV
{
   public void Play()
   {
       MessageBox.Show("Play the wav file.");
   }
}
面向对象的关系数据库设计
一、概念的区分
  有些人把面向对象的数据库设计(即数据库模式)思想与面向对象数据库管理系统(OODBMS)理论混为一谈。其实前者是数据库用户定义数据库模式的思路,后者是数据库管理程序的思路。用户使用面向对象方法学可以定义任何一种DBMS数据库,即网络型、层次型、关系型、面向对象型均可,甚至文件系统设计也照样可以遵循面向对象的思路。 面向对象的思路或称规范可以用于系统分析、系统设计、程序设计,也可以用于数据 结构设计、数据库设计。OOSE自上至下、自始至终地贯彻面向对象思路,是一个一气呵成 的统一体。面向对象的数据库设计只是 OOSE 的一个环节。

  二、数据库设计的重要性

  一般数据库设计方法有两种,即属性主导型和实体主导型。属性主导型从归纳数据库应用的属性出发,在归并属性集合(实体)时维持属性间的函数依赖关系。实体主导型则先 从寻找对数据库应用有意义的实体入手,然后通过定义属性来定义实体。一般现实世界的 实体数在属性数 1/10 以下时,宜使用实体主导型设计方法。面向对象的数据库设计是从对象模型出发的,属于实体主导型设计。 一般数据库应用系统都遵循以下相关开发步骤:

1设计应用系统结构;

2 选择便于将应
   用程序与DBMS结合的DBMS体系结构,如RDBMS;

3 根据应用程序使用的环境平台,选择适宜的DBMS(如Oracle)和开发工具(如PB);

4 设计数据库,编写定义数据库模式的SQL程序;

5 编写确保数据正确录入数据库的用户接口应用程序;

6 录入数据库数据;

7 运行各种与 数据库相关的应用程序,以确认和修正数据库的内容。

  对以上各步骤,有几点需要说明:

(1) 这不是瀑布模型,每一步都可以有反馈。
  在公路局系统中,以上各步不仅有反馈、有反复,还有并行处理。比如一些库表在数 据录入时,另一些库表设计还在修改。这与我们的递增式开发方法有关,也与面向对象的 特征有关。

(2) 上述顺序不是绝对的,大多数场合是从第三步开始的。

(3) 对大多数数据库应用系统来说,上述各步中最重要、最困难的不是应用系统设计而是数据库设计。

  三、DBMS的支持和数据库设计

  多数据库应用系统开发者不重视数据库设计的原因是:他们太迷信DBMS,认为购入一个功能强大的DBMS后数据库设计就不困难、不重要了。一些国内外的数据库教材常常 是在为DBMS的开发厂商做宣传,而很少站在数据库用户角度,从数据库应用系统出发介绍数据库设计方法。结果往往使读者搞不清书中介绍的是数据库管理程序的设计思想,还是 应用这种 DBMS 进行数据库设计的思想。 其实,DBMS只是给用户为已采用的数据库提供一个舞台,而是否使用这个舞台上的道具以及唱什么戏,则完全取决于用户的戏剧脚本和导演(开发者)的安排。例如,公路局系统所使用的数据库管理系统,是以二维表为基本管理单元、支持所有关系代数操作、支持 实体完整性与实体间参照完整性的全关系型RDBMS,而我们要在这个舞台上利用上述"道具"设计一个面向对象的关系数据库。

  四、应用对象模型与RDBMS模型的映射

  数据库设计(模式)是否支持应用系统的对象模型,这是判断是否是面向对象数据库系统的基本出发点。由于应用系统设计在前,数据库设计随后,所以应用系统对象模型向 数据库模式的映射是面向对象数据库设计的关键。

  1. 三层数据库模式面向对象模型的扩展

  一般数据库设计多参照ANSL/SPARC关于数据库模式的3层标准结构提案。最接近物理数据库的内部模式由DBMS提供的SQL来描述。概念模式可以由若干个内部模式聚集而成 ,它是由数据库用户规范的一些表的集合。例如,公路局计划处数据库模式、机务处数据 库模式等,它们是逻辑数据库,常常通过库表ID来界定库边界。一般的概念模式是数据库 物理模式作用域的边界,它能实现数据库的物理意义、特定DBMS 的特殊操作对外部应用 程序的信息隐蔽。外部模式是从特定用户应用角度看待的数据库模式,从不同的应用出发对同一概念模式可以给出多种不同的外部模式。例如:公路绿化情况查询应用看到的数据 库是公路上的树木种类、数量、分布比率等, 梁隧道状况查询应用看到的是公路上的桥 梁、隧道长度、个数、路段等,但是它们可能访问的是同一个库表的不同子集。 当外部应用系统以对象模型进行抽象时,从各个应用出发抽象出的对象模型可以映射 到外部模型上,对此我们不妨称之为外部对象模型。但是,外部模型只是概念模型的子集 ,所以面向对象的数据库设计核心在于系统对象模型(不妨称之为概念对象模型) 向数据 库概念模型的映射(参见图1) 。

  2. 对象模型向数据库表的映射规则

  由于RDBMS是以二维表为基本管理单元的,所以对象模型最终是由二维表及表间关系来描述的。换言之,对象模型向数据库概念模型的映射就是向数据库表的变换过程。有关的变换规则简单归纳如下:

(1) 一个对象类可以映射为一个以上的库表,当类间有一对多的关系时,一个表也可 以对应多个类。

(2) 关系(一对一、一对多、多对多以及三项关系)的映射可能有多种情况,但一般映 射为一个表,也可以在对象类表间定义相应的外键。对于条件关系的映射,一个表至少应 有3个属性。

(3) 单一继承的泛化关系可以对超类、子类分别映射表,也可以不定义父类表而让子 类表拥有父类属性;反之,也可以不定义子类表而让父类表拥有全部子类属性。

(4) 对多重继承的超类和子类分别映射表,对多次多重继承的泛化关系也映射一个表 。 (5) 对映射后的库表进行冗余控制调整,使其达到合理的关系范式。

  3. 数据库模式要面向应用系统

  我们选择面向对象的系统设计也好,面向对象的数据库设计也好,根本目的是服务于应用系统的需要。 以公路局计划处子系统为例。计划处的最大工作量就是处理成堆的报表,因此如何有 效地存取这些报表是计划处数据库设计的关键。考虑到每月上交的报表是同构的,我们可 以创建一张库表去存储同一种报表,例如公路工程月报表。但是又产生另一个问题,当用户想查询某个月的公路工程月报表时,如何从库表中取出数据呢?按照数据库的思想应该 有一个主键来标识这张报表。在公路局的报表里,区别月报表靠上报时间和上报单位,但 如果为每条记录都加上这两个字段,无疑会加大库表冗余,增加查询时间,降低效率。更何 况每张报表都有单位负责人、填表人的属性,那么怎样解决这个问题呢?我们设计了超类 对象 X3 表和流水号表。
  将它们加入由应用对象模型映射出的数据库概念模型后,得到图2所示的结构。
  每一个应用模块对象对应建立一张流水号表,同一类的报表属同一流水号表,由流水号表统一管理。流水号表对各分局、处室提交和建立的每一张报表分配一个流水号,该流 水号在整个数据库中是唯一的,因此在库中存放任何一张报表都是明确的。流水号的数据类型为 Char(10),前4位为表号,后6位为序列号,其中序列号取自X3表中最大序列号。也 就是说,流水号就是对象标识符,报表是一个对象,一个对象标识符唯一决定一个对象。流 水号一旦被分配出去后,在这张报表的生存期内就具有了永久不变性。无论报表的内容及 结构怎么变化,它都不变,直到报表被删除,流水号才会消失。流水号表是父类,报表是子类,流水号表之间的联系只能通过 X3 表。5个应用模块对象完全映射到数据库概念模型中,形成应用对象与数据库对象的一一对应,保持了5个应用对象在目标系统设计中原有的 独立性,具有很好的封装性和信息隐蔽性。尽管流水号表会有一些冗余,但它是值得的。

  五、面向对象关系数据库设计效果

  在公路局系统设计中,从某种意义上讲,是数据库设计的面向对象特征最终奠定了整个系统的面向对象性,才使面向对象方法在程序开发阶段全面开花。其效果归纳如下:

  1. 数据库结构清晰
  便于实现OOP由于实现了应用模块对象对数据库对象的完全映射,数据库逻辑模型可以自然且直接 地模拟现实世界的实体关系。公路局用户所处的当前物理世界、系统开发者所抽象的系 统外部功能,与支持系统功能的内部数据库 (数据结构)一一对应,所以用户、开发者和数 据库维护人员可以用一致的语言进行沟通。 特别是对多数不了解公路局业务的程序开发人员来说,这种将应用对象与相应的数据 对象封装在对象统一体中的设计方法,大大减轻了程序实现的难度,使他们只要知道加工的数据及所需的操作即可,而且应用程序大多雷同,可以多处继承由设计人员抽象出来的 、预先开发好的各种物理级超类。

  2. 数据库对象具有独立性,便于维护
  除了数据库表对象与应用模块对象一一对应外,在逻辑对象模型中我们没有设计多重 继承的泛化关系,所以这样得到的数据库结构基本上是由父表类和子表类构成的树型层次 结构,表类间很少有继承以外的复杂关系,是一个符合局部化原则的结构,从而使数据库表 数据破坏的影响控制在局部范围且便于修复,给公路局系统开通后的数据库日常维护工作带来便利。

  3. 需求变更时程序与数据库重用率高,修改少
  在映射应用对象时,除关系映射规范化后可能出现一对多的表映射外,大多数应用对 象与表对象是一一对应的。我们可以把规范化处理后的、由一个应用对象映射出来的多个表看成一个数据库对象。因此当部分应用需求变更时,首先,系统修改可以不涉及需求 不变更的部分。其次,变更部分的修改可以基本上只限于追加或删除程序模块或追加新库 表,而基本上不必修改原有程序代码或原有库表定义,从而大大减少了工作量,降低了工作难度。

  六、最简单的就是最好的

  客观世界是错综复杂的,计算机科学理论的发展也越来越高深、复杂。然而,人类探索理论和技术的最终目的是:让客观世界的复杂变简单,最简单的就是最好的。
  为此我们 给出以下几点忠告:

  1. 慎用外键

  RDBMS 支持复杂关系的能力很强,无论用户怎么在逻辑上设定外键,它基本上都能从 物理上帮用户实现。但是外键把许多独立的实体牵连在一起,不仅使 RDBMS 维持数据一 致性负担沉重,也使数据库应用复杂化,加重了程序开发负担。这样的数据库很难理解,很 难实现信息隐蔽性设计,往往把简单问题复杂化。

  2. 适当冗余

  减少数据库冗余的设计思路产生于70年代,它是促使 DBMS 进步的重要动力之一。然 而,犹如为了节省2个字节的存储空间而酿成了如今全球为之头痛的2000年问题一样,它是 计算机硬件主导时代的产物。以今天国内计算机市场价格为例,6G服务器硬盘的价格不过 2000元,而上海物价局 1996 年颁发的一个人月软件开发的指导价约8000元,即一个人月 的软件价格就可以购买20G左右的硬盘。即使有5万行数据的库表,每个记录压缩40字符的 冗余,单纯计算合计也不足2M,即节省0.6元钱的磁盘空间。 今天的世界已进入软件主导的计算机时代。因此,最容易理解、应用开发工作量最少 、维护最简单的数据库结构才是最好的。只要数据完整性、一致性不受威胁,有些冗余,不足为虑。换言之,最节省软件成本 (而不是硬件成本) 的是最好的。

面向对象数据库的正确评价与选择
一、概述
就象关系数据库一样,市场上有许多面向对象的数据库(Object-Oriented Database,OODB)可供选择。然而,OODB在价格、功能、特色和体系上没有什么统一的标准。本文将帮助你理解各种OODB系统之间的一些差别,在为应用选择合适的OODB时,帮助你缩小挑选的范围。

根据标准的不同,我用于评估这些产品的参考资料也是五花八门。在大多数情况下,本文的评价以个人经验和看法为基础;另外一些细节直接从评测软件或供应商提供的数据资料获得。在作出选择之前,务必针对你的应用进行全面的测试。毕竟,你我看法可能有所不同。

我们将要分析的四种OODB产品是ObjectStore、Versant Developer Suite、Poet FastObjects和Objectivity。每一种产品将从以下几个方面进行评估:
价格和许可 顺从性 兼容性 特色 性能 可伸缩性和可用性
二、价格和许可
工程成本包括两个方面:初始成本和维护费用。初始成本往往在评估中起支配作用,但象年度支持、联机讨论、新手培训等问题都应该成为产品成本的考虑因素。即使和它的竞争者大型RDBMS相比,OODB的价格通常显得很昂贵。

标准考虑…评估软件可免费下载的、全功能的试用软件。开发版许可价格低价格,以用户数量为基础的许可。产品许可价格低价格,按照CPU数量为单位的许可,低廉的年度维护费用。联机支持负责的技术支持人员,大量有用的技术说明、示范和论坛。活跃的用户社团大量的热心用户,新闻组里丰富有益的活动,非官方网站的支持。
三、顺从性
和关系数据库相比,OODB一般对顺从标准的要求不是那么严格。大多数OODB都用自己独特的方法实现各种特色功能。由于还没有明确的标准评价OODB,所以对象数据库管理组织(Object Database Management Group,ODMG)的标准是当前最好的准绳。但各个OODB各自为政却带来了一些麻烦,要找出一个完全顺从ODMG 2.0或3.0规范的OODB产品很困难(与已经有一年历史的3.0规范相比,2.0规范是一个相当宽松和不完善的规范)。也许在不远的将来,我们将用Java Data Object(JDO)规范评价Java OODB。
标准考虑…对“对象定义语言”(ODL)的顺从完全遵从ODMG 3.0规范有关对象定义的规范。对“对象查询语言”(OQL)的顺从完全遵从ODMG 3.0规范有关查询的规范。对Java的顺从性完全遵从ODMG 3.0规范意味着正确实现Java API/绑定。对C++的顺从性完全遵从ODMG 3.0规范意味着正确实现C++ API。对Smalltalk的顺从性完全遵从ODMG 3.0规范(如果你的系统不太可能用到Smalltalk,那么这只是一个可选的标准)。
四、兼容性
无论是语言还是平台,工程对可伸缩性的要求会日益增加。OODB不应该在任何一方面影响这种可伸缩性。然而,平台支持的代价很昂贵,它要求进行广泛的测试和大量的文档说明。因此,一些供应商的产品只支持数量很少的平台。要找出一个和各种主流OO语言(如C++、Java、Smalltalk)紧密结合的方案是相当困难的。

标准考虑…支持的平台广泛的平台支持——我总是考虑三个关键的平台:Linux,Win2000,和Solaris。你优先考虑的平台可能有所不同。Java集成广泛的JDK支持,紧紧跟踪最新的JDK规范。C++集成广泛的编译器支持持久类的特殊化避免紧密结合——寻找那些不要求修改代码中持久类的数据库。这是一种与偏好有关的选择,所以你应该认真研究处理后扩展和扩展/实现方式相比的优缺点。可嵌入的版本只占用少量的磁盘空间、RAM;具有取消一些非核心功能的能力。
五、特色
每一种数据库方案都有自己的一些独特的功能。下面我特别指出一些核心功能,因为这些功能对于开发工程来说具有很高的价值:

标准考虑…数据库浏览管理数据库、修改内容、更新模式和生成内容报表的能力。客户端缓冲改善“热点”数据库响应速度的能力;在确保对象同步的前提下,使得对数据库的提取操作减到最少。数据库安全用户、用户组访问控制,最好在对象(如果不是容器的话)的层次上进行。XML支持无缝地从数据库提取、向数据库插入XML的工具。IDE集成和TogetherSoft之类提高开发效率的环境集成能够提高开发效率。
六、性能
在性能的某些方面,OODB占有优势;但在其他方面,OODB又有所不足。在这里提供每一种产品详细的性能测试数据显得过于冗长,但理解可能影响性能的体系和功能方面的局限是很重要的。

标准考虑…加锁策略应用-对象级的加锁机制能够带来很大的方便,但页面级的加锁机制在某些条件下能够带来性能上的飞跃。负载平衡透明地分布数据库、调用远程服务器上的方法、在并发线程/访问之间共享对象的能力。最大的数据库大小越大越好。事务支持检查点:由多个线程共享一个事务,一个线程占用多个事务,嵌套事务;当某个给定产品的实现影响了你的应用时,确保你自己理解了结合客户端缓冲时客户端/服务器端同步的工作机制。有关查询/性能的信息提取这些信息的能力,它能够帮助你找出性能瓶颈;OODB提供的优化和调整选项通常要比RDB少,但一些帮你提取性能信息和解释查询执行计划的工具仍很有用。
七、可伸缩性和可用性
虽然并非每一个工程都要求有企业级的恢复、可用性、可伸缩性功能,了解你所选择的OODB方案能够随着工程一起发展而提供这方面的能力是值得的。

标准考虑…失败转移主服务器出现问题时,透明地切换到冗余数据库。负载平衡把负载分布到冗余服务器、把对象分割到多个服务器、同步多个客户端对象缓冲之间数据视图的能力。复制和增量备份无缝地复制数据,支持负载平衡和恢复的能力。专用的查询引擎(Ad hoc query engine)丰富的查询语言,允许对数据的快速访问;理想情况下,它应该能够跨越没有直接关联的对象连接数据。
八、产品评论
下面,我按照前文提出的标准评估以下产品:

ObjectStore Versant Developer Suite Poet FastObjects Objectivity
请参考Cetus OODB area,那里有一个相当新的OODB供应商清单。
标准ObjectStoreVersant Developer SuitePoetObjectivity背景信息供应商Object DesignVersantPoetObjectivity产品主页ObjectStoreVDSFastObjectsObjectivity技术参考[29]Spec 或 PDF data sheetOverview 或 manualsJava 或 C++PDF overview 或 specific data sheets版本6.06.0t7 8.0[16]6.0价格和许可试用版本30天试用[1]60天试用功能限制90天试用许可费用[2][2][2][2]联机支持尚可[3][9]尚可[9]好[19]很好用户社团[23]中等[4]中等较小中等顺从性ODL顺从性NNN不完整的2.0/3.0支持OQL顺从性NN[10]ODMG 3.0[17]NJava接口遵从ODMG 3.0ODMG 3.0ODMG 3.0ODMG 3.0C++接口支持ODMG 3.0N/A不完整的2.0/3.0支持Smalltalk接口N/AN/AN/AODMG 3.0兼容性支持的Unix操作系统Linux,Solaris,HP-UX,IRIX,AIX,Tru64 [ref]Linux,Solaris,HP-UX,IRIX,SGI,Tru64 [ref]Linux,Solaris,HP-UX[ref]Linux,Solaris,HP-UX,IRIX,AIX,Tru64 [ref]支持的Windows操作系统98,NT4,2000 [ref]NT4,2000 [ref]98,NT4,2000 [ref]98,NT4,2000 [ref]JDK要求1.0,1.1–1.3[22]1.2,1.31.1-1.31.22,1.3持久类的特殊化N[5]N[5]N[5]Y[24]可嵌入的版本YNY[18]N功能数据库浏览器YYYY客户端缓冲YYYY数据库安全数据库或者段的用户/组控制数据库的用户控制[14]特定类和数据库的用户/组控制数据库的用户控制XML支持YY[15]部分[20]部分[20]性能加锁策略数据库,页,或者对象对象级对象级容器级[25]最大的数据库大小[7]数百个GB?数十个GB到数百个GB?[12]?根据报告,它达到了TB级事务支持死锁检测,MVCC [8]分布式事务管理(类似于MVCC的概念)检查点,共享或并行的事务,嵌套事务检查点,死锁检测,共享或并行的事务提供有关查询/性能的信息YN[13]NN[26]可伸缩性失败转移(failover)YYY可选[27]负载平衡部分[6]部分[11]部分[21]可选[27]复制和增量备份YYY可选[27]专门的查询引擎(Ad hoc query engine)没有OQL。使用集合和查询对象Y[10]Y (OQL)Y[28]
■ 结束语
我希望自己还没有给人以OODB狂热鼓吹者的印象——对于我所使用的大多数应用,我认为应用OODB带有一定的风险。然而,一旦你理解并熟悉了OODB,它们可以成为很方便的工具。我个人比较看好Poet和ObjectStore,但我觉得它们都很有用。

■ 注解
[1]只提供单实例个人版(Single-instance Personal Edition,PSE)供下载。[2]报价可以从销售代表处获得。[3]部分支持服务只提供给有效维护合同的拥有者访问。[4]社团规模只相对其他OODB产品而言。OODB的用户社团远远小于关系数据库的用户社团。[5]持久类必须进行事后处理。[6]多个数据库之间的负载平衡看来不太可能。相反,处理通过客户端缓冲得以分布,它把更多的逻辑和计算任务透明地移到了客户端,从而减小了服务器的负载。[7]这些数据未经证实,主要从供应商的声明获得。在讨论数据库大小的时候,对象的复杂性、大小、“合理的应答时间”等问题都是必须考虑的因素。[8]多版本并发控制(Multiversion Concurrency Control,MVCC)是一种非标准的技术,在并发读取/写入操作期间用来维持缓冲客户端和服务器端数据视图的一致性。[9]可以从大量在线演示、FAQ、论坛获益,所有这一切都对现有和潜在用户开放。[10]实现了一种私有的VQL语言,它和OQL有一些共同点。[11]通过复制实现部分负载平衡能力;根据推测,负载平衡可能通过把请求重定向到多个数据库实现,但这一点未经证实。多个数据库之间的透明复制确保了每一个OODB上都有一份最新的数据。[12]需要64位的版本,以便超越大量在内村、记录计数等方面的2^32限制;Versant的64位版本已经构造完毕,但还没有在所有它所支持的平台上经过验证。[13]提供一些用于查询调整和计时的工具。[14]如果不是我在什么地方错过,它似乎没有提到粒度更小的安全机制或者是在API中提供这方面的能力。我找到特别提及的只是通过OS强制的数据库文件权限实现的数据库级访问控制。[15]需要单独提供的工具。[16]Poet分三种形式:t2(实时嵌入式Java),e7(嵌入式Java/C++),以及t7(企业Java/C++)。版本号没有明确显示,t7的v8.0得自文件的版本号,当前是8.0.0.19。[17]不能保证它完整地支持ODMG 3.0 OQL,但覆盖范围看来相当广泛。[18]包含一个C++或Java的嵌入式版本,以及一个Java的实时嵌入式版本。[19]联机支持网站community.fastobjects.com的内容非常全面,但速度常常很慢。我拥有高速连接,但它有规律地返回页面超时错误。[20]我只能通过命令行接口使用批量导入/导出功能。[21]没有分布式功能的任何明显标记,而且我也没有用过一个分布式的配置。如果Poet的配置中数据库复制带有“reader scalability”选项,负载平衡可以在某种程度上得以实现,使得查询可以对只读的从属数据库进行。[22]至少JDK 1.2看来最好,你将得到更好的集合支持和避免一些已经有报道的“quirk”问题(未能肯定是否为JDK 1.1解决了这些问题)。[23]很难进行评估。我从收入和Internet/新闻组的讨论入手分析。请把它看成是一种猜测。[24]持久类的标识或者是它从ooObj派生,或者它实现IooObj接口。[25]虽然对整个容器加锁听起来吓人,但它的基本思想是,它会显著减少加锁服务器的负载。[26]通过API调用和统计功能提供一些运行时查询调试能力。[27]一些资料,例如这一份说明,显示出这些选项会增加成本。[28]没有提供类似OQL的等价语言。SQL++是一种遵从SQL的功能,允许针对OODB进行SQL查询;容器支持一个scan()方法,以及支持“谓词查询语言”表达式(Predicate Query Language)。[29]这四种产品都提供评估版供下载。下载软件包附带的文档和示例一般都比较完善。
面向对象软件开发的十大原则 (二)
当定义方法的参数时,一定要使它们可以扩展。例如,下面这行代码是不可扩展的:

   Public Function PlaceOrder(sLastName as String, sFirstName as String, sAddress as String)

  要想调用这个方法你必须传递这3个参数。但是如果你以后决定在定单上还需要电话号码,就必须修改函数签名,这就破坏了兼容性以及每个调用此方法的代码段。为了防止这个问题的发生,一个更好的解决方法是:在一个容器中传递参数。你可以使用记录集、变量数组或 XML字符串来传递更普通的参数。

  当转移到.NET时,这个技巧就不是十分必要了,因为在.NET中你可以进行函数装载,这样就可以具有两套不同参数的同一个函数:

Public Function PlaceOrder(sLastName _
as String, sFirstName as String, sAddress as String)
Public Function PlaceOrder(sLastName _
as String, sFirstName as String, _
sAddress as String, sPhone as String)

    6. 现在就使用XML

  XML提供了一个管理状态和在应用程序的组件之间传递它的很好方法。 你可以用任何喜欢的格式来定义XML,然后可以从XML字符串插入、更新、删除或回顾任何信息。

  XML最好的一点是它的跨平台性并拥有独立的销售商。XML既不是Microsoft的技术也不是Sun的技术,是**网联盟W3C控制着这个标准,具体细节请参见原文。

  XML成为了.NET中的数据存取标准,所以现在就使用会使你一路领先。

  7. 定义灵活的界面

  界面(Interfaces)提供了一个在运行时间插入组件的很好途径。你可以定义一个界面,建立一个执行它的类,然后就可以在任何时间,很容易地用任何执行同一界面的其它类来代替这个类。

  例如,你可以开发一个邮件列表的标签打印组件,并且定义这个组件所要求的标准界面。在这个例子中,那个界面包括名字和地址信息。然后,任何执行这个界面的类都可以使用这个组件,而不需要对其进行任何修改。一个执行标签打印界面的客户类可以使用这个组件来打印客户邮件标签,一个执行标签打印界面的职员类可以使用这个组件来打印薪水标签等。

  8. 继承功能

  继承是OOP的一个关键原则,它允许你定义一个对象的基础功能,然后将那个功能应用于对象的特定子类中。

  例如,你可以为一个客户(Customer)对象定义基础功能,可以恢复和保存数据以及计算一个折扣。然后就可以定义一个政府客户对象,让它继承客户对象的所有功能,但是屏蔽掉折扣计算,因为给政府客户的折扣更高。

  在VB.NET中,你很快就能实现这些。

  9. 按照模型进行处理

  开发一个描述交易的域模型对于理解应用程序的目的以及它应该解决的问题是非常有用的。 但是有一个常见的错误是取得域的模型并试图为它编码。相反,你应该融合域模型到一个执行模型中,这个执行模型定义了如何建立类的方法。融合过程中,需要考虑许多因素,请参阅建立对象模型。

  10. 了解OOP,使用OOP

  所有的.NET都是面向对象的。现在你对OOP了解得越多,理解得越好,将来转移到.NET就越容易。在所有项目中都要准备好使用类模型。如果你不了解OOP,或者虽然你了解它但是并不真正理解它,可以采用一个OOP类。OOP技术很快就会成为你的第二个自我,你会发现想记起原来是怎么编码的都很困难。

面向对象软件开发的十大原则 (转一)
  对于开发人员来说,时时回过头来检查一下应用程序是非常有用的。考虑一下这个应用程序是否达到了预期目的?是否遵循了最佳方案?要想实现目的是否还存在更好的方法?温故而知新,也许你会从回顾中发现新的思路。
  本文就提出10种顶尖的面向对象的编程技术来帮助你对应用程序进行评价,希望你使用这10个顶尖技术从面向对象的编程中获得最大利益。
  1. 重新审视对象的声明操作
  如何声明对象变量会给对象的使用和性能带来重大的影响。你应该使用这个语法:
    Private m_oCust As Ccustomer
注意一定要使用正确的范围。在大多数情况下,你希望对象变量是私有的。如果只是在一个特定的程序中需要这个对象,就可以用Dim来声明它是属于那个程序私有的。如果在整个类或表单模块中都需要它的话,就应该用Private 在那个类或表单的顶部声明它。按照习惯,在模块层声明的变量要加一个前缀 "m_",以表示用于模块层或成员变量。如果需要从类外部使用对象变量,就不要将变量设成Public,宁可定义一个Property Get程序来使用对象变量,这样就防止了其它代码不用你不希望的方法去使用变量。
  在声明中,总是使用类名称,而不要使用 "As Object",因为使用 "As Object" 会使对象变量成为后期捆绑(late-bound)。这就意味着在运行时间之前,Visual Basic不知道你将使用的对象类型,因此它不能给你提供自动列表成分(对象属性和方法的下拉列表)。更重要的是,在运行时会对早期捆绑(early-bound)对象造成明显的性能冲击。
  最后,在声明中不要使用"New"这个关键字。使用关键字"New"时,将在引用它的任何时间自动创建对象,这对性能会造成轻微的影响,因为每个对象的属性和方法调用都必须进行校验以确定对象是否被创建了。使用"New"这个关键字还会给调试带来噩梦,因为在对象被破坏之后,如果你不经意地再次引用它的话,应用程序会自动地重新创建对象。当你准备移植到.NET时,理解这一点更加重要,因为在那里关键字"New"有一个不同的意义。在.NET中,关键字"New"允许你在声明时创建一个对象,比如下面这条语句将创建一个新的Customer对象:
   Private mo_Cust as New CCustomer()
  2. 重视对象清除行为
 当不再需要对象变量时,一定要释放它们,然后VB就会破坏与变量相联系的对象,从而释放应用程序中的内存。使用下面的代码释放一个对象:
   Set x = Nothing
  3. 封装的重大作用
  Encapsulate(封装)从字面上讲就是放入封套或"放在一个壳子中"。从面向对象编程 (OOP) 的角度来看,这意味着将所有与一个对象相关的数据和处理都放在它的类中。
  封装有时候指的是信息隐藏,因为你将对象的数据藏在了类的内部。如果其它的类需要使用数据,可以使用属性程序(.NET 中的属性声明)来暴露这些数据。这个行为为类提供了控制, 它控制用户可以如何观看或刷新数据,并且防止其它对象误用数据。
  封装使得对象之间的交互作用变得简单化。一个对象不用知道另一个对象的全部数据或者其数据是如何管理的,就能够使用这个对象。例如,一个Customer对象可能有名字和地址属性以及电话号码、信用历史和其它相关域。如果一个Invoice 对象想要使用一个 Customer 对象,它可以为发票请求名字和地址,而不需要知道Customer是从哪里获取那个数据的,或者其它数据元素是什么。
  对于如何为可扩展的标记语言(XML)将文档对象模型 (DOM) 封装在一个包装类中的解决方案,请参考此文,这使你能够更容易地与一个XML文件进行交互作用,而不需要知道DOM的细节。
  4. 状态监视问题
  良好的基于组件的开发技术会频繁地要求无状态的对象。无状态对象没有保持的属性,只有方法。你可以访问一个无状态的组件,执行方法,然后不需要获取方法调用间的任何数据就可以完成处理。
  有一些OOP方面的文章提出没有属性的对象就不是对象。 但是我们现在忽略这个语义上的问题,你可以创建有方法而没有属性的类。当你从事COM+或Web开发时,经常会需要这样做。一个ASP页面可以在一个组件上执行一个方法来完成一个特殊的处理,然后再释放组件,不用为管理对象状态而费心。
  5. 传递参数的可扩展性
  参数提供了类的方法之间传递信息的途径。你需要用参数向方法传递任何必要的数据,特别是当你需要无状态对象的时候。
  例如,无状态对象Customer 的PlaceOrder方法要求把客户的信息作为参数传递给它,然后PlaceOrder方法就用这个信息来完成必要的处理。

 

 

 

 

你可能感兴趣的:(C#knowledge)