More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  Endless Song of S.SPhotosProfileFriendsMore Tools Explore the Spaces community

Endless Song of S.S

---What we do in life, echos in eternity.---

Jeff

View spaceSend a message
Occupation:
Age:
Man r weak, so I must learn to be strong.
by 
by 
by 
by 
View space
View space
BrondonHeat
View space
茫然
View space
Watergrave
View space
kill jk
View space
洛洛---自由的飞翔
View space
Olivia Chung
View space
kittenzhang

No list items have been added yet.
March 21

写给自己-这最后的一年

一个人坐在家中 静静的想 破碎不堪的思绪 只存在于此刻
所有的事情都集中在这最后的一年发生 充满变数的一年
我的大学 就要离我远去 而我的未来 又在哪里
憧憬却又有些不敢想
 
有因必有果 似乎我们都逃不出因果循环 可是如果没有因 怎么会有果
看不清前方的路
 
前不久试着努力去做一件很想做的事 可终究没有做 写了很多东西 终于还是狠心扔掉了
终于能舍得 终于能放弃了
对我来说 真是欣慰

很久以前 一个敬爱的叔叔对我说 你最大的问题就是不舍得拒绝别人 不舍得放弃 你一定要学会拒绝 学会放弃 现在想起来 真是太对了

在以前 我可真是一个舍不得 放不下的人 对什么都想占着 不属于自己的也会嫉妒
我终于有些变化了
 
记得有人对我说过 将来我会变得不认得自己 不知道自己是谁
也许
我看待一切的方式都会变 也许以后 我再也不做我原先喜欢的事 而让自己去喜欢另一些事 也许我再也不过类似的生活 而换一种生活方式
 
可惜我愿意改变 渴望天翻地覆的革命 即使不认得原来的自己
可谁又真正认得呢
只要朋友们还认得就好
 
我总是在乎一些不该在乎的事 在乎一些不该在乎的人 老考虑别人的想法 活得真累
为自己活着 走自己的路 然而总想着这句话 却总也做不到 不想活得太自我 却似乎看到很多自我的人过得很好 矛盾
如果国父在世 是否得给我加冕个博爱的牌子呢
 
学习 工作 赚钱 也许是我未来十年的主题 也许是二十年 也许到永远都是
但这不应该就是生命的主题 我想 生命的意义 应该更多
 
许多人说 人生苦短 及时行乐 不要考虑明天 
仍然不敢苟同
但活在当下 倒是确实要比活在过去好
 
和朋友们在一起 是今年最开心的时刻
幸亏有你们
尤其要感谢cs同学对我的开导
 
手边的千纸鹤不再承载着什么信息和意义 它们已经随风远去 成为一段闪亮的回忆
 
我还活着
所以要好好的活着
善待自己
January 01

持之以恒

2008年 转眼自己也快该22岁了 生命的1/3基本已经过去了 得努力奋斗了 08年是最重要的一年 一定要加油
 
前段时间看了个电视剧 虽然很肥皂 但有句话我觉得说的很对 与其后悔过去做过的甚至想回到过去 不如决定去改变对未来的意志更为重要 共勉共勉
 
p.s. 祝所有人新年快乐! 生活事业爱情更上一层楼!
 
 
August 18

今天很开心

今天真的很开心 见到了很多人 尽管也没见到不少人
大家都过的都很不错  很棒 也都很幸福
我替大家感到开心 真心的
看到你们这么的快乐 我心中的快乐是无法言喻的
尽管生活中有苦 有累 有难 有恨 但总有东西能让你快乐 这就够了
你们在我心中不只是朋友 也不仅是同学 我也无法说清那是什么 总之 在某个时候 我们总会聚在一起 开怀 大笑
 
学校也越来越好了 现在的学生真幸福 希望以后能常回去看看
 
明天就要出发了 希望11天的旅行能给我带来更多的快乐^o^
 
Best widhes for everyone.
 
Bye for a while.
August 14

从朋友那转的..很老很无聊..

