面向对象的程序设计(Object-Oriented Programming,简记为OOP)是一种功能非常强大的编程方法,立意于创建软件重用代码,以类为基础去思考编程问题。类是OOP中的核心组成元素,通常都是使用类来“封装”对象(属性、行为)。在经典图书《代码大全》里定义:“创建高质量的类,第一步,可能也是最重要的一步,就是创建一个好的接口。这也包括了创建一个可以通过接口来展现的合理的抽象,并确保细节仍被隐藏在抽象背后。”,其实接口也是一个类,一个更为抽象的类.
在我以前的一篇文章里曾经提到:“面向对象编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。----选自《大话设计模式》” 。
可见,在OOP中,类是何等的重要,本文将以类作为谈论点,全方面的分析和介绍类的各种相关知识点。
一. 日常生活中的对象
在大多数人眼里,对象是一个抽象的名词,而类呢则更为抽象。日常生活中,可以把对象理解为可辩识的一种实体。如汽车、轮船、飞机、电脑等。如果把对象和程序联系起来,则可看作是在程序中表示的一个实体。
一切从现实出发,才能使编写出的代码更能反映出真实的生活。如电脑这个对象,则包括有主板,硬盘,显卡,内存等组成元素。然而实际的编程中并不是这么的简单,但是我们还是得从现实中去分析。以这种方式去考虑程序的设计,就会得到很多的好处:程序更容易设计,程序的体系结构更直观,更容易理解,更清晰的反映了程序从真实生活中抽象出来的含义,更便于多个开发人员一起工作,因为他们可以在代码中处理不同的对象,只需要知道其他人的对象做什么工作,无需担心其他人的代码的工作细节。
二. 编程中的对象
我们平时在编程中用到过些什么对象呢?我想无外乎是两大类:系统对象和用户自定义对象。OK, 为了更清楚的说明这点,我们从“Hello World”开始吧。看看如下代码定义:
上面这句代码对于正在看本文的你来说,我想应该是在熟悉不过了.他的功能就是将“Hello World”这个字符串在控制台输出.然而,出此之外,这句代码还隐含着其他的相关知识.那么具体是什么呢?其实我们在编写这句代码的时候已经使用对象了.Console就是一个对象,.NET Framework提供的一个系统对象,我们之所以能够通过WriteLine方法实现输出,是因为我们调用了这个系统对象的方法. 这里Console就是一个典型的系统对象.
下面我们来看看什么的自定义对象,首先我们来分析一下现实生活中的对象.以电脑来说,一台电脑大概由主板,硬盘,显卡,内存等众多元素组成.通过抽象,我们就可以认为电脑就是一个对象,主板,显卡这些组成元素则是电脑这个对象的属性.下面是电脑类的定义:
2/// 电脑类
3/// </summary>
4 public class Computer
5 {
6 private string name;
7 public string Name
8 {
9 get { return name; }
10 set { name = value; }
11 }
12
13 public string 主板 = "精英主板";
14 public string 硬盘 = "希捷硬盘";
15 public string 内存 = "金士顿";
16 public string 显卡 = "七彩红";
17 //其他组成元素
18
19 public Computer()
20 { }
21
22 public Computer(string name)
23 {
24 this.Name = name;
25 }
26}
2 {
3 static void Main(string[] args)
4 {
5 Computer zhangsan = new Computer("张三");
6 Computer lisi = new Computer("李四");
7
8 Console.WriteLine(zhangsan.主板 == lisi.主板);
9 }
10}
通过运行上面程序段,结果为:true.这说明什么,说明张三的主板和李四的主板是同一个吗?不是,我们应该说张三和李四都是使用的同一个类的对象.这里Computer类到底做了什么呢?封装属性和行为吗?最容易的理解便是把这个类看作是一种新的数据类型.也就是说Computer是一种数据类型,而zhangsan和lisi则是这种新数据类型的变量,用OOP的话来说的话,zhangsan和lisi则是Computer这个对象的实例或是对象.
三、类的相关特性
类的特性有很多,这里我就简单的介绍下最常用的几个特性。
1. 类名
类名是唯一一个用于区分其他类的特性,如同人的名字一样。当然在同一个项目或是程序集或是框架中,会出现同名的类,就如同世界之大,名字相同的人到处都有一样。而现实生活中的同名人可以根据他所在的国家、省份、区县、乡村以及家庭来区分,对于同名的类呢则是通过命名空间来区分的。
如上电脑类Computer,Computer就是电脑类的类名,用这个名字类区分其他类。
2. 属性
属性是一个类的重要组成部分,就以上面的电脑类(Computer)来说吧。name,主板,显卡等都是Computer类的属性。我们可以通过类对象(实例对象)去访问他们,示意性代码如下:
2 {
3 public string Name = "张三";
4}
5 class TestComputer
6 {
7 void Method()
8 {
9 Computer computer = new Computer();
10 Console.WriteLine(computer.Name);
11 }
12}
在实际的开发中,我们通常将属性设置为private的,通过对外提供get;set;属性访问器供外部调用,关于这点这里就不作详细介绍。
3. 构造方法
构造方法是用得最多的一个特性,与普通的方法相比不同的是构造方法无返回值修饰符,每当你要创建一个对象实际上就是在调用给对象的构造方法。实际开发中,我们可以多次重载构造方法,以及通过构造方法来初始化属性的值。示意性代码如下:
2 {
3 public Computer() { } //无参构造方法
4 public Computer(string name) //重载构造方法,带一个参数
5 {
6 this.Name = name;
7 }
8
9 private string name;
10 public string Name
11 {
12 get { return name; }
13 set { name = value; }
14 }
15}
16 class TestComputer
17 {
18 void Method()
19 {
20 Computer computer = new Computer(); //调用无参构造方法
21 computer.Name = "张三"; // 通过属性访问器设置属性的值
22 Console.WriteLine(computer.Name); //调用重载的带参数构造方法
23 computer = new Computer("张三"); //通过构造方法设置属性的值
24 Console.WriteLine(computer.Name);
25 }
26}
4. 行为(方法)
这点很容易理解,类的行为也就是类的方法,如上面类构造方法里的程序代码,在TestComputer类里就有一个方法Method,我们可以称为是TestComputer类的行为或是方法。
5. 类对象(实例)
这里也很容易理解,在之前我们已经使用了多次类的对象了。如TestComputer类的Method方法里,就创建了Computer类型的类对象computer,通过此对象,我们可以很方便的访问Computer类里公开的属性和方法,示意代码如下:
2 {
3 //创建一Computer类型的对象computer
4 Computer computer = new Computer("张三");
5 string name = computer.Name; //通过对象computer去调用Computer类的属性Name
6}
6. 类继承
由于C#是单继承语言,因此在类的继承上只支持单一继承,也就是说一个类只能有一个父类,但是可以继承多个接口。
2 {
3 public string Name { get; set; };
4}
5 class B:A // 类B继承于类A
6 {
7
8}
2 {
3 public string Name { get; set; };
4}
5 interface Ia
6 {
7 void MethodA();
8}
9 class B:A,Ia // 类B继承于类A,同时继承于Ia接口
10 {
11 public void MethodA()
12 {
13 //
14 }
15}
2 {
3 public string Name { get; set; };
4}
5 interface Ia
6 {
7 void MethodA();
8}
9 interface Ib
10 {
11 void MethodB();
12}
13 class B:A,Ia,Ib // 类B继承于类A,同时继承了Ia,Ib接口
14 {
15 public void MethodA()
16 {
17 //
18 }
19
20 public void MethodB()
21 {
22 //..
23 }
24}
7. 类修饰
关于类修饰这里就不做详细介绍,只是简单的提出几个概念。详细请查阅相关资料了解更多。下面是几种常见的类修饰:
2 public static class ClassName { }
3 public sealed class ClassName { }
4 public partial class ClassName { }
本文介绍了与类相关的常用知识点,当然类还有很多的知识点本文里没有提出,要更详细的了解类请查阅相关书籍和资料,篇幅限制,本文就介绍于此,希望本文对新手有所帮助。个人能力有限,文中难免会出错,还望大家勇言提出,谢谢。
在上一篇文章中,介绍了类如何封装程序中的对象.而实际中,出了类可以封装对象外,结构和枚举也可以封装一些对象,本文将着重介绍结构和枚举这两个知识点的相关应用.
一. 结构(Structure)
什么是结构(Structure)?在C#里,Struct是一种值类型,通常用来封装小型相关变量组,和类有很大的相似度.同类一样,它不但可以包含不同数据类型数据。还可以包含复合类型(数组,结构,DateTime等等)除了以名字/值方式出现还可以有属性和方法,所以说结构是一种强大的复合型数据。
1. 使用类封装对象的属性
以图书来作为示例来分析,图书包括图书编号,图书名称,图书作者以及出售价格等.我们平时所通常都是使用类来封装这些信息,如下:
2/// 图书对象
3/// </summary>
4 public class Books
5 {
6 public int bookId;
7 public string bookName;
8 public string bookAuthor;
9 public double bookPrice;
10}
2. 使用结构封装对象的属性
其实出了使用类来封装,我们还可以使用结构来封装,实现如下:
2/// 使用结构封装图书信息
3/// </summary>
4 public struct Book
5 {
6 public int bookId;
7 public string bookName;
8 public string bookAuthor;
9 public double bookPrice;
10}
3. 结构可以定义构造方法
2/// 使用结构封装图书信息
3/// </summary>
4 public struct Book
5 {
6 /**//// <summary>
7 /// 可定义构造方法
8 /// </summary>
9 public Book()
10 {
11 //
12 }
13
14 public int bookId;
15 public string bookName;
16 public string bookAuthor;
17 public double bookPrice;
18}
4. 可以使用new进行初始化
2/// 使用结构封装图书信息
3/// </summary>
4 public struct Book
5 {
6 /**////// <summary>
7 ///// 可定义构造方法
8 ///// </summary>
9 //public Book()
10 //{
11 // //这里需要注意,当结构中已有带参数的构造方法时,则不能定义无参数的构造方法
12 //}
13
14 public Book(int id, string name, string author, double price)
15 {
16 bookId = id;
17 bookName = name;
18 bookAuthor = author;
19 bookPrice = price;
20 }
21 public int bookId;
22 public string bookName;
23 public string bookAuthor;
24 public double bookPrice;
25}
5. 结构可以实现接口
2/// 接口
3/// </summary>
4 public interface IBook
5 {
6 DataSet QueryAll();
7}
8
9 /**/ /// <summary>
10/// 结构实现接口
11/// </summary>
12 public struct BookComponent : IBook
13 {
14 public DataSet QueryAll()
15 {
16 return null;
17 }
18}
6. 类和结构的区别
从上面可以看出,无论是使用类还是结构,都可以用来封装对象的属性.当然出了相同点外,两者之间还是有很大的不同,大体如下:
类型:类是引用类型,结构是值类型.
继承:类可继承,结构不能继承.结构不能继承其他的结构和类,或是被用作其他结构或类的基类.
构造方法与析够函数:结构可以定义构造方法,但不能定义析够函数.类既可以定义构造方法,也可定义析够函数.
对象创建:可以使用new进行初始化,或进行手工初始化.类和结构都可以.
结构与接口:结构可以实现一个或多个接口. 如上:public struct BookComponent:IBook{ //....}.
多态: 结构与接口之间是支持多态的.如上面的结构实现接口为例,多态:IBook book = new BookComponent();
二. 枚举(Enumeration)
什么是枚举(Enumeration)?枚举可以理解为通过预定义列出所有值的标识符来定义一个有序集合,这些值的次序和枚举说明中的标识符的次序一致的. 通常用于定义一些不变的序列,如一年四个季节,一礼拜为七天等等. 可以通过enum来定义(这里主要是针对C#语言的实现,其实在JAVA语言里也是通过enum来定义).
1. 枚举的定义形式
修饰符 enum 枚举名
{
标识符1,
标识符2,
..........,
标识符n
}
如下则为一个完整的枚举定义:
2/// 技术方向
3/// </summary>
4 public enum TechnologyDirection
5 {
6 CPlusPlus程序员,
7 Java程序员,
8 DotNet程序员,
9 架构设计师,
10 开发组长,
11 需求分析师
12}
2. 另类枚举定义(可以初始化值)
2 /**/ /// <summary>
3/// 季节
4/// </summary>
5 public enum Season
6 {
7 SPRING, //春天
8 SUMMER, //夏天
9 AUTUMN, //秋天
10 WINTER //冬天
11}
12 ============== 以下为第二种定义方法 ==============
13 /**/ /// <summary>
14/// 季节
15/// </summary>
16 public enum SeasonTwo
17 {
18 SPRING = 0, //春天
19 SUMMER = 1, //夏天
20 AUTUMN = 2, //秋天
21 WINTER = 3 //冬天
22}
23 ================= TEST ===================
24 class Program
25 {
26 static void Main(string[] args)
27 {
28 //以上两种定义方法效果都一样
29 Console.WriteLine(Season.AUTUMN);
30 Console.WriteLine(SeasonTwo.AUTUMN);
31 //运行结果都为:AUTUMN
32 }
33}
如果不初始化值则从0开始,实际上面两种定义方式定义得到的都是一个效果.
3. 对象的形式实现
何为对象的形式实现呢? 其实这个很简单,也就是将需要枚举的标识定义在对象里,通过对象的特性将其封装.详细如下代码:
2 {
3 public static readonly SeasonOne SPRING = new SeasonOne("春天");
4 public static readonly SeasonOne SUMMER = new SeasonOne("夏天");
5 public static readonly SeasonOne AUTUMN = new SeasonOne("秋天");
6 public static readonly SeasonOne WINTER = new SeasonOne("冬天");
7
8 public SeasonOne(string name)
9 {
10 this.name = name;
11 }
12
13 //成员
14 private string name;
15 public string Name
16 {
17 get { return name; }
18 set { name = value; }
19 }
20}
以上的形式实现很简单, 不过好象已经偏离了"枚举(enum)",因为实现已经由枚举转向为了类(class)对象来实现了.定义一成员属性,通过构造方法初始化,在类里定义需要枚举的只读成员,通常不建议使用此方式来实现. 我们可以这样来测试:
2 {
3 static void Main(string[] args)
4 {
5 Console.WriteLine(SeasonOne.AUTUMN.Name);
6 //运行结果:秋天
7 }
8}
4. Java中的枚举
在与C#语言一样流行的JAVA中,枚举还存在着另类故事,作为一个面向对象的开发者或是既将成为面向对象的开发者的你,我想了解下另类语言的某些特性是有必要的.在JAVA中,枚举除了上面所介绍的相关功能外,还有其他的一些功能,下面将简单的介绍下这些功能的应用.
枚举里定义抽象方法,标识符实现抽象方法
2 ADD {
3 public double calculate(double a,double b){
4 return a+b;
5 }
6 },
7 SUBSTRACT{
8 public double calculate(double a,double b){
9 return a-b;
10 }
11 },
12 MULTIPLY{
13 public double calculate(double a,double b){
14 return a*b;
15 }
16 },
17 DIVIDE{
18 public double calculate(double a,double b){
19 return a/b;
20 }
21 };
22 public abstract double calculate(double a,double b);
23}
枚举里定义属性字段和构造方法
2 COREJAVA("zhangsan"), //zhangsan学CoreJava
3 WEB("lisi"), //lisi学Web
4 EJB ("wangwu"); //wangwu学EJB
5
6 String name;
7 Course(String name){
8 this.name=name;
9 }
10 public String getName(){
11 return this.name;
12 }
13}
属性字段,构造方法及抽象方法并存
COREJAVA("zhangsan"){
public void study(){
System.out.println("Study Corejava");
}
},
WEB("lisi"){
public void study(){
System.out.println("Study Web");
}
},
EJB ("wangwu"){
public void study(){
System.out.println("Study EJB");
}
};
String teachName;
Course(String name){
this.teachName=name;
}
public String getName(){
return this.teachName;
}
public abstract void study();
}
三. .NET Framework中的枚举基类
在.NET Framework中枚举基类(Enum)是一抽象类,位于System命名空间下,继承了ValueType类和IComparable, IFormattable, IConvertible接口,这里以一个简单的文本编辑作为示例介绍下枚举在实际中的应用.
简单文本编辑器示例运行图如下:
从上图很容易看出,此文本编辑器主要是用于设置字体样式,其实在实现这个简单文本编辑器的时候就是使用的字体样式枚举(FontStyle),FontStyle的源代码定义如下:
2 // 指定应用到文本的字形信息。
3 [Flags]
4 public enum FontStyle
5 {} {
6 // 摘要:
7 // 普通文本。
8 Regular = 0,
9 //
10 // 摘要:
11 // 加粗文本。
12 Bold = 1,
13 //
14 // 摘要:
15 // 倾斜文本。
16 Italic = 2,
17 //
18 // 摘要:
19 // 带下划线的文本。
20 Underline = 4,
21 //
22 // 摘要:
23 // 中间有直线通过的文本。
24 Strikeout = 8,
25}
要实现上图示的简单文本编辑器很简单,基本思路就是通过点击上面字体样式设置功能键,设置编辑区域的文本字体样式,这实现很简单.在此,我就直接把代码贴出来,有不清楚之处可从下面我提供的示例代码下载连接下载本文示例代码查看.
2 {} {
3 ToolStripButton btn = sender as ToolStripButton;
4 FontStyle fontStyleContent = this.rchTxtContent.SelectionFont.Style;
5 FontStyle BtnFont = ( FontStyle)(Enum.Parse(typeof(FontStyle),btn.Tag.ToString()));
6 if ((fontStyleContent | BtnFont) == fontStyleContent)
7 {
8 fontStyleContent = ~BtnFont & fontStyleContent;
9 }
10 else
11 {} {
12 fontStyleContent = fontStyleContent | BtnFont;
13 }
14 this.rchTxtContent.SelectionFont = new Font(this.rchTxtContent.SelectionFont.FontFamily,
15 this.rchTxtContent.SelectionFont.Size,
16 fontStyleContent,
17 this.rchTxtContent.SelectionFont.Unit);
18 }
本文就介绍于此,更多请关注本系列后续文章,本文示例代码下载
C#接口是一个让很多初学者容易迷糊的东西,用起来好象很简单,定义接口,然后在里面定义方法,通过继承与他的子类来完成具体的实现。但没有真正认识接口的作用的时候就觉得用接口是多此一举,当然你这样想是绝对错误的。在软件设计中有一个非常重要的原则就是:面向接口编程,依赖与接口或抽象层。可见接口在真正的开发中是多么的重要。
在之前C#编程利器之一:类(Class)一文里介绍了类的相关知识,本文主要介绍OO编程中的另一个重要知识点--接口。在某种程度上说,接口也是类,一种特殊的类或抽象类。 更准确说接口只包含方法、委托或事件的签名。方法的实现是在实现接口的类中完成的[MSDN]。
一、接口的定义
如上MSDN上对接口的定义,接口只包含方法、委托或事件的签名。这句话用更通俗点的解释便是,接口只是负责完成定义的操作,而不去实现具体的细节。如下面的IPlayer接口,它是一个玩游戏的接口,里面只是定义了相应的方法,而不带方法的具体实现,代码如下:
2/// 玩游戏接口
3/// </summary>
4 public interface IPlayer
5 {
6 /**//// <summary>
7 /// 获取玩家的名字
8 /// </summary>
9 /// <returns>玩家的名字</returns>
10 string GetName();
11
12 /**//// <summary>
13 /// 由Player决定出什么手势
14 /// </summary>
15 /// <returns>本接口定义的三个常量之一</returns>
16 string Show();
17}
以上就是一个典型的接口的定义。定义了一个名为IPlayer的接口,内部定义了两个方法GetName和Show。除了在接口里定义方法以外,我们还可以定义属性、索引及事件等,详细请查看MSDN上的定义或是相关书籍,这里以属性为例简单介绍下,在接口里只能定义不实现,具体的实现是交给其子类去完成的,那么属性应该怎么定义呢?
通常我们定义属性如下:
2/// 定义_Name属性,并提供get;set属性访问器
3/// </summary>
4 private string _Name;
5 public string Name
6 {
7 get { return _Name; }
8 set { _Name = value; }
9}
那么在接口中又是怎么定义属性,并让其子类去实现呢?如下代码段:
2/// 定义接口,并在接口里定义一名为Name的属性
3/// </summary>
4 public interface IAttribute
5 {
6 string Name { get;set;}
7}
8 /**/ /// <summary>
9/// 定义一个类去继承IAttribute接口,并实现其属性
10/// </summary>
11 public class Component : IAttribute
12 {
13 public string Name
14 {
15 get
16 {
17 return "张三";
18 }
19 set
20 {
21 this.Name = value;
22 }
23 }
24}
二、接口的实现
在本文开始部分曾经说过,接口只负责定义,不负责实现,具体的实现是交给他的子类去完成的。 OK,现在我们就以上面定义的玩游戏的接口IPlayer为例,来简单的介绍下接口的实现。
就拿我的趣味编程中的玩剪刀石头布的案例来说吧,爷爷和奶奶从小就教授小孙子各中东西,其中玩趣味游戏就他们常有的事,可小孙子还小不知道变换,每次都出剪刀,这样能赢他爷爷吗?有了这个分析,我们可以怎么做呢?上面定义了接口,我们是不是直接去实现这个接口便OK了。爷爷和小孙子玩游戏,那么就定义两个类去继承IPlayer接口。代码如下:
2/// 出手动作状态
3/// </summary>
4 public class Options
5 {
6 public static readonly string JIANDAO = "剪刀";
7 public static readonly string SHITOU = "石头";
8 public static readonly string BU = "布";
9}
游戏里只会出现这三种动作状态,所以我们可以进行封装,这里是通过类封装的,当然我们也可以通过别的相关技术来封装,比如在本系列第二篇文章《C#编程利器之二:结构与枚举(Structure and enumeration)》 里介绍的结构与枚举,本例中所出现的这三中不变的状态完全可以使用结构或枚举来封装,详细请阅读上篇文章。下面是定义爷爷(Grandpa)类和孙子(Grandson)类去实现接口(IPlayer)了。代码如下:
2/// 爷爷--玩家之一
3/// </summary>
4 public class Grandpa:IPlayer
5 {
6 public string GetName()
7 {
8 return "爷爷";
9 }
10
11 public string Show()
12 {
13 Random random = new Random();
14 int i = (int)(random.Next() * 1000) % 3;
15 switch (i)
16 {
17 case 0: return Options.JIANDAO;
18 case 1: return Options.SHITOU;
19 default: return Options.BU;
20 }
21 }
22}
2/// 孙子--玩家之一
3/// </summary>
4 public class Grandson:IPlayer
5 {
6 public string GetName()
7 {
8 return "孙子";
9 }
10
11 public string Show()
12 {
13 return Options.JIANDAO;
14 }
15}
如上,我们的GrandPa和GrandSon就实现了接口IPlayer,如下图示:
三、接口的继承
关于这点这里就不作详细的介绍,只需要记住有这样一句话就万岁了:“一个接口可从一个或多个基接口继承”。示意性代码:
2 interface IB:IA { }
3 interface IC : IA, IB { }
4 interface ID : IA, IB, IC { }
四、接口的特性
接口除了可以包含方法之外,还可以包含属性、索引器、事件等,而且这些成员都被定义为公有的。除此之外,不能包含任何其他的成员,例如:常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。
从类型上来说接口是引用类型的,类似于类,和抽象类的相似之处有三点:
1、不能实例化;
2、包含未实现的方法声明;
3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员);
五、接口与回调
通常情况下,我们创建一个对象,并马上直接去使用它的方法。然而,在有些情况下,希望能在某个场景出现后或条件满足时才调用此对象的方法。回调就可以解决这个“延迟调用对象方法”的问题。这个被调用方法的对象称为回调对象。
首先创建一个回调对象,然后再创建一个控制器对象,将回调对象需要被调用的方法告诉控制器对象.控制器对象负责检查某个场景是否出现或某个条件是否满足.当此场景出现或此条件满足时,自动调用回调对象的方法.示意性代码如下:
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace CallBack
6 {
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 //创建一个控制器对象,将提供给它的回调对象传入
12 Resolve resolve = new Resolve(new PlayBasketball());
13 resolve.Play();
14
15 resolve = new Resolve(new PlayFootball());
16 resolve.Play();
17 }
18 }
19
20 /**//// <summary>
21 /// 定义一个接口--回调对象
22 /// </summary>
23 public interface IPlayer
24 {
25 void Play();
26 }
27
28 /**//// <summary>
29 /// 篮球
30 /// </summary>
31 public class PlayBasketball:IPlayer
32 {
33 public void Play()
34 {
35 Console.WriteLine("玩篮球");
36 }
37 }
38
39 /**//// <summary>
40 /// 足球
41 /// </summary>
42 public class PlayFootball : IPlayer
43 {
44 public void Play()
45 {
46 Console.WriteLine("玩足球");
47 }
48 }
49
50 /**//// <summary>
51 /// 控制角色--控制器对象
52 /// </summary>
53 public class Resolve
54 {
55 //持有一个接口的引用,通过构造方法初始化
56 private IPlayer player;
57 public Resolve(IPlayer player)
58 {
59 this.player = player;
60 }
61
62 public void Play()
63 {
64 player.Play();
65 }
66 }
67}
关于接口的相关知识点本文就介绍于此,更详细的学习接口这门功夫请大家查阅相关资料。
本文试图在.net Framework环境下,使用C#语言来描述委托、事件的概貌。希望本文能有助于大家理解委托、事件的概念,理解委托、事件的用途,理解它的C#实现方法,理解委托与事件为我们带来的好处。
C#是一种新的语言,希望大家能通过本文清楚地看到这些,从而可以对委托、事件等技术进行更深入的理解和探索。
关于委托与事件这两个知识点,一下很难以介绍清楚,所以我一分为二,分两篇文章来介绍,详细请可点击下面连接进行查看文章:
C#里面的集合对象,是一个很重要的知识点.可以说没有人编程不使用集合.这里我不打算过多的去介绍理论相关的知识,下面和大家分享和学习一下在平时开发中的常用集合对象,以及他们之间的关系.
记得教科书上有这样一句话:"如果需要使用同一类型的多个对象,就可以使用集合和数组。" ,是的,没有错.只是数组的大小是固定的。如果元素个数是动态的,就应使用集合类。在.NET Framework里,集合有很多,如List<T>和ArrayList是与数组相当的集合类。还有其他类型的集合:队列、栈、链表和字典。本文不会对这些集合对象作详细的介绍,只是把常用的集合对象拿出来讨论一下。
一、数组
在实际应用中,数组又可分为:简单数组、多维数组、锯齿数组、Array数组.使用最多的应该算的简单数组和多维数组,这里我以简单数组为例简单介绍下数组的简单使用,关于的其他知识点请大家参考相关资料和书籍.
比如说我们要定义一个整型的简单数组,那应该怎么定义呢?
2 {
3 static void Main( string [] args)
4 {
5 int [] users = new int [ 5 ];
6 users[ 0 ] = 10 ;
7 users[ 1 ] = 20 ;
8 //
9 }
10 }
是上面这样定义和使用的吗?我想稍微学过编程的朋友都可以给出肯定的答案.关于这点我不想作过多的解释.见下图:
这里我们是使用的基本类型(int)类型来定义的数组,在使用开发中我们还会使用到自定义类型数组,下面我就简单的说说这方面的知识.要定义自定义类型数组,那么首先就应该有个自定义类型,当然这个类型可以封其他的相关属性.OK,如下代码段:
2 /// 自定义类型User,内部封装了一个属性name.
3 /// </summary>
4 public class User
5 {
6 private string name;
7 public string Name
8 {
9 get { return name; }
10 set { name = value; }
11 }
12 }
如上就是一个自定义的类型,我们在其内部封装了一个name属性.那么,类型已经定义好,那我们应该如何去使用这个自定义类型去定义一个数组呢?是这样的吗?
2 {
3 static void Main( string [] args)
4 {
5 User[] user = new User[ 2 ]; //定义User类型的数组
6 User u = new User();
7 u.Name = " Beniao " ;
8 user[ 0 ] = u;
9 Console.WriteLine(user[ 0 ].Name);
10 }
11 }
如上我们就完成了一个自定义类型的对象数组的定义,以及数组的相关操作.见下图:
由于多维数组、锯齿数组、Array数组等数组在实际的开发中使用不是很多,这里就不作介绍.
二、集合对象
集合类可以组合为集合,存储Object类型的元素和泛型集合类。在.NET 2.0之前,不存在泛型。现在泛型集合类通常是集合的首选类型。泛型集合类是类型安全的,如果使用值类型,是不需要装箱操作的。如果要在集合中添加不同类型的对象,且这些对象不是相互派生的,例如在集合中添加int和string对象,就只需基于对象的集合类。
象类型的集合位于System.Collections命名空间;泛型集合类位于System.Collections. Generic命名空间;当然,组合集合类还有其他方式。集合可以根据集合类执行的接口组合为列表、集合和字典。接口及其功能如表10-1所示。.NET 2.0为集合类添加了新的泛型接口,例如IEnumerable<T>和IList<T>。这些接口的非泛型版本将一个对象定义为方法的参数,而其泛型版本使用泛型类型T。
1. 列表
.NET Framework为动态列表提供了类ArrayList和List<T>。System.Collections.Generic命名空间中的类List<T>的用法非常类似于System.Collections命名空间中的ArrayList类。这个类实现了IList、ICollection和IEnumerable接口。如下代码段:
2 {
3 static void Main( string [] args)
4 {
5 // 创建列表
6 ArrayList list = new ArrayList();
7 // 添加元素
8 list.Add( " 张三 " );
9 // 插入元素
10 list.Insert( 1 , " 李四 " );
11 // 访问元素
12 string name = list[ 0 ].ToString();
13 // 删除元素
14 list.RemoveAt( 1 ); // 删除索引号为1的元素 | Remove(Object obj);
15 // 检索元素
16 int index = list.IndexOf( " 张三 " ); // 得到对象所在的下标
17 // 排序元素
18 list.Sort(); // 有多种重载
19
20 List < int > l = new List < int > (); // 创建列表
21 l.Add( 1 ); // 添加元素
22 l.Add( 7 );
23 l.Add( 3 );
24 l.Add( 9 );
25 l.Add( 6 );
26 l.Add( 10 );
27 l.Sort();
28 foreach ( int i in l)
29 {
30 Console.Write(i + " " );
31 }
32
33 l.Remove( 10 ); // 删除值为10的元素
34 }
35 }
关于列表的算法可以参考我以前写过的一篇文章,文章连接:列表算法
2. 队列
队列是其元素以先进先出(FIFO)的方式来处理的集合。先放在队列中的元素会先读取。可结合下图来理解:
在.NET的System.Collections命名空间中有非泛型类Queue,在System.Collections. Generic命名空间中有泛型类Queue<T>。这两个类的功能非常类似,但泛型类是强类型化的,定义了类型T,而非泛型类基于Object类型。
在创建队列时,可以使用与List<T>类型类似的构造函数。默认的构造函数会创建一个空队列,也可以使用构造函数指定容量。在把元素添加到队列中时,容量会递增,包含4、8、16和32个元素。与List<T>类型类似,队列的容量也总是根据需要成倍增加。非泛型类Queue的默认构造函数与此不同,它会创建一个包含32项的空数组。
不过一般项目中用得不是很多,这里故不做示例代码.
3.链表
链表算发其实也很简单,因为项目开发中使用频率不高,所以这里就不做详细介绍.可参考以前我写的关于链表算法的一篇文章,文章连接: 单链表
关于结合对象我就简单的介绍这些,另外还有如栈,字典等集合对象,在某些时候也会用到,详细大家可以查阅相关书籍和资料.
这里我推荐一本书,大家有兴趣的可以看看《C# 2005 & .NET 3.0高级编程(第5版) 》。
注:转载出处:http://beniao.cnblogs.com/ 或 http://www.cnblogs.com/ 作者:Beniao