C#面向对象学习心得

时间:2019-05-13 16:08:39下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《C#面向对象学习心得》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《C#面向对象学习心得》。

第一篇:C#面向对象学习心得

一、封装

这是一种隐藏信息的特性。拿本节引例来说,类CalculateDate 将数据结构与算法隐藏在类的内部,外界使用者无需知道具体技术实现细节即可使用此类。封装这一特性不仅大大提高了代码的易用性,而且还使得类的开发者可以方便地更换新的算法,这种变化不会影响使用类的外部代码。可以用以下公式展示类的封装特性:封装的类=数据+对此数据所进行的操作(即算法)。通俗地说,封装就是:包起外界不必需要知道的东西,只向外界展露可供展示的东西。在面向对象理论中,封装这个概念拥有更为宽广的含义。小到一个简单的数据结构,大到一个完整的软件子系统,静态的如某软件系统要收集数据信息项,动态的如某个工作处理流程,都可以封装到一个类中。具备这种“封装”的意识,是掌握面向对象分析与设计技巧的关键。

二、继承

继承是面向对象编程中一个非常重要的特性,它也是另一个重要特性——多态的基础。现实生活中的事物都归属于一定的类别。在一些书中,将父类称为超类(super class)。“继承”关系有时又称为“派生”关系,“B 继承自A”,可以说为“B 派生自A”,或反过来说,“A 派生出B”。父类与子类之间拥有以下两个基本特性:

(1)是一种(IS-A)关系:子类是父类的一种特例。

(2)扩充(Extends)关系:子类拥有父类所没有的功能。

1.类成员的访问权限

面向对象编程的一大特点就是可以控制类成员的可访问性。当前主流的面向对象语言都拥有以下三种基本的可访问性:

(1)公有 public 访问不受限制。

(2)私有 private 只有类自身成员可以访问。

(3)保护 protected 子类可以访问,其他类无法访问。

由此可见,可以通过子类对象访问其父类的所有公有成员,事实上,外界根本分不清楚对象的哪些公有成员来自父类,哪些公有成员来自子类自身。小结一下继承条件下的类成员访问权限:

(1)所有不必让外人知道的东西都是私有的。

(2)所有需要向外提供的服务都是公有的。

(3)所有的“祖传绝招”,“秘不外传”的都是保护的。

C#中还有一种可访问性,就是由关键字internal 所确定的“内部”访问性。internal 有点像public,外界类也可以直接访问声明为internal 的类或类的成员,但这只局限于同一个程序集内部。

读者可以简单地将程序集理解为一个独立的DLL 或EXE 文件。一个DLL 或EXE 文件中可以有多个类,如果某个类可被同一程序集中的类访问,但其他程序集中的类不能访问它,则称此类具有internal 访问性。internal 是C#的默认可访问性,这就是说,如果某个类没有任何可访问性关键字在它前面,则它就是internal 的。

2.子类父类变量的相互赋值

子类对象可以被当成基类对象使用。这是因为子类对象本就是一种(IS_A)父类对象,因此,以下代码是合法的:

Parent p;

Son c = new Son();

p = c;

然而,反过来就不可以,父类对象变量不可以直接赋值给子类变量。如果确信父类变量中所引用的对象的确是子类类型,则可以通过类型强制转换进行赋值,其语法格式为: 子类对象变量=(子类名称)基类对象变量;

子类对象变量=基类对象变量 as 子类名称;

3.方法重载、隐藏与虚方法调用

由于子类对象同时汇集了父类和子类的所有公共方法,而C#并未对子类和父类的方法名称进行过多限制,因此,一个问题出现了:如果子类中某个方法与父类方法的签名一样(即方法名和方法参数都一样),那当通过子类对象访问此方法时,访问的是子类还是父类所定义的方法?让我们先从子类方法与父类方法之间的关系说起。总的来说,子类方法与父类方法之间的关系可以概括为以下三种:

(1)扩充(Extend):子类方法,父类没有;

(2)重载(Overload):子类有父类的同名函数,但参数类型或数目不一样;

(3)完全相同:子类方法与父类方法从方法名称到参数类型完全一样。

当子类与父类拥有完全一样的方法时,称“子类隐藏了父类的同名方法,当分别位于父类和子类的两个方法完全一样时,调用哪个方法由对象变量的类型决定。“new”关键字明确告诉C#编译器,子类隐藏父类的同名方法,提供自己的新版本。如果子类隐藏了父类的同名方法,要在子类方法的实现代码中调用父类被隐藏的同名方法时要使用base 关键字。如果子类隐藏了父类的同名方法,不进行强制转换,就无法通过父类变量直接调用子类的同名方法,哪怕父类变量引用的是子类对象。这是不太合理的。我们希望每个对象都只干自己职责之内的事,即如果父类变量引用的是子类对象,则调用的就是子类定义的方法,而如果父类变量引用的就是父类对象,则调用的是父类定义的方法。这就是说,希望每个对象都“各人自扫门前雪,莫管他人瓦上霜”。为达到这个目的,可以在父类同名方法前加关键字virtual,表明这是一个虚方法,子类可以重写此方法:即在子类同名方法前加关键字override,表明对父类同名方法进行了重写。所以,将父类方法定义为虚方法,子类重写同名方法之后,通过父类变量调用此方法,到底是调用父类还是子类的,由父类变量引用的真实对象类型决定,而与父类变量无关!很明显,“虚方法调用”特性可以让我们写出非常灵活的代码,大大减少由于系统功能

扩充和改变所带来的大量代码修改工作量。由此给出结论:面向对象语言拥有的“虚方法调用”特性,使我们可以只用同样的一个语句,在运行时根据对象类型而执行不同的操作。

三、抽象

1.抽象类与抽象方法

在一个类前面加上“abstract”关键字,此类就成为了抽象类。对应地,一个方法类前面加上“abstract”关键字,此方法就成为了抽象方法。注意抽象方法不能有实现代码,在函数名后直接跟一个分号。抽象类专用于派生出子类,子类必须实现抽象类所声明的抽象方法,否则,子类仍是抽象类。抽象类一般用于表达一种比较抽象的事物,而抽象方法则说明此抽象类应该具有的某种性质,从同一抽象类中继承的子类拥有相同的方法(即抽象类所定义的抽象方法),但这些方法的具体代码每个类都可以不一样。抽象类不能创建对象,一般用

它来引用子类对象。一个抽象类中可以包含非抽象的方法和字段。因此:包含抽象方法的类一定是抽象类,但抽象类中的方法不一定是抽象方法。除了方法可以是抽象的之外,属性也可以是抽象的。

2.接口

接口可以看成是一种“纯”的抽象类,它的所有方法都是抽象方法。抽象类定义了对象所属的类别,而接口实际上定义了一种对象应具有的行为特性。某个类可以实现多个接口,当创建一个此类的对象之后,通过引用这个对象的对象变量可以访问其所有的公有方法(包括自身的公有方法以及由接口定义的公有方法以)。在这种情况下,根本分不清哪些方法是由接口定义的,哪些是由类自己定义的。C#提供了一种“显式接口”实现机制,可以区分开这两种情况。由此得到一个结论:如果一个类显式实现某个接口,则只能以此接口类型的变量为媒介调用此接口所定义的方法,而不允许通过类的对象变量直接调用。或者这样说:被显式实现的接口方法只能通过接口实例访问,而不能通过类实例直接访问。

四、多态

方法重载属于多态的一种,两个构成重载关系的函数必须满足几个条件:函数名相同、参数类型不同,或参数个数不同。具体调用哪个方法要看参数,需要注意的是,方法返回值类型的不同不是方法重载的判断条件。多态编程的基本原理是:使用基类或接口变量编程。在多态编程中,基类一般都是抽象基类,其中拥有一个或多个抽象方法,各个子类可以根据需要重写这些方法。或者使用接口,每个接口都规定了一个或多个抽象方法,实现接口的类根据需要实现这些方法。因此,多态的实现分为两大基本类别:继承多态和接口多态。