上大学:
大门 是雄伟的
进去 会后悔的
宿舍 是简陋的
价格 是不菲的
上课 是很闲的
社团 是骗钱的
恋爱 是常见的
生活 是贫贱的
学习 是无奈的
睡觉 是最爽的
理想 是奇怪的
未来 是实在的
考好 是意外的
重修 是常在的
作弊 是被迫的
开卷 是不错的
昼夜 是不分的
精神 是不振地
废话 是没完的
发疯 是常有的
自杀 是勇敢的
上了大学才知道. 玩游戏是要通宵的!
上了大学才知道. 原来大学和养老院是差不多的!!
上了大学才知道. 馒头一捏就能变汤圆的!!
上了大学才知道. 所有的课都能跷,只有体育课是不跷的!!
上了大学才知道. 打水是用来泡面的!!
上了大学才知道. 开学校原来是那么赚钱的!!
上了大学才知道. 二锅头是用来干的!!
上了大学才知道. 一门课一学期可以就考试那天去一次的!!
上了大学才知道. 一头猪是可以供食堂吃几天的!!
上了大学才知道. 原来理想只是高中以前才有的 
 
 
尽管很老很无聊 我还是想笑..最近总是很单纯的傻笑...哈哈~哈哈~哈哈~
July 26

再转一个小note 处理C#, JAVA内存泄漏的方法

大多数的程序员(特别新手)以为:用这样的语言编程的一个好处就是无需再考虑内存的分配和释放。你只需创建对象,然后通过一种叫做垃圾收集的机制来处理这些对象,也就是说:当它们不再被应用程序需要的时候来自动的清除这些对象。这个过程意味着Java或C#解决了其它语言中极难处理的问题──可怕的内存泄露。是这样吗?

     其实不然,让我们先了解一下垃圾收集的工作原理。垃圾收集器的工作就是寻找那些不再被应用程序需要的对象,当它们不会再被访问或引用的时候清除它们。(注意是在不会再被访问或引用的时候才清除它们

     例如:class A 引用 class B的实例

         class B

         {

         }

         class A

         {

             B refB = new B();//在这里开始引用了

             //这样在class A 的整个生命周期内,refB所占的内存空间将不会得到释放

             //因为垃圾收集器会一直认为实例B一直被refB引用

         }

         正确的处理方式是

         class A

         {

             B refB = new B();//在这里开始引用了

             //使用了refB一段时间后

             //如果下面的程序不在需要refB,那么就将它设置为null

            

             refB = null; //这样垃圾收集器就会将实例B所占用的内存空间回收掉

         }

所以我们需要明确的极为关键的一点是:一个对象只有当它不再被引用的时候才会被当作是无用的。

         {

             B refB = new B();//在这里开始引用了

             //使用了refB一段时间后

             //如果下面的程序不在需要refB,那么就将它设置为null

             10pt; LINE-HEIGHT: 150%; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family:     'Times New Roman'">            

             refB = null; //这样垃圾收集器就会将实例B所占用的内存空间回收掉

         }

所以我们需要明确的极为关键的一点是:一个对象只有当它不再被引用的时候才会被当作是无用的。

 
July 17

初学c#,转篇比较浅的文章,就是太长了..

摘要:C#是建立在C++的语法和语义的,可以让C语言编程人员利用.NET和通用语言运行库带来的便利。尽管从C++转向C#是相对容易的,但仍然有些地方值得我们注意。
在这篇文章中我们将探索其中的一些新特性,如碎片收集、属性、foreach-loop循环和界面等。 

      每隔10年左右,编程人员就需要花费大量的时间和精力去学习新的编程技术。在80年代是Unix和C,90年代是Windows和C++,现在又轮到了微软的.NETFramework和C#。尽管需要学习新的技术,但由此带来的好处却远高于付出的劳动。幸运的是,使用C#和.NET进行的大多数工程的分析和设计与在C++和Windows中没有本质的变化。在本篇文章中,我将介绍如何实现由C++到C#的飞跃。 

      已经有许多文章介绍过C#对C++的改进,在这里我就不再重复这些问题了。在这里,我将重点讨论由C++转向C#时最大的变化:由不可管理的环境向可管理的环境的变化。此外,我还会提出一些C#编程人员容易犯的错误供大家参考,此外,还将说明一些C#语言的能够影响编程的新功能。 

转向可管理的环境 

      C++的设计目标是低级的、与平台无关的面向对象编程语言,C#则是一种高级的面向组件的编程语言。向可管理环境的转变意味着你编程方式思考的重大转变,C#不再处理细微的控制,而是让架构帮助你处理这些重要的问题。例如,在C++中,我们就可以使用new在栈中、堆中、甚至是内存中的某一特定位置创建一个对象。 

      在.NET的可管理环境中,我们再不用进行那样细微的控制了。在选择了要创建的类型后,它的位置就是固定的了。简单类型(ints、double和long)的对象总是被创建在栈中(除非它们是被包含在其他的对象中),类总是被创建在堆中。我们无法控制对象是创建在堆中哪个位置的,也没有办法得到这个地址,不能将对象放置在内存中的某一特定位置。(当然也有突破这些限制的方法,但那是很另类的方法。)我们再也不能控制对象的生存周期,C#没有destructor。碎片收集程序会将对象所占用的内存进行回收,但这是非显性地进行的。 

      正是C#的这种结构反映了其基础架构,其中没有多重继承和模板,因为在一个可管理的碎片收集环境中,多重继承是很难高效地实现的。 

      C#中的简单类型仅仅是对通用语言运行库(CLR)中类型的简单映射,例如,C#中的int是对System.Int32的映射。C#中的数据类型不是由语言本身决定的,而是由CLR决定的。事实上,如果仍然想在C#中使用在VisualBasic中创建的对象,就必须使自己的编程习惯更符合CLR的规定。 

      另一方面,可管理的环境和CLR也给我们带来了好处。除了碎片收集和所有.NET语言中统一的数据类型外,它还提供给我们一个功能强大的面向组件的编程语言,无须对后期绑定提供特别的支持,类型发现和后期绑定都是被内置在语言中的。属性是C#语言中的第一类的成员,事件和代理也是。 

      可管理环境最主要的优点是.NETFramework。尽管在所有的.NET语文中都可以使用这种框架,但C#可以更好地使用.NET框架中丰富的类、接口和对象。 

Traps 

      C#看起来与C++非常相似,这使得我们在由C++转向C#时比较轻松,但其中也有一些容易出错的地方。在C++中编写得非常漂亮的代码,在C#中会不能通过编译,甚至会出现意想不到的结果。C#与C++之间在语法上的变化并不大,编译器能够发现这二者之间大部分的差异,我在这里就不再多费笔墨了,在这里我介绍几个容易出问题的比较重要的变化: 

引用类型和值类型 

      在C#中,值类型和引用类型数据是有区别的。简单类型(int、long、double等)和结构属于值类型数据,类和对象属于引用类型数据。除非是包含在引用类型的变量中,与在C++中一样,值类型变量的值存储在栈中。引用类型的变量也存储在栈中,但它的值是一个存储在堆中的对象的地址,这一点也与C++类似。值类型变量是将自己的值传递给方法,而引用类型变量则将自己的指针传递给方法。 

结构 

      C#中的结构与C++中有非常明显的区别。在C++中,结构更象是类,除了缺省的继承外,其缺省的访问权限是public而不是private。在C#中,结构与类截然不同,它是用来封装轻型对象的,是值类型的数据类型,在传递时传送的是变量的值,而不是其地址。此外,它们也有一些不适用于类的限制,例如,它是不能继承的,也没有除System.ValueType之外的基本类。结构还不能定义一个缺省的constructor。 

      另一方面,由于结构比类的效率要高,因此它非常适合于创建轻型对象。因此,如果它的缺点对你的软件没有影响,使用结构比使用类效率要高得多,尤其是对于小对象而言。 

所有的一切都是对象 

      在C#中,所有的东西都是由继承Object得到的,包括创建的类和int、structs等值类型的变量。Object类提供了一些有用的方法,例如ToString,使用ToString的一个例子是与System.Console.WriteLine一起使用,它可以接受一个字符串和许多对象。与使用printf语句不同,要使用WriteLine,需要提供代换变量。假设myEmployee是用户定义的Employee类的一个实例,myCounter是用户定义的Counter类的一个实例: 

      Console.WriteLine("Theemployee:,thecountervalue:",
myEmployee,myCounter); 

      其中的WriteLine会调用每个对象的Object.ToString方法,替换作为参数返回的变量。如果Employee类不覆盖ToString,就会调用缺省的实现(由System.Object继承得到的),它将把类的名字作为一个字符串返回。Counter会覆盖ToString,返回一个整型的变量,因此,上面代码的输出为: 

Theemployee:Employee,thecountervalue:12 

      如果向WriteLine传递一个整型变量会发生什么情况呢?由于不能对整型变量调用ToString,编译器将自动将整型变量封装在一个对象的实例中。当WriteLine调用ToString时,对象就会返回表示整型变量值的字符串。下面的代码就说明了这个问题: 

类的使用 

usingSystem;
//不覆盖ToString的类
publicclassEmployee
{
}
//覆盖了ToString的类
publicclassCounter
{
      privateinttheVal;
      publicCounter(inttheVal)
      {
            this.theVal=theVal;
      }
      publicoverridestringToString()
      {
            Console.WriteLine("CallingCounter.ToString()");
            returntheVal.ToString();
      }
}

publicclassTester
{
      publicstaticvoidMain()
      {
            //创建类的实例
            Testert=newTester();
            //调用非静态成员
            //(mustbethroughaninstance)
            t.Run();
      }
      //演示调用ToString的非静态方法
      publicvoidRun()
      {
            EmployeemyEmployee=newEmployee();
            CountermyCounter=newCounter(12);
            Console.WriteLine("Theemployee:,thecountervalue:",
            myEmployee,myCounter);
            intmyInt=5;
            Console.WriteLine("Herearetwointegers:and",17,myInt);
      }


引用型参数和输出型参数 

      与C++中相同,C#中的方法也只能有一个返回值。在C++中,我们通过将指针或索引作为参数而克服了这个限制,被调用的方法改变其中的参数,调用方法就可以得到新的值了。 

      向方法中传递一个索引作为参数时,只能严格地按传递索引或指针所能够提供的方式访问原来的对象。对于值类型变量而言,就不能采用这种方法了。如果要通过引用型参数传递值型变量,就需要在其前面加上ref关健字。如下所示: 

publicvoidGetStats(refintage,refintID,refintyearsServed) 

需要注意的是,既需要在方法的定义中使用ref关健字,也需要在对方法的实际调用中使用ref关健字。 

Fred.GetStats(refage,refID,refyearsServed); 

      现在,我们可以在调用方法中定义age、ID和yearsServed变量,并将它们传递给GetStats,得到改变后的值。 

      C#要求明确的赋值,也就是说,在调用GetStats方法之前,必须对age、ID和yearsServed这三个局部变量进行初始化,这一工作似乎有点多余,因为我们仅仅使用它们从GetStats中得到新的变量的值。为了解决这一问题,C#提供了out关健字,表示我们可以向方法中传递没有被初始化的变量,这些变量将通过引用变量的方式进行传递: 

publicvoidGetStats(outintage,outintID,outintyearsServed) 

当然了,调用方法也必须作出相应的变化: 

Fred.GetStats(outage,outID,outyearsServed); 

New的调用 

      在C++中,new关健字可以在堆上生成一个对象。在C#中却不是这样。对于引用类型变量而言,new关健字在堆上生成一个对象;对于结构等值类型变量而言,new关健字在栈中生成一个对象,并需要调用constructor。 

      事实上,我们可以不使用new关健字而在栈上生成一个结构类型的变量,但这时需要注意的是,New关健字能够初始化对象。如果不使用new,则在使用前必须手工地对结构中的所有成员进行初始化,否则在编译时会出错。 

对象的初始化 

usingSystem;//有二个成员变量和一个构造器的简单结构
publicstructPoint
{
      publicPoint(intx,inty)
      {
            this.x=x;
            this.y=y;
      }
      publicintx;
      publicinty;
}

publicclassTester
{
      publicstaticvoidMain()
      {
            Testert=newTester();
            t.Run();
      }

      publicvoidRun()
      {
            Pointp1=newPoint(5,12);
            SomeMethod(p1);//fine

            Pointp2;//不调用new而直接创建

            //编译器编译到这里时会出错,因为p2的成员变量没有被初始化
            //SomeMethod(p2);

            //手工对它们进行初始化
            p2.x=1;
            p2.y=2;

            SomeMethod(p2);
      }
      //一个可以接受Point作为参数的方法
      privatevoidSomeMethod(Pointp)
      {
            Console.WriteLine("Pointatx",p.x,p.y);
      }
}

属性 

      大多数的C++编程人员都希望使成员变量的属性为private,这种隐藏数据的想法促进了数据封装概念的出现,使我们能够在不改变用户依赖的接口的情况下而改变类的实现。通常情况下,我们只希望客户获取或设置这些成员变量的值。因此,C++编程人员开发出了用来存取private成员变量的存取器。 

      在C#中,属性是类的第一级成员。对于客户而言,属性看起来象一个成员变量。对于类的实现者而言,它看起来更象是方法。这种设计很巧妙,既可以实现数据的隐藏和封装,又可以使客户很方便地访问成员变量。 

      我们可以在Employee类中添加一个Age属性,使客户可以很方便地获取和设置员工年龄这个类的成员: 
publicintAge
{
      get
      {
            returnage;
      }
      set
      {
            age=value;
      }


关健字value可以被属性隐性地使用。如果编写如下的代码: 

      Fred.Age=17; 

编译器将会把值17传递给value。 

通过只采用Get而不采用Set,我们可以为YearsServed创建一个只读的属性: 
publicintYearsServed
{
      get
      {
            returnyearsServed;
      }
}

Accessors的使用
privatevoidRun()
{
      EmployeeFred=newEmployee(25,101,7);
      Console.WriteLine("Fred'sage:",Fred.Age);
      Fred.Age=55;
      Console.WriteLine("Fred'sage:",Fred.Age);
      Console.WriteLine("Fred'sservice:",Fred.YearsServed);
      //Fred.YearsServed=12;//是不被允许的
}

      我们可以通过属性获取Fred的年龄,也可以使用这一属性设置年龄。我们虽然可以访问YearsServed属性获得它的值,但不能设置值。如果没有注释掉最后一行的代码,在编译时就会出错。 如果以后决定从数据库中获取Employee的年龄,我们就只需要改变存取器的实现,而客户不会受到任何影响。 

数组 

      C#提供了一个数组类,它比C/C++中传统的数组更智能化。例如,在C#中写数组时不会超出边界。此外,数组还有一个更智能的伙伴—ArrayList,可以动态地增长,管理对数组大小不断变化的需求。 

      C#中的数组有三种形式:一维数组、多维均匀数组(象C++中传统的数组那样)、非均匀数组(数组的数组)。我们可以通过下面的代码创建一维数组: 

      int[]myIntArray=newint[5]; 

另外,还可以以如下的方式对它进行初始化: 

      int[]myIntArray=; 

我们可以通过如下方式创建一个4×3的均匀数组: 

      int[,]myRectangularArray=newint[rows,columns]; 

我们可以按如下方式对该数组进行初始化: 
int[,]myRectangularArray=
{
      ,,,
};
 
由于非均匀数组是数组的数组,因此,我们只能创建一维非均匀数组: 

int[][]myJaggedArray=newint[4][]; 

然后再创建内部的每个数组: 

myJaggedArray[0]=newint[5];
myJaggedArray[1]=newint[2];
myJaggedArray[2]=newint[3];
myJaggedArray[3]=newint[5]; 

由于数组是由继承System.Array对象而得到的,因此,它们带有许多包括Sort、Reverse在内的许多有用的方法。 

索引器 

我们可以创建象数组一样的对象。例如,我们可以创建一个显示一系列字符串的列表框,可以把列表框当作一个数组,使用一个索引就可以很方便地访问列表框中的内容。 

stringtheFirstString=myListBox[0];
stringtheLastString=myListBox[Length-1]; 

      这是通过索引器完成的。索引器在很大程度上象一个属性,但支持索引操作的语法。图4显示了一个后面跟着索引操作符的属性,图5显示如何完成一个很简单的ListBox类并对它进行索引: 

界面 

      软件界面是二种对象之间如何进行交互的契约。如果一个对象发布了一个界面,就等于向所有可能的客户声明:我支持下面的方法、属性、事件和索引器。 

      C#是一种面向对象的语言,因此这些契约被封装在一个被称作界面的实体中,界面定义了封装着契约的引用型类型的对象。从概念上来讲,界面与抽象类非常相似,二者的区别是抽象类可以作为一系列衍生类的基础类,界面则是与其他继承树结合在一起的。 

IEnumerable界面 

      再回到上面的例子中。象在普通的数组中那样,使用foreach-loop循环结构就能够很好地打印ListBoxTest类中的字符串,通过在类中实现IEnumerable界面就能实现,这是由foreach-loop循环结构隐性地完成的。在任何支持枚举和foreach-loop循环的类中都可以实现IEnumerable界面。 

      IEnumerable界面只有一个方法GetEnumerator,其任务是返回一个特别的IEnumerator的实现。从语法的角度来看,Enumerable类能够提供一个IEnumerator。 

Figure5ListBoxClass
usingSystem;
//简化的ListBox控制
publicclassListBoxTest
{
      //用字符串初始化该ListBox
      publicListBoxTest(paramsstring[]initialStrings)
      {
            //为字符串分配空间
            myStrings=newString[256];
            //把字符串拷贝到构造器中
            foreach(stringsininitialStrings)
            {
                  myStrings[myCtr++]=s;
            }
      }
      //在ListBox的末尾添加一个字符串
      publicvoidAdd(stringtheString)
      {
            myStrings[myCtr++]=theString;
      }
      publicstringthis[intindex]
      {
            get
            {
                  if(index<0||index>=myStrings.Length)
                  {
                        //处理有问题的索引
                  }
                  returnmyStrings[index];
            }
            set
            {
                  myStrings[index]=value;
            }
      }
      //返回有多少个字符串
      publicintGetNumEntries()
      {
            returnmyCtr;
      }
      privatestring[]myStrings;
      privateintmyCtr=0;
}
publicclassTester
{
      staticvoidMain()
      {
            //创建一个新的列表并初始化
            ListBoxTestlbt=newListBoxTest("Hello","World");
            //添加一些新字符串
            lbt.Add("Who");
            lbt.Add("Is");
            lbt.Add("John");
            lbt.Add("Galt");
            stringsubst="Universe";
            lbt[1]=subst;
            //访问所有的字符串
            for(inti=0;i]:",i,lbt[i]);}
      }


      Enumerator必须实现IEnumerator方法,这可以直接通过一个容器类或一个独立的类实现,后一种方法经常被选用,因为它可以将这一任务封装在Enumerator类中,而不会使容器类显得很混乱。我们将在上面代码中的ListBoxTest中添加Enumerator类,由于Enumerator类是针对我们的容器类的(因为ListBoxEnumerator必须清楚ListBoxTest的许多情况),我们将使它在ListBoxTest中成为不公开的。在本例中,ListBoxTest被定义来完成IEnumerable界面,IEnumerable界面必须返回一个Enumerator。 

publicIEnumeratorGetEnumerator()
{
      return(IEnumerator)newListBoxEnumerator(this);


      注意,方法将当前的ListBoxTest对象(this)传递给Enumerator,这将使Enumerator枚举这一指定的ListBoxTest对象中的元素。 

      实现这一类的Enumerator在这里被实现为ListBoxEnumerator,它在ListBoxTest中被定义成一个私有类,这一工作是相当简单的。 

      被枚举的ListBoxTest作为一个参数被传递给constructor,ListBoxTest被赋给变量myLBT,构造器还会将成员变量index设置为-1,表明对象的枚举还没有开始。 

publicListBoxEnumerator(ListBoxTesttheLB)
{
      myLBT=theLB;
      index=-1;


      MoveNext方法对index进行加1的操作,然后确保没有超过枚举的对象的边界。如果超过边界了,就会返回false值,否则返回true值。 

publicboolMoveNext()
{
      index++;
      if(index>=myLBT.myStrings.Length)
            returnfalse;
      else
            returntrue;


Reset的作用仅仅是将index的值设置为-1。 

      Current返回最近添加的字符串,这是一个任意的设定,在其他类中,Current可以有设计人员确定的意义。无论是如何设计的,每个进行枚举的方法必须能够返回当前的成员。 

publicobjectCurrent
{
      get
      {
            return(myLBT[index]);
      }


      对foreach循环结构的调用能够获取枚举的方法,并用它处理数组中的每个成员。由于foreach循环结构将显示每一个字符串,而无论我们是否添加了一个有意义的值,我们将myStrings的初始化改为8个条目,以保证显示的易于处理。 

myStrings=newString[8]; 

使用基本类库 

      为了更好地理解C#与C++的区别和解决问题方式的变化,我们先来看一个比较简单的例子。我们将创建一个读取文本文件的类,并在屏幕上显示其内容。我将把它做成多线程程序,以便在从磁盘上读取数据时还可以做其他的工作。 

      在C++中,我们可能会创建一个读文件的线程和另一个做其他工作的线程,这二个线程将各自独立地运行,但可能会需要对它们进行同步。在C#中,我们也可以完成同样的工作,由于.NET框架提供了功能强大的异步I/O机制,在编写线程时,我们会节省不少的时间。 

      异步I/O支持是内置在CLR中的,而且几乎与使用正常的I/O流类一样简单。在程序的开始,我们首先通知编译器,我们将在程序中使用许多名字空间中的对象: 

usingSystem;
usingSystem.IO;
usingSystem.Text; 

      在程序中包含System,并不会自动地包含其所有的子名字空间,必须使用using关健字明确地包含每个子名字空间。我们在例子中会用到I/O流类,因此需要包含System.IO名字空间,我们还需要System.Text名字空间支持字节流的ASCII编码。 

      由于.NET架构为完成了大部分的工作,编写这一程序所需的步骤相当简单。我们将用到Stream类的BeginRead方法,它提供异步I/O功能,将数据读入到一个缓冲区中,当缓冲区可以处理时调用相应的处理程序。 

      我们需要使用一个字节数组作为缓冲区和回叫方法的代理,并将这二者定义为驱动程序类的private成员变量。 

publicclassAsynchIOTester
{
      privateStreaminputStream;
      privatebyte[]buffer;
      privateAsyncCallbackmyCallBack; 

      inputStream是一个Stream类型的变量,我们将对它调用BeginRead方法。代理与成员函数的指针非常相似。代理是C#的第一类元素。 

      当缓冲区被磁盘上的文件填满时,.NET将调用被代理的方法对数据进行处理。在等待读取数据期间,我们可以让计算机完成其他的工作。(在本例中是将1个整型变量由1增加到50000,但在实际的应用程序中,我们可以让计算机与用户进行交互或作其他有意义的工作。) 

      本例中的代理被定义为AsyncCallback类型的过程,这是Stream的BeginRead方法所需要的。System空间中AsyncCallback类型代理的定义如下所示: 

publicdelegatevoidAsyncCallback(IAsyncResultar); 

      这一代理可以是与任何返回void类型值、将IAsyncResult界面作为参数的方法相关联的。在该方法被调用时,CLR可以在运行时传递IAsyncResult界面对象作为参数。我们需要如下所示的形式定义该方法: 

voidOnCompletedRead(IAsyncResultasyncResult) 

然后在构造器中与代理连接起来: 

AsynchIOTester()
{
      ???
      myCallBack=newAsyncCallback(this.OnCompletedRead);


      上面的代码将代理的实例赋给成员变量myCallback。下面是全部程序的详细工作原理。在Main函数中,创建了一个类的实例,并让它开始运行: 

publicstaticvoidMain()
{
      AsynchIOTestertheApp=newAsynchIOTester();
      theApp.Run();


      new关健字能够启动构造器。在构造器中我们打开一个文件,并得到一个Stream对象。然后在缓冲中分配空间并与回调机制联结起来。 

AsynchIOTester()
{
      inputStream=File.OpenRead(@"C:MSDNfromCppToCS.txt");
      buffer=newbyte[BUFFER_SIZE];
      myCallBack=newAsyncCallback(this.OnCompletedRead);


在Run方法中,我们调用了BeginRead,它将以异步的方式读取文件。 

inputStream.BeginRead(buffer,//存放结果
      0,//偏移量
      buffer.Length,//缓冲区中有多少字节
      myCallBack,//回调代理
      null);//本地对象 

这时,我们可以完成其他的工作。 

for(longi=0;i<50000;i++)
{
      if(i%1000==0)
      {
            Console.WriteLine("i:",i);
      }


文件读取操作结束后,CLR将调用回调方法。 

voidOnCompletedRead(IAsyncResultasyncResult)


      在OnCompletedRead中要做的第一件事就是通过调用Stream对象的EndRead方法找出读取了多少字节: 
      intbytesRead=inputStream.EndRead(asyncResult); 

      对EndRead的调用将返回读取的字节数。如果返回的数字比0大,则将缓冲区转换为一个字符串,然后将它写到控制台上,然后再次调用BeginRead,开始另一次异步读的过程。 
      if(bytesRead>0)
      {
            Strings=Encoding.ASCII.GetString(buffer,0,bytesRead);
            Console.WriteLine(s);
            inputStream.BeginRead(buffer,0,buffer.Length,
            myCallBack,null);
      } 

      现在,在读取文件的过程中就可以作别的工作了(在本例中是从1数到50000),但我们可以在每次缓冲区满了时对读取的数据进行处理(在本例中是向控制台输出缓冲区中的数据)。有兴趣的读者可以点击此处下载完整的源代码。 

      异步I/O的管理完全是由CLR提供的,这样,在网络上读取文件时,会更好些。 

在网络上读取文件 

      在C++中,在网络上读取文件需要有相当的编程技巧,.NET对此提供了广泛的支持。事实上,在网络上读取文件仅仅是基础类库中Stream类的另一种应用。 

      首先,为了对TCP/IP端口(在本例中是65000)进行监听,我们需要创建一个TCPListener类的实例。 

TCPListenertcpListener=newTCPListener(65000); 

      一旦创建后,就让它开始进行监听。 

tcpListener.Start(); 

      现在就要等待客户连接的要求了。 

SocketsocketForClient=tcpListener.Accept(); 

      TCPListener对象的Accept方法返回一个Socket对象,Accept是一个同步的方法,除非接收到一个连接请求它才会返回。如果连接成功,就可以开始向客户发送文件了。 

if(socketForClient.Connected)
{
???
 
      接下来,我们需要创建一个NetworkStream类,将报路传递给constructor: 

NetworkStreamnetworkStream=newNetworkStream(socketForClient); 

      然后创建一个StreamWriter对象,只是这次不是在文件上而是在刚才创建的NetworkStream类上创建该对象: 

System.IO.StreamWriterstreamWriter=
newSystem.IO.StreamWriter(networkStream); 

当向该流写内容时,流就通过网络被传输给客户端。 

客户端的创建 

客户端软件就是一个TCPClient类的具体例子,TCPClient类代表连向主机的一个TCP/IP连接。 

TCPClientsocketForServer;
socketForServer=newTCPClient("localHost",65000); 

有了TCPClient对象后,我们就可以创建NetworkStream对象了,然后在其上创建StreamReader类: 

NetworkStreamnetworkStream=socketForServer.GetStream();
System.IO.StreamReaderstreamReader=
newSystem.IO.StreamReader(networkStream); 

现在,只要其中有数据就读取该流,并将结果输出到控制台上。 

do
{
      outputString=streamReader.ReadLine();
      if(outputString!=null)
      {
            Console.WriteLine(outputString);
      }
}
while(outputString!=null); 

为了对这一段代码进行测试,可以创建如下一个测试用的文件: 

Thisislineone
Thisislinetwo
Thisislinethree
Thisislinefour 

这是来自服务器的输出: 

Output(Server)
Clientconnected
SendingThisislineone
SendingThisislinetwo
SendingThisislinethree
SendingThisislinefour
Disconnectingfromclient...
Exiting... 

下面是来自客户端的输出: 

Thisislineone
Thisislinetwo
Thisislinethree
Thisislinefour  

属性和元数据 

      C#和C++之间一个显著的区别是它提供了对元数据的支持:有关类、对象、方法等其他实体的数据。属性可以分为二类:一类以CLR的一部分的形式出现,另一种是我们自己创建的属性,CLR属性用来支持串行化、排列和COM协同性等。一些属性是针对一个组合体的,有些属性则是针对类或界面,它们也被称作是属性目标。 

将属性放在属性目标前的方括号内,属性就可以作用于它们的属性目标。 

[assembly:AssemblyDelaySign(false)]
[assembly:AssemblyKeyFile(".\keyFile.snk")] 

或用逗号将各个属性分开: 

[assembly:AssemblyDelaySign(false),
assembly:AssemblyKeyFile(".\keyFile.snk")] 

自定义的属性 

      我们可以任意创建自定义属性,并在认为合适的时候使用它们。假设我们需要跟踪bug的修复情况,就需要建立一个包含bug的数据库,但需要将bug报告与专门的修正情况绑定在一块儿,则可能在代码中添加如下所示的注释: 

//Bug323fixedbyJesseLiberty1/1/2005. 

      这样,在源代码中就可以一目了然地了解bug的修正情况,但如果如果把相关的资料保存在数据库中可能会更好,这样就更方便我们的查询工作了。如果所有的bug报告都使用相同的语法那就更好了,但这时我们就需要一个定制的属性了。我们可能使用下面的内容代替代码中的注释: 

[BugFix(323,"JesseLiberty","1/1/2005")Comment="Offbyoneerror"] 

与C#中的其他元素一样,属性也是类。定制化的属性类需要继承System.Attribute: 

publicclassBugFixAttribute:System.Attribute 

我们需要让编译器知道这个属性可以跟什么类型的元素,我们可以通过如下的方式来指定该类型的元素: 

[AttributeUsage(AttributeTargets.ClassMembers,AllowMultiple=true)] 

      AttributeUsage是一个作用于属性的属性━━元属性,它提供的是元数据的元数据,也即有关元数据的数据。在这种情况下,我们需要传递二个参数,第一个是目标(在本例中是类成员。),第二个是表示一个给定的元素是否可以接受多于一个属性的标记。AllowMultiple的值被设置为true,意味着类成员可以有多于一个BugFixAttribute属性。如果要联合二个属性目标,可以使用OR操作符连接它们。 

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface,AllowMultiple=true)] 

上面的代码将使一个属性隶属于一个类或一个界面。 

      新的自定义属性被命名为BugFixAttribute。命名的规则是在属性名之后添加Attribute。在将属性指派给一个元素后,编译器允许我们使用精简的属性名调用这一属性。因此,下面的代码是合法的: 

[BugFix(123,"JesseLiberty","01/01/05",Comment="Offbyone")] 

编译器将首先查找名字为BugFix的属性,如果没有发现,则查找BugFixAttribute。 

      每个属性必须至少