1.接口多态与继承多态

接口多态与继承多态其编程方式与作用都是类似的。但由于一个类可以实现多个接口,所以,接口多态较继承多态更灵活,因而在编程中也用得更广。多态是面向对象技术中最精华的部分之一。大量的精巧软件设计方案都建立在对多态特性的巧妙应用上。在编程中应用多态,可以将其简化为两句:应用继承实现对象的统一管理;应用接口定义对象的行为特性。对比传统的不使用多态的编程方式,使用多态的好处是:当要修改程序并扩充系统时,需要修改的地方较少,对其他部分代码的影响较小。

五、类与对象

类是面向对象编程的基本单元,与使用C语言等结构化编程语言不一样,使用C#编程,所有的程序代码几乎都放在类中,不存在独立于类之外的函数。一个类可以包含两种成员:静态成员和实例成员,静态成员是供类的所有对象所共享的,而实例成员只供某一个对象所有。实例成员与静态成员的访问规则:位于同一类中的实例方法可直接相互调用;类的字段(包括实例字段和静态字段)可以被同一类中的所有实例方法直接访问;类中的静态方法只能直接访问类静态字段。

类中包括:方法和字段,属性是一种特殊的字段,它可以保证数据的合法性,方法和字段这两个概念是面向对象理论的术语,是通用于各种面向对象语言的。字段(Field)代表了类中的数据,在类的所有方法之外定义一个变量即定义了一个字段。在变量之前可以加上public、private 和protected 表示字段的访问权限。方法(function)功能代码的集合,在程序开发过程中,经常发现多处需要实现或调用某一个公用功能,这些功能的实现都需要书

写若干行代码。如果在调用此功能的地方重复书写这些功能代码,将会使整个程序中代码大量重复,会增大开发工作量,增加代码维护的难度。为了解决代码重复的问题,绝大多数程序设计语言都将完成某一公用功能的多个语句组合在一起,起一个名字用于代表这些语句的全体,这样的代码块被称为“函数(function)”。引入“函数”概念之后,程序中凡需要调用此公用功能的地方都可以只写出函数名,此名字就代表了函数中所包含的所有代码,这样一来,就不再需要在多个地方重复书写这些功能代码。

对象是以类为模板创建出来的。类与对象之间是一对多的关系。在C#中,使用new 关键字创建对象。在程序中“活跃”的是对象而不是类。在面向对象领域,对象有时又被称为是“类的实例”,“对象”与“类的实例”这两个概念是等同的。

六、值类型与引用类型

1.值类型

值类型变量与引用类型变量的内存分配模型也不一样。每个正在运行的程序都对应着一个进程(process),在一个进程内部,可以有一个或多个线程(thread),每个线程都拥有一块“自留地”,称为“线程堆栈”,大小为1M,用于保存自身的一些数据,比如函数中定义的局部变量、函数调用时传送的参数值等,这部分内存区域的分配与回收不需要程序员干涉。所有值类型的变量都是在线程堆栈中分配的。值类型共有三种:简单类型、枚举类型和结构类型。

2.引用类型

另一块内存区域称为“堆(heap)”,在.NET 这种托管环境下,堆由CLR 进行管理,所以又称为“托管堆(managed heap)”。用new 关键字创建的类的对象时,分配给对象的内存单元就位于托管堆中。在程序中我们可以随意地使用new 关键字创建多个对象,因此,托管堆中的内存资源是可以动态申请并使用的,当然用完了必须归还。打个比方更易理解:托管堆相当于一个旅馆,其中的房间相当于托管堆中所拥有的内存单元。当程序员用new 方法创建对象时,相当于游客向旅馆预订房间,旅馆管理员会先看一下有没有合适的空房间,有的话,就可以将此房间提供给游客住宿。当游客旅途结束,要办理退房手续,房间又可以为其他旅客提供服务了。引用类型共有四种:类类型、接口类型、数组类型和委托类型。所有引用类型变量所引用的对象,其内存都是在托管堆中分配的。严格地说,我们常说的“对象变量”其实是类类型的引用变量。但在实际中人们经常将引用类型的变量简称为“对象变量”,用它来指代所有四种类型的引用变量。

七、命名空间与类库

1.命名空间

在使用面向对象技术开发的现代软件系统中,经常拥有数百甚至上千个类,为了方便地管理这些类,面向对象技术引入了“命名空间(namespace)”的概念。命名空间可以看成是类的“容器”,它可以包含多个类。.NET Framework 使用命名空间来管理所有的类。如果把类比喻成书的话,则命名空间类似于放书的书架,书放在书架上,类放在命名空间里。当我们去图书馆查找一本书时,需要指定这本书的编号,编号往往规定了书放在哪个书库的哪个书架上,通过逐渐缩小的范围:图书馆->书库->书架,最终可以在某个书架中找到这本书。类似地,可以采用图书馆保存图书类似的方法来管理类,通过逐渐缩小的范围:最大的命名空间->子命名空间->孙命名空间„„,最终找到一个类。

2.类库

为了提高软件开发的效率,人们在整个软件开发过程中大量应用了软件工程的模块化原则,将可以在多个项目中使用的代码封装为可重用的软件模块,其于这些可复用的软件模块,再开发新项目就成为“重用已有模块,再开发部分新模块,最后将新旧模块组装起来”的过程。整个软件开发过程类似于现代工业的生产流水线,生产线上的每个环节都由特定的人员负责,整个生产线上的工作人员既分工明确又相互合作,大大地提高了生产效率。在组件化开发大行其道的今天,人们通常将可以重用的软件模块称为“软件组件”。在全面向对象的.NET 软件平台之上,软件组件的表现形式为程序集(Assembly),可以通过在Visual Studio 中创建并编译一个类库项目得到一个程序集。在Visual Studio 的项目模板中,可以很方便地创建类库(Class Library)项目,Visual Studio 会自动在项目中添加一个名为Class1.cs 的类文件,程序员可在此类文件中书写代码,或者添加新的类。一个类库项目中可以容纳的类数目没有限制,但只有声明为public 的类可以被外界使用。类库项目编译之后,会生成一个动态链接库(DLL:Dynamic Link Library)文件。这就是可以被重用的.NET 软件组件——程序集。默认情况下,类库文件名就是项目名加上“.dll”后缀。每个类库项目都拥有一个默认的命名空间,可以通过类库项目的属性窗口来指定。需要仔细区分“类库项目”、“程序集”和“命名空间”这三个概念的区别:

(1)每个类库项目编译之后,将会生成一个程序集。

(2)类库项目中可以拥有多个类,这些类可属于不同的命名空间。

(3)不同的类库项目可以定义相同的命名空间。

根据上述三个特性,可以得到以下结论:“命名空间”是一个逻辑上的概念,它的物理载体是“程序集”,具体体现为“DLL”(或EXE)文件。在Visual Studio 中,可通过创建“类库”类型的项目生成程序集。一个程序集可以有多个命名空间,而一个命名空间也可以分布于多个程序集。一旦生成了一个程序集,在其他项目中就可以通过添加对这一程序集的引用而使用此程序集中的类。其方法是在“项目”菜单中选择“添加程序集”命令,激活“浏览”卡片,选择一个现有的程序集文件(DLL 或EXE)。一个项目添加完对特定程序集的引用之后,就可以直接创建此程序集中的类了,当然要注意指明其命名空间。

八、委托

委托是一种新的面向对象语言特性,在历史比较长的面向对象语言比如C++中并未出现过。微软公司在设计运行于.NET Framework平台之上的面向对象语言(如C#和VisualBasic.NET)时引入了这一新特性。委托(delegate)也可以看成是一种数据类型,可以用于定义变量。但它是一种特殊的数据类型,它所定义的变量能接收的数值只能是一个函数,更确切地说,委托类型的变量可以接收一个函数的地址,很类似于C++语言的函数指针。简单地说:委托变量可看成是一种类型安全的函数指针,它只能接收符合其要求的函数地址。委托可以看成是一个函数的“容器”,将某一具体的函数“装入”后,就可以把它当成函数一样使用。定义委托类型时对函数的要求被称为函数的“签名(signature)”。函数的签名规定了函数的参数数目和类型,以及函数的返回值,体现了函数的本质特征。每一个委托都确定了一个函数的签名。拥有不同签名的函数不能赋值给同一类型的委托变量。因此,一个委托类型的变量,可以引用任何一个满足其要求的函数。

1.委托的组合与分解

委托变量可以代表某一函数,使用委托变量就相当于调用一个函数。如果仅是这么简单,那么直接调用函数不就行了吗?为什么还要引入“委托”这一特性?事实上,委托不仅可以代表一个函数,还可以组合“一堆”的函数,然后批量执行它们,这样的委托变量又称为“多路委托变量”。可以用加法运算符来组合单个委托变量为多路委托变量。类似地,也可以使用减法运算符来从一个多路委托变量中移除某个委托变量。

2.事件与多路委托

事件的主要特点是一对多关联,即一个事件源,多个响应者。在具体技术上,.NET Framework 的事件处理机制是基于多路委托实现的。事件与多路委托其实大同小异,只不过多路委托允许在事件源对象之外激发事件罢了。所有的.NET Framework 可视化窗体控件的预定义事件,都是某一对应的“事件名+Handler”委托类型的变量。与此事件相关的信息都封装在“事件名+Args”类型的事件参数中,此事件参数有一个基类EventArgs,它是所有事件参数的基类。明了上述内部机理,对于我们在程序中定义自己的事件非常有好处,尤其是开发一个自定义的可视化控件时,如果需要增加新的事件类型,我们应尽量遵循.NET Framework 的定义事件的框架,给事件取一个名字,定义一个“事件名+Handler”的事件委托类型,再从EventArgs 派生出自定义事件的参数,取名为“事件名+Args”。

面向对象的软件系统有许多都是事件驱动的,ASP.NET 就采用了“事件驱动”的编程方式。所谓“事件驱动”的开发方式,就是指整个系统包含许多的对象,这些对象可以引发多种事件,软件工程师的主要开发工作就是针对特定的事件书写代码响应它们。.NET 事件处理机制建立在委托的基础之上,而这两者都是ASP.NET 技术的基础之一。因此,必须牢固地掌握好委托和事件这两种编程技术,才能为掌握ASP.NET 技术扫清障碍。

第二篇:学习心得《面向对象》

面向对象课程学习心得

这学期的面向对象课程对我来说是收获匪浅的一门课。通过老师课件的讲解,自己一些相关书籍的阅读和实践作业的完成,逐步对课程有了由浅及深的认识。

面向对象(Object Oriented,OO)是一门以实践为主课程,课程中可以分开两块OOA(面向对象系统分析)和OOD(面向对象系统设计)。OOA(面向对象系统分析)主要内容: 研究问题域和用户需求,运用面向对象的观点和原则发现问题域中与系统责任有关的对象,以及对象的特征和相互关系.OOA不涉及针对具体实现采取的设计决策和有关细节,独立于具体实现的系统模型。是一个完整确切反映问题域和用户需求的系统模型。OOA的优势:复用、可扩展、可维护性、弹性。

OOD(面向对象系统设计):以OOA模型为基础,按照实现的要求进行设计决策,包括全局性的决策和局部细节的设计,与具体的实现条件相关。OOD的步骤:细化重组类→细化和实现类之间的关系,明确其可见性→增加属性,指定属性的类型和可见性→分配职责,定义执行每个职责的方法→对消息驱动的系统,明确消息传递的方式→利用设计模式进行局部设计→画出详细的类图和时序图。

面向对象的分析与设计方法将致力于解决传统软件研发过程中由于软件模块化结构化程度不高带来的软件重用性差、软件可维护性差、开发出的软件不能满足用户需要等方面问题。面向对象的概念包括:对象、对象的状态和行为、类、类的结构、消息和方法。对象概念将包含对象唯一性、抽象性、继承性、多态性的重要特征。面向对象的要素包含:抽象、封装性、共享性三方面。

在设计模式的研究过程中,我们组选择的是迭代器(Iterator)的设计模式研究。完成设计研究后,我对迭代器的设计模式有了更为深刻的理解。迭代器(Iterator)提供一个方法顺序访问一个聚合对象的各个元素,而又不暴露该对象的内部表示。并了解到迭代器设计模式一般在以下三类场合使用较多。

 访问一个聚合对象的内容而无需暴露它的内部表示。 支持对聚合对象的多种遍历。因为遍历状态是保存在每一个迭代器对象中的。

 为遍历不同的聚合结构提供一个统一的接口。根据实现方式的不同,效果上会有差别。同时还简化了容器的接口。但是在java Collection中为了提高可扩展性,容器还是提供了遍历的接口。在面向对象的软件设计中,我们经常会遇到一类集合对象,这类集合对象的内部结构可能有着各种各样的实现,但是归结起来,无非有两点是需要我们去关心的:一是集合内部的数据存储结构,二是遍历集合内部的数据。面向对象设计原则中有一条是类的单一职责原则,所以我们要尽可能的去分解这些职责,用不同的类去承担不同的职责。Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。

在Java Collection的应用中,提供的具体迭代器角色是定义在容器角色中的内部类。这样便保护了容器的封装。但是同时容器也提供了遍历算法接口,你可以扩展自己的迭代器。至于迭代器模式的使用。客户程序要先得到具体容器角色,然后再通过具体容器角色得到具体迭代器角色。这样便可以使用具体迭代器角色来遍历容器了。

OOA和OOD之间没有明显的界限。OOA与OOD的不可分割性正好说明了OO思想的强大,即软件过程阶段的无缝连接,在交流与沟通中不会产生鸿沟,这是相对结构化思想的好处,因为从功能模块到某块详细控制逻辑设计两者之间的联系不是十分紧密,需要分析人员与设计人员的再沟通。

通过课程的学习与实践,对面向对象的理念,以及相关方法,设计模式有了更为深刻的理解与掌握。针对面向对象的分析与设计课程的授课内容及方法,我个人觉得对我还是有不少的帮助和 提高。结合自己的工作,虽然与开发接触的比较少,但是在运维过程中,如果能了解开发原理,结合实际的工作,会对一些源代码的分析能力以及工作效率的提高起到明显的帮助作用。

第三篇:java面向对象的学习心得

Java面向对象的学习心得

大三的时候学校组织我们去苏州NIIT参加四个月的java实训,我开始系统的学习期java,之前大学的时候学的比较宽泛,没有专门的正对java的学习。

首先我是从学习Java编程开始接触OOP(面向对象编程),刚开始使用Java编写程序的时候感觉很别扭,因为我早以习惯用C来编写程序,很欣赏C的简洁性和高效性,喜欢C简练而表达能力丰富的风格,特别忍受不了Java运行起来慢吞吞的速度,相对冗长的代码,而且一个很简单的事情,要写好多类,一个类调用一个类,心里的抵触情绪很强。

我对Java的面向对象的特性琢磨良久,自认为有所领悟,也开始有意识的运用OOP风格来写程序,然而还是经常会觉得不知道应该怎样提炼类,面对一个具体的问题的时候,会觉得脑子里千头万绪的,不知道怎么下手,一不小心,又会回到原来的思路上去。

举个例子,要发广告邮件,广告邮件列表存在数据库里面。倘若用C来写的话,一般会这样思考,先把邮件内容读入,然后连接数据库,循环取邮件地址,调用本机的qmail的sendmail命令发送。

然后考虑用Java来实现,既然是OOP,就不能什么代码都塞到main过程里面,于是就设计了三个类:

一个类是负责读取数据库,取邮件地址,调用qmail的sendmail命令发送; 一个类是读邮件内容,MIME编码成HTML格式的,再加上邮件头;

一个主类负责从命令读参数,处理命令行参数,调用发email的类。

把一件工作按照功能划分为3个模块分别处理,每个类完成一件模块任务。

仔细的分析一下,就会发现这样的设计完全是从程序员实现程序功能的角度来设计的,或者说,设计类的时候,是自低向上的,从机器的角度到现实世界的角度来分析问题的。因此在设计的时候,就已经把程序编程实现的细节都考虑进去了,企图从底层实现程序这样的出发点来达到满足现实世界的软件需求的目标。

这样的分析方法其实是不适用于Java这样面向对象的编程语言,因为,如果改用C语言,封装两个C函数,都会比Java实现起来轻松的多,逻辑上也清楚的多。

我觉得面向对象的精髓在于考虑问题的思路是从现实世界的人类思维习惯出发的,只要领会了这一点,就领会了面向对象的思维方法。

举一个非常简单的例子:假使现在需要写一个网页计数器,客户访问一次页面,网页计数器加1,计数器是这样来访问的后台有一个数据库表,保存每个id(一个id对应一个被统计访问次数的页面)的计数器当前值,请求页面一次,对应id的计数器的字段加1(这里我们忽略并发更新数据库

表,出现的表锁定的问题)。

如果按照一般从程序实现的角度来分析,我们会这样考虑:首先是从HTTP GET请求取到id,然后按照id查数据库表,获得某id对应的访问计数值,然后加1,更新数据库,最后向页面显示访问计数。

现在假设一个没有程序设计经验的人,他会怎样来思考这个问题的呢?他会提出什么样的需求呢?他很可能会这样想:

我需要有一个计数器,这个计数器应该有这样的功能,刷新一次页面,访问量就会加1,另外最好还有一个计数器清0的功能,当然计数器如果有一个可以设为任意值的功能的话,我就可以作弊了。

做为一个没有程序设计经验的人来说,他完全不会想到对数据库应该如何操作,对于HTTP变量该如何传递,他考虑问题的角度就是我有什么需求,我的业务逻辑是什么,软件应该有什么功能。

按照这样的思路(请注意,他的思路其实就是我们平时在生活中习惯的思维方式),我们知道需要有一个计数器类 Counter,有一个必须的和两个可选的方法:

getCount()// 取计数器值方法

resetCounter()// 计数器清0方法

setCount()// 设计数器为相应的值方法

把Counter类完整的定义如下:

public class Counter {

public int getCount(int id){}

public void resetCounter(int id){}

public void setCount(int id, int currentCount){}

}

解决问题的框架已经有了,来看一下如何使用Counter。在count.cgi里面调用Counter来计数,程序片断如下:

// 这里从HTTP环境里面取id值

...Counter myCounter = new Counter();// 获得计数器

int currentCount = myCounter.getCount(id);// 从计数器中取计数

// 这里向客户浏览器输出

...程序的框架全都写好了,剩下的就是实现Counter类方法里面具体的代码了,此时才去考虑具体的程序语言实现的细节,比如,在getCount()方法里面访问数据库,更新计数

值。

从上面的例子中看到,面向对象的思维方法其实就是我们在现实生活中习惯的思维方式,是从人类考虑问题的角度出发,把人类解决问题的思维方式逐步翻译成程序能够理解的思维方式的过程,在这个翻译的过程中,软件也就逐步被设计好了。

在运用面向对象的思维方法进行软件设计的过程中,最容易犯的错误就是开始分析的时候,就想到了程序代码实现的细节,因此封装的类完全是基于程序实现逻辑,而不是基于解决问题的业务逻辑。

学习JDBC编程的经典错误问法是:“我怎样封装对数据库的select操作?”

面向对象的设计是基于解决业务问题的设计,而不是基于具体编程技术的设计。我不会去封装select语句的,我只封装解决问题的业务逻辑,对数据库的读取是在业务逻辑的编码实现阶段才去考虑的问题。

回过头看上面那个发广告邮件的例子,应该如何应用面向对象的思维方法呢?

对于一个邮件来说,有邮件头,邮件体,和邮件地址这三个属性,发送邮件,需要一个发送的方法,另外还需要一个能把所有邮件地址列出来的方法。所以应该如下设计:

类JunkMail

属性:

head

body

address

方法:

sendMail()// 发送邮件

listAllMail()// 列邮件地址

用Java来表示:

public class JunkMail {

private String head;

private String body;

private String address;

public JunkMain(){ // 默认的类构造器

// 从外部配置文件读邮件头和邮件体

this.head=...;

this.body=...;

}

public static boolean sendMail(String address){

// 调用qmail,发送email

}

public static Collection listAllMail(){

// 访问数据库,返回一个邮件地址集合}

}

当把JunkMail设计好了以后,再调用JunkMail类完成邮件的发送,将是非常轻松的事情。

如果说传统的面向过程的编程是符合机器运行指令的流程的话,那么面向对象的思维方法就是符合现实生活中人类解决问题的思维过程。

在面向对象的软件分析和设计的时候,要提醒自己,不要一上来就去想程序代码的实现,应该抛开具体编程语言的束缚,集中精力分析我们要实现的软件的业务逻辑,分析软件的业务流程,思考应该如何去描述和实现软件的业务。毕竟软件只是一个载体,业务才是我们真正要实现的目标。

但是在设计过程中,心里却往往在担心,如果我完全不去考虑程序代码的实现的话,那么我怎么知道我的设计一定合理呢?我怎么知道我设计的类、接口一定可以实现呢?所以经常可以看到的现象就是:

在设计过程中,虽然知道不能过早考虑代码实现,但是每设计一个类,一个接口,心里都要不知不觉的用自己熟悉的编程语言大概的评估一下,看看能否编出来,因此,一不小心,就会又回到按照程序功能实现的思路进行设计的老路上去了。

举个例子来说明,在做Web程序设计的时候,经常要遇到分页显示数据的情况。比如说需要把系统中所有的用户都列出来这样的功能。假设使用User类来表示用户,增加用户addUser(),删除用户deleteUser(),查询所有用户listUsers()方法。而数据库中有一个user表,一条记录是一个用户的信息。下面考虑一下User类的方法的实现:

addUser()和deleteUser()方法都好实现,就是对数据库增加记录和删除记录。对于listUsers()方法,其实就是对user表的select,取出一个记录集。但是该怎么从listUsers()方法中得到所有用户的列表呢?

一个方法调用的返回值只有一个,没有多个,所以很多情况下采用的办法就是返回值定义为集合类型,比如Vector。这样就可以在listUsers()方法的具体代码实现的时候,从数据库依次取出一个个记录,插入到Vector里面来。在主程序里面,调用listUsers()方法可以返回一个Vector,然后再对Vector遍历操作,就可以得到用户列表了。

public class User {

public static void addUser(...){

// 数据库insert一条记录

}

public static void deleteUser(...){

// 数据库delete一条记录

}

public Vector listUsers(...){

// 数据库select结果放到一个集合里面

}

}

这样的设计基本合理,但是仍然有点小问题。因为在设计的时候,就考虑到了用Java的集合类Vector来实现对不定长数据集的存放,因而违反了面向对象设计的一个原则:在设计的时候不应过早的考虑具体程序语言的实现。所以必须用抽象的方法,和具体实现无关的方法来表达业务逻辑。

我们知道,通常对具有集合特征的数据结构进行遍历通常可以使用next和hasNext方法,next实现取下一个用户,hasNext判断是否还有元素。因此我们定义一个接口Iterator,这个接口中定义两个方法next和hasNext:

public interface Iterator {

public boolean hasNext(){}

public Object next(){}

}

而User类的listUses方法返回值改为Iterator接口的实现类:

public class User {

...public Iterator listUsers(){

}

...}

这样就把User类的设计和具体的实现方法分离开了,因为此时任何实现了next()和hasNext()方法的类都可以做为listUsers的返回值,都可以被用来表达“用户列表”,而不仅仅可以使用Vector而已。比如,我可以用ArrayList来表达用户列表,因为ArrayList也实现了Iterator,当然我也可以自己专门写一个类来存放用户列表,只要实现next()和hasNext()方法就行了。

这样在具体的编写代码的时候,程序员具有了最大的灵活性,可以根据具体的情况,采用不同的编程方法来存放用户列表。特别是降低了程序的耦合度,提高了程序的可移植性。对于上面那个JunkMail的listAllMail()方法也同样应该改为接口类型。

然后,在主程序里面就这样来使用User类的listUsers方法:

User myUser = new User();

Iterator iterator = myUser.listUsers();

while(iterator.hasNext()){

iterator.next();

}

这样就可以完全不用考虑程序代码实现了,从高层次上把功能抽象出来,定义成为接口,同时又可以把系统设计的很合理,完全根据业务的需求来进行设计。

结语

通过上面的几个例子的设计说明,使用面向对象的思维方法,其实是一个把业务逻辑从具体的编程技术当中抽象出来的过程,而这个抽象的过程是自上而下的,非常符合人类的思维习惯,也就是先不考虑问题解决的细节,把问题的最主要的方面抽象成为一个简单的框架,集中精力思考如何解决主要矛盾,然后在解决问题的过程中,再把问题的细节分割成一个一个小问题,再专门去解决细节问题。

因而一旦牢牢的抓住了这一点,你就会发现在软件设计和开发过程中,你自己总是会不知不觉的运用面向对象的思维方法来设计和编写程序,并且程序的设计和开发也变得不再那么枯燥,而一个合理运用面向对象技术进行设计和架构的软件,更是具备了思维的艺术美感。

最后,愿面向对象的思维方法也能给您的程序设计之路带来创作的乐趣。

第四篇:C#学习心得

集合声明:类B可以换成任意object对象

1、CollectionBase

类A继承CollectionBase类,通过CollectionBase的成员List实现类A的Add(类

B)、Remove(类B)和RemoveAt(类B)方法:

publicvoidAdd(类B newB)

{List.Add(newB);}

publicvoidRemove(类B newB)

{List.Remove(newB);}

publicvoidRemoveAt(int index)

{List.RemoveAt(index);}

在类A中建立索引可以按类似数组的方法访问。

public 类B this[int index]

{get{return(类B)List[index];}

set{List[index]=value;}

}

利用CollectionBase的成员InnerList(ArrayList对象)实现类A的Contains()方法:

publicboolContains(类B newB)

{

returnInnerList.Contains(newB);

}

注意:InnerList是ArrayList类实例,其Contains方法通过调用Object.Equals确定相等性,Equals默认实现仅支持引用相等。对于引用类型,相等定义为对象相等,即这些引用是否引用同一对象。对于值类型,相等定义为按位相等。

可以在类B中重写Object.Equals方法和GetHashCode()方法。publicoverrideboolEquals(objectobj)

{//Check for null and compare run-time types.if(obj == null || GetType()!= obj.GetType())returnfalse;

B b =(B)obj;

return(比较逻辑);

}

publicoverrideintGetHashCode(){„„}

2、DictionaryBase

类A继承DictionaryBase类,通过DictionaryBase的成员

Dictionary(IDictionary类型的接口),实现类A的 Add(object key,类B)和Remove(object key,类B)方法:

publicvoidAdd(object key,类B newB)

{Dictionary.Add(key,newB);}

publicvoidRemove(object key,类B newB)

{Dictionary.Remove(key,newB);}

在类A中建立索引可以按类似数组的方法访问。

public 类B this[object index]

{get{return(类B)Dictionary[index];}

set{Dictionary[index]=value;}

}

利用DictionaryBase的接口成员Dictionary实现类A的Contains()方法: publicboolContains(object key)

{

returnDictionary.Contains(key);

}

3、迭代器

对于继承CollectionBase类的A,使用

foreach(BsourceBin类A对象){}

对于继承DictionaryBase类的A,使用

foreach(DictionaryEntrysourceBin类A对象){source.Value.}

对于类迭代,使用方法GetEnumerator(),返回类型是IEnumerator;类成员迭代使用IEnumerable(),返回类型是IEnumerable;

例如继承DictionaryBase类的A的迭代器,public new IEnumeratorGetEnumerator()

{foreach(object b in Dictionary.Values)

yield return(B)b;

}

以后使用foreach循环时,可按照类似继承CollectionBase类的的方式使用。

4、浅度复制与深度复制

浅度复制:简单地按照成员复制对象可以通过派生于System.Object的MemberwiseClone()方法来完成,这是一个受保护的方法,但是很容易在对象上定义一个调用该方法的公共方法例如GetCopy()。这个方法的复制功能成为浅复制。浅拷贝是对引用类型拷贝地址,对值类型直接进行拷贝,但是string类例外,因为string是readonly的,当改变string类型的数据值时,将重新分配了内存地址。数组、类也是浅度复制,而结构体、数值型、枚举是深度复制。

深度复制:需要深度复制的类A添加ICloneable接口,实现该接口的Clone()方法。

public object Clone()

{A newA=new A();

object []arr=new object[维度];//object 可以是数值类型,string //不能使用newA.arr=arr;因为通过数组名赋值引用同一地址,是浅度复制 arr.CopyTo(newA.arr,0);

returnnewA;}

假设类A中有成员对象类B实例myB,则在类B定义中也要实现ICloneable的Clone()方法,class B:ICloneable

{

public object Clone(){„„}

}

然后在类A的Clone方法中,newA.myB=myB.Clone();

比较

1、is运算符

检查对象是否是给定类型或者是否可以转换为给定类型,是则返回true。 is

如果type是类类型,operand也是该类型,或继承该类型、封箱到该类型,为true 如果type是接口类型,operand也是该类型,或实现该接口的类型,为true 如果type是值类型,operand也是该类型,或拆箱到该类型,为true2、运算符重载

public static 返回类型 operator 需重载的运算符(参数„„){}

注意不能重载赋值运算符,&&和||运算符,但可重载&和|;有些运算符需成对重载,如“<”和“>”

3、IComparable接口

类A实现IComparable接口的方法intCompareTo(objectobj)后,利用成员为类A的实例的ArrayList或Array类可以调用Sort()方法,按CompareTo(objectobj)的方法排序。

4、IComparer接口

类A实现IComparer接口的方法intCompare(objectx, objecty)后,利用ArrayList或Array类可以调用Sort(IA)方法(IComparer IA=new A()),按

Compare(,)方法排序。注意ArrayList或Array类的实例不一定是类A。也可以在类A中定义一个公用动态接口成员IComparer ID,这样可以直接调用Sort(ID)。另外,在Compare方法中可以调用Comparer.Default.Compare(,)方法,实现特定的关键字排序。Default是Compare类的动态实例。

转换

1、隐式和显示转换

在没有继承关系,没有共享接口的类型之间转换时,必须定义类型之间的隐式和显示转换。public classA

{„„

//定义A到B的隐式转换

public staticimplicit operatorzhuanB(Aa){„„ return }

}

public classB

{„„

//定义B到A的显式转换

public staticexplicit operatorzhuanA(Bb){„„return }

}

2、as运算符

把类型转换为给定类型。

is

operand类型是type类型,或可以隐式转换为type类型,或封箱到type类型 如果不能转换,则表达式的结果是null

异常处理

Exception:所有异常对象的基类。

SystemException:运行时产生的所有错误的基类。

IndexOutOfRangeException:当一个数组的下标超出范围时运行时引发。NullReferenceException:当一个空对象被引用时运行时引发。

InvalidOperationException:当对方法的调用对对象的当前状态无效时,由某些方法引发。

ArgumentException:所有参数异常的基类。

ArgumentNullException:在参数为空(不允许)的情况下,由方法引发。ArgumentOutOfRangeException:当参数不在一个给定范围之内时,由方法引发。

InteropException:目标在或发生在CLR外面环境中的异常的基类。ComException:包含COM类的HRESULT信息的异常。

SEHException:封装Win32结构异常处理信息的异常。

SqlException:封装了SQL操作异常。

常见具体的异常对象:

ArgumentNullException一个空参数传递给方法,该方法不能接受该参数ArgumentOutOfRangeException参数值超出范围

ArithmeticException出现算术上溢或者下溢

ArrayTypeMismatchException试图在数组中存储错误类型的对象

BadImageFormatException图形的格式错误

DivideByZeroException除零异常

DllNotFoundException找不到引用的DLL

FormatException参数格式错误

IndexOutOfRangeException数组索引超出范围

InvalidCastException使用无效的类

InvalidOperationException方法的调用时间错误

NotSupportedException调用的方法在类中没有实现

NullReferenceException试图使用一个未分配的引用OutOfMemoryException内存空间不够

StackOverflowException堆栈溢出

第五篇:C#学习心得

C#速成

一、绪论

C#是这样的一种语言,具有C++的特点,象Java一样的编程风格, 并且象Basic一样的快速开发模型。如果你已经知道了C++,本文会在不到一个小时的时间内让你迅速掌握C#的语法。熟悉Java的括会更好,因为Java的程序结构、打包(Packages)和垃圾收集的概念有助于你更快的了解C#。因此在讨论C#的构造时,我会假定你了解C++。

本文会讨论C#语言的构造与特点,同时会采取简洁的和你能理解的方式使用些代码示例,我们会尽量让你能稍微看看这些代码就能理解这些概念。

注意:本文不是为C#高手(C# gurus)所写.这是针对在C#学习上还是初学者的文章。下面是将要讨论的C#问题的目录: 程序结构 命名空间 数据类型 变量

运算符和表达式 枚举

语句(Statements)

类(Classes)和结构(Structs)修饰符(Modifiers)属性(Properties)接口(Interfaces)方法参数(Function Parameters)数组(Arrays)索引器(Indexers)装箱及拆箱操作 委托(Delegates)继承和多态

下面的内容将不会在被讨论之列:

C++与C#谁更通用

诸如垃圾回收、线程以及文件处理等概念 数据的类型转换 异常处理.NET库

二、程序结构

这一点象C++,C#是一种对大小写字母敏感的语言,分号“;”是语句间的分隔符。与C++不同的是,C#当中声明代码文件(头文件)与实现代码文件(cpp文件)不是独立存在的,所有代码(类声明和类实现)都位于一个扩展名为cs的文件内。

让我们瞧瞧C#当中的 Hello world 程序是怎样的。using System;namespace MyNameSpace { class HelloWorld { static void Main(string[] args){ Console.WriteLine(“Hello World”);} } }

在C#当中的每样东西都被封装到一个类中,C#的类又被封装到一个命名空间当中(就象一个文件夹中的文件)。类似于 C++,main方法是你的程序的入口点。C++的main函数调用名称是“main”,而C#的main函数是以大写字母M为起点的名称是“Main”。

没有必要把分号分隔符放在类语句块或者结构定义语句块后。这在C++当中被要求,但在C#当中却不是。

三、命名空间

每一个类都被包装进一个命名空间。命名空间的概念与C++的完全相同,但在C#当中使用命名空间的频率较C++还高。你可以使用点限定符(dot qulifier)访问一个类。在上面的hello world程序当中MyNameSpace就是一个命名空间。

现在思考这样的一个问题,你想从某些别的类的命名空间当中来访问HelloWorld这个类该如何操作。

这有一个例子:

using System;namespace AnotherNameSpace { class AnotherClass { public void Func(){ Console.WriteLine(“Hello World”);} } }

现在,从你的HelloWorld类里你能象这样去访问上面的这个AnotherNameSpace的命名空间: using System;using AnotherNameSpace;// you will add this using statement namespace MyNameSpace { class HelloWorld { static void Main(string[] args){ AnotherClass obj = new AnotherClass();obj.Func();} } }

在.NET库当中,System是位于顶层的命名空间,别的命名空间都存在这个命名空间之下。默认状态下,存在一个全局的命名空间,因此一个在命名空间外定义的类将直接在这个全局命名空间之下;因此,你能在没有任何点限定符的情况下访问这个类。

四、变量

除以下区别外,C#当中的变量几乎与C++同:

与C++不同,C#变量被访问之前必须被初始化;否则编译时会报错。因此,访问一个未初始化变量是不可能的事。

C#中你不会访问到一个不确定的指针。(译者注:严格说起来C#已经把指针概念异化,限制更严格。所以有些资料上会说C#取消了指针概念)一个超出数组边界的表达式是不可访问的。

C#中没有全局(整个Application)的变量或全局函数,全局方式的操作是通过静态函数和静态变量来实现的。

五、数据类型

所有C#数据类型都派生自基类Object。这里有两类数据类型: 基本型/内置型 用户自定义型 下面一个C#内置类型列表:

类型 字节数 解释 byte 1 无符号字节型 sbyte 1 有符号字节型 short 2 有符号短字节型 ushort 2 无符号短字节型 int 4 有符号整型 uint 4 无符号整型 long 8 有符号长整型 ulong 8 无符号长整型 float 4 浮点数 double 8 双精度数 decimal 8 固定精度数 string unicode字串型 char unicode字符型 bool 真假布尔型

注意:C#当中的类型范围与C++有所不同;例如,C++的long型是4个字节,而在C#当中是8个字节。同样地,bool型和string型都不同于C++。bool型只接受true和false两种值。不接受任何整数类型。

用户定义类型包括: 类类型(class)结构类型(struct)接口类型(interface)

数据类型的内存分配形式的不同又把它们分成了两种类型: 值类型(Value Types)

引用类型(Reference Types)

值类型:

值类型数据在栈中分配。他们包括:所有基本或内置类型(不包括string类型)、结构类型、枚举类型(enum type)

引用类型:

引用类型在堆中分配,当它们不再被使用时将被垃圾收集。它们使用new运算符来创建,对这些类型而言,不存在C++当中的delete操作符,根本不同于C++会显式使用delete这个运算符去释放创建的这个类型。C#中,通过垃圾收集器,这些类型会自动被收集处理。引用类型包括:类类型、接口类型、象数组这样的集合类型类型、字串类型、枚举类型 枚举类型与C++当中的概念非常相似。它们都通过一个enum关键字来定义。示例:

enum Weekdays { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday }

类类型与结构类型的比较

除了在内存分配形式上外,类与结构的概念完全与C++相同。类的对象被分配在堆中,并且通过new来创建,结构也是被new创建但却被分配在栈当中。C#当中,结构型适于快速访问和拥有少量成员的数据类型。如果涉及量较多,你应该创建一个类来实现他。

(译者注:这与堆和栈内存分配结构的特点有关。简而言之,栈是一种顺序分配的内存;堆是不一定是连续的内存空间。具体内容需要大家参阅相关资料)示例:

struct Date { int day;int month;int year;}

class Date { int day;int month;int year;string weekday;string monthName;

public int GetDay(){ return day;}

public int GetMonth(){ return month;}

public int GetYear(){ return year;}

public void SetDay(int Day){ day = Day;}

public void SetMonth(int Month){ month = Month;}

public void SetYear(int Year){ year = Year;}

public bool IsLeapYear(){ return(year/4 == 0);}

public void SetDate(int day, int month, int year){

}...}

六、属性

如果你熟悉C++面象对象的方式,你就一定有一个属性的概念。在上面示例当中,以C++的观点来看,Data类的属性就是day、month和year。用C#方式,你可以把它们写成Get和Set方法。C#提供了一个更方便、简单、直接的方式来访问属性。

因此上面的类可以被写成:

using System;class Date { int day;public int Day{ get { return day;}

set { day = value;} }

int month;

public int Month{

get { return month;}

set { month = value;} }

int year;

public int Year{

get { return year;}

set { year = value;} }

public bool IsLeapYear(int year){ return year%4== 0 ? true: false;}

public void SetDate(int day, int month, int year){ this.day = day;this.month = month;this.year = year;} }

你可在这里得到并设置这些属性:

class User {

public static void Main(){ Date date = new Date();date.Day = 27;date.Month = 6;date.Year = 2003;Console.WriteLine(“Date: {0}/{1}/{2}”, date.Day, date.Month, date.Year);} }

七、修饰符

你必须已经知道public、private、protected这些常在C++当中使用的修饰符。这里我会讨论一些C#引入的新的修饰符。readonly(只读)

readonly修饰符仅在类的数据成员中使用。正如这名字所提示的,readonly 数据成员仅能只读,它们只能在构造函数或是直接初始化操作下赋值一次。readonly与const数据成员不同,const 要求你在声明中初始化,这是直接进行的。看下面的示例代码: class MyClass {

const int constInt = 100;//直接初始化

readonly int myInt = 5;//直接初始化

readonly int myInt2;//译者注:仅做声明,未做初始化

public MyClass(){ myInt2 = 8;//间接的 }

public Func(){ myInt = 7;//非法操作(译者注:不得赋值两次)Console.WriteLine(myInt2.ToString());} }

sealed(密封)

密封类不允许任何类继承,它没有派生类。因此,你可以对你不想被继承的类使用sealed关键字。

sealed class CanNotbeTheParent { int a = 5;}

unsafe(不安全)

你可使用unsafe修饰符来定义一个不安全的上下文。在不安全的上下文里,你能写些如C++指针这样的不安全的代码。看下面的示例代码:

public unsafe MyFunction(int * pInt, double* pDouble){ int* pAnotherInt = new int;*pAnotherInt = 10;pInt = pAnotherInt;...*pDouble = 8.9;}

八、interface(接口)

如果你有COM方面的概念,你会立亥明白我要谈论的内容。一个接口就是一个抽象的基类,这个基类仅仅包含功能描述,而这些功能的实现则由子类来完成。C#中你要用interface关键字来定义象接口这样的类。.NET就是基于这样的接口上的。C#中你不支持C++所允许的类多继承(译者注:即一个派生类可以从两个或两个以上的父类中派生)。但是多继承方式可以通过接口获得。也就是说你的一个子类可以从多个接口中派生实现。interface myDrawing { int originx { get;set;} int originy { get;set;} void Draw(object shape);} class Shape: myDrawing { int OriX;int OriY;public int originx { get{ return OriX;} set{ OriX = value;} } public int originy { get{ return OriY;} set{ OriY = value;} } public void Draw(object shape){...// do something }

// class's own method public void MoveShape(int newX, int newY){.....} }

九、Arrays(数组)

C#中的数组比C++的表现更好。数组被分配在堆中,因此是引用类型。你不可能访问超出一个数组边界的元素。因此,C#会防止这样类型的bug。一些辅助方式可以循环依次访问数组元素的功能也被提供了,foreach就是这样的一个语句。与C++相比,C#在数组语法上的特点如下: 方括号被置于数据类型之后而不是在变量名之后。创建数组元素要使用new操作符。

C#支持一维、多维以及交错数组(数组中的数组)。示例:

int[] array = new int[10];// 整型一维数组 for(int i = 0;i < array.Length;i++){ array[i] = i;} int[,] array2 = new int[5,10];// 整型二维数组 array2[1,2] = 5;int[,] array3 = new int[5,10,5];// 整型的三维数组 array3[0,2,4] = 9;int[][] arrayOfarray = = new int[2];// 整型交错数组(数组中的数组)arrayOfarray[0] = new int[4];arrayOfarray[0] = new int[] {1,2,15};

十、索引器

索引器被用于写一个访问集合元素的方法,集合使用“[]”这样的直接方式,类似于数组。你所要做的就是列出访问实例或元素的索引清单。类的属性带的是输入参数,而索引器带的是元素的索引表,除此而外,他们二者的语法相同。示例: 注意:CollectionBase是一个制作集合的库类。List是一个protected型的CollectionBase成员,储存着集合清单列表。class Shapes: CollectionBase { public void add(Shape shp){ List.Add(shp);} //indexer public Shape this[int index] { get { return(Shape)List[index];} set { List[index] = value;} } }

十一、装箱和拆箱操作(Boxing/Unboxing)

C#的装箱思想是全新的。上面提到过所有的数据类型,不论内置或用户自定义,全都从命名空间System的一个基类object派生出来。因此把基本的或者原始类型转换成object类型被称做装箱,反之,这种方式的逆操作被称为拆箱。示例: class Test { static void Main(){ int myInt = 12;object obj = myInt;// 装箱 int myInt2 =(int)obj;// 拆箱 } }

示例展示了装箱和拆箱操作。一个整型值转换成object类型,然后又转换回整型。当一个值类型的变量需要转换成引用类型时,一个object的箱子会被分配容纳这个值的空间,这个值会被复制进这个箱子。拆箱与此相反,一个object箱子中的数据被转换成它的原始值类型时,这个值将被从箱中复制到适当的存储位置。

十二、方法参数

C#中有三种类型的参数:

值参数/输入型参数

引用型参数/输入输出型参数 Out参数

如果你有COM接口和它的参数类型的概念,你会很容易理解C#参数类型。值参数/输入型参数

值概念与C++相同。所要传递的值会被复制到一个位置上并被传递给函数。示例: SetDay(5);void SetDay(int day){....}

引用型参数/输入输出参数

C#中的引用参数既不是C++中的指针也不是引用操作符(&)来传递的。C#的引用型参数减少了出错的可能。引用型参数也被称作输入输出参数,因为你传递了一个引用地址,因此你可以从函数中传递一个输入值并且可以获得一个输出值。

你不能把一个未经初始化的引用型参数传递给函数。C#用ref这个关键字来声明引用型参数。当你传递一个变量给函数要求的引用参数时必须使用一个ref关键字说明。示例:

int a= 5;FunctionA(ref a);// 要用ref声明变量,否则你会得到 // 一个编译错误

Console.WriteLine(a);// 指向地址的值为20 void FunctionA(ref int Val){ int x= Val;Val = x* 4;}

Out参数

Out型参数仅仅从函数当中返回一个值。不要求有输入值。C#用关键字out来描声明这个参数 示例:

int Val;GetNodeValue(Val);bool GetNodeValue(out int Val){ Val = value;return true;}

可变数量的参数和数组

数组在C#当中是通过关键字params来描述传递的。作为数组类型的变量,你能传递任意数量的元素。从下面示例中你可以理解的更好。示例:

void Func(params int[] array){ Console.WriteLine(“number of elements {0}”,array.Length);} Func();// prints 0 Func(5);// prints 1 Func(7,9);// prints 2 Func(new int[] {3,8,10});// prints 3 int[] array = new int[8] {1,3,4,5,5,6,7,5};Func(array);// prints 8

十三、运算符和表达式

运算符和表达式概念与C++完全相同。但是一些新的有用的运算符被填加了进来。我将在这里讨论其中的某些部分。

is 运算符

is 运算符被用于检查操作数的类型是否相同或者是否可以转换。is 运算符在多态环境下特别有用。它有两个操作数,运算结果是一个布尔型。看这个示例: void function(object param){ if(param is ClassA)//do something else if(param is MyStruct)//do something } } as 运算符

as 运算符检查操作数的类型是否可被转换或者是否相等(这些 as通过 is 运算符来完成。如果结果是可转换的,则结果将被转换或者被装箱,成object(关于as运算符进行装箱成目标类型的操作请看前面的装箱/拆箱操作)。如果不可转换或者装箱,则返回值是null。瞧一瞧下面的例子我们会更好地理解这个概念。Shape shp = new Shape();Vehicle veh = shp as Vehicle;// 结果是null, 类型不可转换 Circle cir = new Circle();Shape shp = cir;Circle cir2 = shp as Circle;//会被转换 object[] objects = new object[2];objects[0] = “Aisha”;object[1] = new Shape();string str;for(int i=0;i&< objects.Length;i++){ str = objects[i] as string;if(str == null)Console.WriteLine(“can not be converted”);else Console.WriteLine(“{0}”,str);} 输出: Aisha can not be converted

十四、语句

除了对某些新增语句和对某些语句的修改以外,C#语句与C++非常相象。下面是新增的语句: foreach 用于循环依次访问集合元素,比如象数组等。示例: foreach(string s in array)Console.WriteLine(s);lock 用于锁住代码块,使线程在临界争区内,别的线程无法进入锁定的临界区。

checked/unchecked 用于数值运算中的溢出检测。示例:

int x = Int32.MaxValue;x++;// 溢出检测 { x++;// 异常

}

unchecked { x++;// 溢出} }

下面的语句在C#当中已经被修改: Switch 执行一个case语句后,程序流程不允许跳到下一个相邻case语句。这在C++当中是被允许的。示例:

int var = 100;switch(var){ case 100: Console.WriteLine(“”);// 没有break语句

case 200: Console.WriteLine(“”);break;}

C++编译后的输出:

C#下,编译时会报错:

error CS0163: Control cannot fall through from one case label('case 100:')to another 但是你仍然能做C++类似的事 switch(var){ case 100: case 200: Console.WriteLine(“100 or 200”);break;}

你也可以常数变量作为case 的值: 示例:

const string WeekEnd = “Sunday”;const string WeekDay1 = “Monday”;....string WeekDay = Console.ReadLine();switch(WeekDay){ case WeekEnd: Console.WriteLine(“It's weekend!”);break;case WeekDay1: Console.WriteLine(“It's Monday”);break;}

十五、委托

委托让我们把一个函数引用存储在一个变量里。C++当中,这类似于使用typedef定义的函数指针,我们通常用存储一个函数指针。

声明委托使用的关键字是 delegate。瞧瞧这个示例,你会理解什么是委托: 示例:

delegate int Operation(int val1, int val2);public int Add(int val1, int val2){ return val1 + val2;}

public int Subtract(int val1, int val2){ return val1-val2;}

public void Perform(){ Operation Oper;Console.WriteLine(“Enter + or-”);string optor = Console.ReadLine();Console.WriteLine(“Enter 2 operands”);string opnd1 = Console.ReadLine();string opnd2 = Console.ReadLine();int val1 = Convert.ToInt32(opnd1);int val2 = Convert.ToInt32(opnd2);if(optor == “+”)Oper = new Operation(Add);Else Oper = new Operation(Subtract);Console.WriteLine(“ Result = {0}”, Oper(val1, val2));}

十六、继承和多态

C#仅允许单继承,多继承要通过接口来实现。示例:

class Parent { } class Child : Parent { }

十七、虚拟方法

除了在子类中实现虚拟方法采用override关键字外,虚拟方法实现多态的概念C#与C++相同。父类使用相同的virtual关键字。从重载虚拟方法的每个类都要使用override关键字。class Shape { public virtual void Draw(){ Console.WriteLine(“Shape.Draw”);} }

class Rectangle : Shape { public override void Draw(){ Console.WriteLine(“Rectangle.Draw”);} }

class Square : Rectangle { public override void Draw(){ Console.WriteLine(“Square.Draw”);} }

class MainClass { static void Main(string[] args){ Shape[] shp = new Shape[3];Rectangle rect = new Rectangle();shp[0] = new Shape();shp[1] = rect;shp[2] = new Square();shp[0].Draw();shp[1].Draw();shp[2].Draw();} }

输出t: Shape.Draw Rectangle.Draw Square.Draw

十八、使用“new”来隐藏父方法

你可以定义一个子类成一个新方法版本,隐藏基类当中的那个版本。使用new关键字就可以定义一个新版本。思考下面的示例,它是上面示例的修改后的版本。注意当我用Rectangle类中的new关键字代替override关键字时示例的输出情况。

class Shape { public virtual void Draw(){ Console.WriteLine(“Shape.Draw”);} }

class Rectangle : Shape { public new void Draw(){ Console.WriteLine(“Rectangle.Draw”);} } class Square : Rectangle { //没在这里让你重载 public new void Draw(){ Console.WriteLine(“Square.Draw”);} } class MainClass { static void Main(string[] args){ Console.WriteLine(“Using Polymorphism:”);Shape[] shp = new Shape[3];Rectangle rect = new Rectangle();shp[0] = new Shape();shp[1] = rect;shp[2] = new Square();shp[0].Draw();shp[1].Draw();shp[2].Draw();Console.WriteLine(“Using without Polymorphism:”);rect.Draw();Square sqr = new Square();sqr.Draw();} } 输出: Using Polymorphism Shape.Draw Shape.Draw Shape.Draw Using without Polymorphism: Rectangle.Draw Square.Draw 这里的多态性不会把Rectangle类的Draw方法当做Shape的Draw方法多态性的一种表现。相反,它会认为这是一种不同的方法。因此,为了避免父类与子类间的命名冲突,我们使用了new修饰符。注意:你不能使用同一类下面一种方法的两个版本,即一个是用new修饰符的版本,另一个是用override或virtual修饰符的版本。正象上面示例所说明的,我不能再在拥有virtual或override方法的Rectangle类中添加另一个命名为Draw的方法。同样地,在Square类中,我也不能重载Square类的虚拟的Draw方法。

十九、调用基类成员

如果子类与基类有同名的数据成员,为避免命名冲突,访问基类数据成员和函要使用一个关键字base。在下面的示例中我们来看看如何调用基类的构造函数以及如何使用数据成员。public Child(int val):base(val){ myVar = 5;base.myVar;}

或者

public Child(int val){ base(val);myVar = 5;base.myVar;}

文学の音声図書館[转贴]

みなさん: こんにちは。私がつくった「文学の音声図書館」をご覧下さい。みなさまの聴力に役立つかもしれません。どうぞ。

http://作家辞典http://horagai.com/歌舞伎文庫http://http://日语教材 http://seki.nease.net/yufa.htm 语法网站======================================外来语从此不再可怕http://[align=right][color=#000066][此贴子已经被作者于2005-1-27 20:18:53编辑过][/color][/align]

下载C#面向对象学习心得word格式文档
下载C#面向对象学习心得.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐

    《面向对象程序设计》教学大纲

    《面向对象程序设计》教学大纲 课程名称:信息内容安全 课程代码:042504 总 学 时:64 学分:2 课程类型:专业必修课 适用专业: 信息安全专业 制 订 人:徐雪飞 审 定 人:肖文 制订日期......

    面向对象程序设计教学大纲

    面向对象程序设计(专业限选课) Object-Oriented Programming 【课程编号】XZ26175 【学分数】3 【学时数】60=38+22 【课程类别】专业限选 【编写日期】2010.3.30 【先修课程......

    《面向对象程序设计》教学大纲

    《面向对象程序设计》教学大纲 课程名称:面向对象程序设计 英文名称:Object Oriented Programming) 学分:2 总学时:36 实验(上机)学时: 8 开课专业:电子商务专业 一、 本课程的性质......

    面向对象程序设计心得体会

    这次课程设计让我收获到很多,上个学期我们也有过课程设计,但是我们都是从网上下一个程序,连带着报告,看一遍程序,修改修改报告,然后就直接去答辩,都是糊弄糊弄就过去了,自己根本就没......

    面向对象知识总结

    一、 封装 1.类本身就是一个封装单元 2.实现:A属于私有化;B共有的方法提供给外界访问;C信息隐藏,安全,对外提供清晰的接口 二、继承1.代码共有,方便维护扩展 2.更符合人类遇到的问......

    达内学习心得:精心总结的面向对象

    达内学习心得:精心总结的面向对象 参赛学员:方钱有 获奖奖项:二等奖 什么面向对象: 个人分析觉得:在程序设计里,面向对象是一种相对说法,相对于面向过程而言的; 面向“对象":即重点......

    达内学习心得:精心总结的面向对象

    达内学习心得:精心总结的面向对象 参赛学员:方钱有 获奖奖项:二等奖 什么面向对象: 个人分析觉得:在程序设计里,面向对象是一种相对说法,相对于面向过程而言的; 面向“对象":即重点......

    面向对象程序设计实习报告

    《面向对象程序设计》实习报告 班级:电子商务 学号:xx 姓名xx 指导教师: 实习时间:2011.12.26-2012.1.6 一、 实习目的 经过两个周的面向对象程序设计的实习,让我再次复习了SQL......