http://fly-sky.iteye.com/blog/1233876
c#安装配制
运行c#,只需要安装.net组件包,Microsoft.NET
安装目录:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
.net Framework类库由命名空间组成,每个命名空间可以在程序中使用的类型:类、结构、枚举、委托和接口。
Helloworld实例:
C#开发工具:SharpDevelop
下载地址:http://sourceforge.net/projects/Sharpdevelop/
数组:
ArrayList
ArrayList位于System.Collections命名空间中
ArrayList 对象是比较复杂的数组。ArrayList类提供了Array类未提供的一些功能。
Array和ArrayList
Array的容量是固定的,而ArrayList的容量可以根据需要自动扩充。
ArrayList提供添加、插入或移除某一范围元素的方法。在Array中,只能一次获取或设置一个元素的值。
Array可以具有多个维度,而ArrayList始终只是一维。
实例1:
实例2:
命名空间
.net Framework类库由命名空间组成。每个命名空间都包含可在程序中使用的类型:类、结构、枚举、委托和接口。
使用命名空间的好处
代码可分布在多个文件中。
命名空间具有扩展性。
可以堆砌出层次式的类组织结构
同时编译多个文件 csc Test1.cs Test2.cs
C#可以直接编译成dll文件(命令 csc /target:library Test.cs).
dll跟主文件关联(命令csc /reference:Test.dll Test2.cs
实例:
别名(Alias)
使用using前缀指令可以有效地解决命名空间冲突问题,不过它还有一个特性,使用using加上简称,可以让定义在其它命名空间的类型更容易使用,大多是应用在使用简短的名称来代替欲使用的类。
实例:
方法(Method)
方法是包含一系列语句的代码块。它实现了一定的功能,并拥有一个便于识别的名称,也便于在程序中调用。
声名方法的语法
成员访问修饰符 返回值 方法名称(参数列表){
//方法的内容
}
方法名称不可重复,大小写视为不同
在小括号中编写参数列表
方法的内容包含在{}之中
实例:
共享变量
如果两个以的方法共享一个变量则将此变量声名在类(Class)阶层。和局部变量不同的地方在于,类级别变量的生命周期是在此类加载到内存时就会自动地分配内存空间,要等到此对象被Common Language Runtime的垃圾回收器(Garbage Collector)回收时,才会释放掉所占用的内存空间。
方法的参数传递机制
值参数(Value Parameter)
方法名称(参数类型 参数名称 [,参数类型 参数名称])
引用参数(Reference Parameter)
方法名称(ref 参数类型 参数名称 [,ref 参数类型 参数名称])
输出参数(Output Parameter)
方法名称(out 参数类型 参数名称[,out 参数类型 参数名称])
实例:
输出参数和引用参数的区别
从CLR有角度,关键字out和关键字ref是等效的,这就是说,无论使用哪一个关键字,都会生成相同的元素,数据和IL代码。但是,C#编译器将两个关键字区别对待,在C#中,这两个关键字的区别在于哪个方法负责初始化引用对象。如果方法的参数标记为out,那个调用者不希望在调用方法之前初始化对象,被调用者不希望调用方法不能读取对象的值,而且被调用的方法必须在返回之前为对象赋值。如果方法的参数标记为ref,那么调用者必须在调用方法之前首先初始化参数的值,被调用的方法可以读取参数或者参数赋值。
向方法传递可变数量的参数
为了将方法声明可以接受变量参数的方法,使用params关键字
实例:
值类型和引用类型
类型区别为两大类的主要原因是在于执行性能与内存资源管理的不同。由于值类型变量直接在堆栈(stack)中存储该类型的值,此类类型在内存的使用上以及访问的效能上引用类型更好,因为引用类型变量存放的是指向实际对象的指针,因此访问对象时必须多进行一次内存引用的操作方可以获取数据。且引用类型的对象必须分配多余的内存来放虚函数指针及线程同步块,对于内存的需求较大。而使用引用类型的优点是回收站会自动替你管理分配在托管(Managed Heap)当中的内存。
值类型 引用类型
变量中存放 真正的数据 指向数据的引用指针
内存空间分配 堆栈(Stack) 托管堆(Managed Heap)
内在需求 一般来说较少 较大
执行效能 较快 较慢
内存释放 执行超过定义 由回收站负责回收
时间点 变量的作用域
可以为null 不可 可以
整数是值类型,数组、字符串是引用类型,但是字符串例外,它不可改变,特性与值传递相似
字符(Char)
在.net Framework中,字符总是表示成16位Unicode代码值,这简化了全球应用程序的开发。一个字符表示成System.Char结构(一个值类型)的一个实例。
扩展ASCII编码--ISO8859
0-127 与ASCII兼容 128-159 保留
160 空格 161-255 新增字符代码
中文编码
GB2312-80 6768个汉字
GBK 21003个汉字
GB18030 可变字长编码
UNICODE 全称:Universal Multiple-Octet Coded Character Set
简称为:UCS
UTF编码 传输编码UTF-8 (可变长度)
Char属性
Char.IsDigit //数字
Char.IsLetter //字母
Char.IsUpper //大写字母
Char.IsLower //小写字母
。。。。
Char.IsLetterOrDigit //字母或数字
实例:
类(Class)
类是面向对象程序设计的核心部分,在逻辑上它是一些相依的功能(或称方法)和数据的组合体。
面向对象(Object Oriented)
OO的特点:
继承 封装 多态
构造器
构造器是允许将类型实例初始化为有效状态的特殊方法。
类的继承用 “:”表示 :this() //继承当前无参构造器
局部变量使用时必须赋值,引用变量可以不赋初值
使用base关键字,可以调用父类构造器 :base()
静态构造器不能有参数,只能是私有,只执行一次,形式 static A(){}
实例:
析构(Destructor)
垃圾回收机制托管堆(Finalize的操作
析构器类前加“~”,只能有一个析构函数。
哪些情况会导致Finalize方法的调用
第0代对象充满
代码显示地调用System.GC的Collect方法
Windows报告内存不足
CLR卸载应用程序域
CLR被关闭
实例:
终结(finalization)
文件 网络连接 套接字 互斥体
释放模式
手动释放 Dispose()方法或Close()方法
自动释放 finalize()方法
实例:
访问控制修饰符
内部 子类 程序内 程序外
default V
public V V V V
private V
internal V V V
protected V V
protected internal(组合) V V V
类修饰
sealed 不能被继承的类
partial 可以声名在不同文件中的同一个类
实例:
C#类型转换
C#中类型转换的机制分为两种:
隐式转换(Implicit Conversions)
显示转换(Explicit Conversions)
隐式转换不需要指明欲转变的目的类型;而显示转换明确使用转换运算符(cast)指定要转换成哪一种类型。
checked和unchecked的使用准则
写代码时,将可能发生非预期溢出的代码放到一个checked块中。
写代码时,将允许发生溢出的代码显式地放到一个unchecked块中。
实例:
引用类型的转换
将一个对象从一种类型转换成其他类型,CLR允许将一个对象强制转换成它类型或者它的任何基类型。
实例:
属性(property)
属性是类、结构和接口的命名成员。它们提供了通过访问器(accessor)读、写或计算私有字段值的灵活机制。
属性是这样的成员:它们提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公用数据成员一样使用属性,但实际上它们是称为“访问器”的特殊方法。这使得数据在可被轻松访问的同时,仍能提供方法的安全性和灵活性。
属性的种类
属性的四个种类:
可读写(Read/Write)属性:需要现get与set访问器。
只读(Readonly)属性:需实现get访问器。
只写(Writeonly)属性:需实现set访问器。
Static属性:只能访问静态数据。
实例:
索引器(indexer)
索引器允许类或结构的实例按照与数组相同的方式进行索引。索引器类似于属性,不同之处在于它们的访问器采用参数。
实例:
Hashtable
在.NET Framewrok中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key/value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hahtable中key/value键值对均为object类型,所以Hashtable可以支持类型的key/value键值对。
实例
索引器与数组的比较
索引器的索引值(Index)类型不受限为整数
用来访问数组的索引值(index),其类型一定为整数,然而索引可以定义其它判刑的索引值。
索引器允许重载(Overloadin)
一个类不限只能够定义一个索引器,只要索引器的函数签名不同,一个类就可以拥有多个索引器,你可以重载它的功能。
索引器不是一个变量
索引器和数组不同的地方在于,索引器并没有对应应用数据存储的地方,而数组则有。索引器有get访问与set访问器,用来指明要读取或写入索引器元素时,需要执行的代码。
索引器与属性的不同点
标识方式:属性以名称来标识;索引器则以函数签名来标识。
索引器可以被重载:因为属性是以名称标识的,所以不能能被重载;索引器是用函数签名标识的,因此可以重载。
索引器不可以声明为static:属性可以为static,而索引器永远属于实例成员,不能为static.
实例:
《C#语言规范》
安装Microsoft Visual Studio 2005后位于 ...\Microsoft Visual Studio 8\VC#\Specifications\2052
BitArray(索引类)
一个Int32占用4个Byte的空间
1byte=8bit
一个Int32=4*8bit=32bit
存放 int[] bits;
实例:
BitArray.cs
MainForm.cs核心代码
预览效果如下图:
委托(delegate)
委托声明定义了一种类型,它用一组特定的参数以及返回类型来封装方法。对于静态方法,委托对象是封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果你有一个委托和一组适当的参数,则可以用这些参数调用该委托。
实例:
事件(event)
事件是类在发生其关注的事情时用来提供通知的一种方式。
事件发行者(Publisher)
一个事件发行者,也称作发送者(sender),其实就是一个对象,这个对象会自行维护本身的状态信息。当本身状态信息变动时,便触发一个事件,并通知所有的事件订阅者。
事件订阅者(Subscriber)
对事件感兴趣的对象,也称为接收者(Receiver),可以注册感兴趣的事件,通常需要提供一个事件处理程序,在事件发行者触发一个事件后,会自动执行这段代码的内容。
实例:
事件的四个步骤
事件件发行者中定义一个事件
在事件发行者中触发事件
在事件订阅者中定义事件处理程序
向事件发行者订阅一个事件
.NET Framework事件设计准则
事件的命名准则应使用PascalCasing命名方式
声名delegate时,使用void类型当作返回值,EventName事件的事件委托是EventNameEventHandler,事件接受两个传入参数,一律为sender与e.
定义一个提供事件数据的类。对类以EventNameEventArgs进行命名,从System.Eventargs派生该类,然后添加所有事件特定的成员。
(public delegate void EventNameEventHandler(object sender,EventNameEventArgse);
下面的过程说明如何在类中实现事件。如果事件不需要传递具体条件如何数据,也要声明两参数,e参数可以使用系统提供的System.EventArgs类。如果需要传递数据,则要从System.EventArgs继承一个类,并把数据放在里面。
public delegate void EventNameEventHandler(object sender,EventArgs e);
public event EventNameEventHandler EventName;
在引发事件的类中提供一个受保护的方法。以OnEventName进行命名。在该方法中引发该事件
protected virtual void OnEventName(EventArgs e){
if(EventName != null){
EventName(this,e);
}
}
实例:
虑方法
virtual 关键字用于修饰方法、属性、索引器或事件声名明,并且允许在派生中重写这些对象。
实例:
多态是指两个或多个属于不同的对象,对同一个消息做出不同响应的能力。
实例:
抽象类(abstract)
abstract修饰衔可以和类、方法、属性、索引器及事件一起使用。在类声明中使用abstract修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象派生的类来实现。
抽象类的特性
抽象类不能实例化。
抽象类可以包含抽象方法和抽象访问器。
不能用sealed修饰符修改抽象类,这意味着抽象类不能被继承。
从抽象类派生的非抽象类必须包括继承的所有抽象方法的抽象访问器的实现
抽象方法是隐式的虚方法。
只允许在抽象类中使用抽象方法声明。
因为抽象方法声明不提供实际的实现,所以没有方法体;方法声明只是以一个号结束,并且在签名后没有大括号({}),实现由一个重写方法提供,此重写方法是非抽象类的成员。
在抽象方法声明中使用static或virtual修饰符是错误的。
除了在声明和调用语法上不同外,抽象属性的行为与抽象方法一样。
在静态属性上使用abstract修饰符是错误的。
在派生类,通过包括使用override修饰符的属性声明,可以重写抽象的继承属性。
实例:
任何情况下,抽象类都不应进行实例化,因此,正确定义其构造函数就非常重要。确保抽象类功能的正确性和扩展性也很重要。下列准则有助于确保抽象类能够正确地设计并在实现后可以预期方式工作。
不要在抽象类型中定义公共的或受保护的内部构造函数,具有public或protected internal可见性的构造函数用于能进行实例化的类型。任何情况下抽象类型都不能实例化。
应在抽象类中定义一个受保护(protected)构造函数或内部(private)构造函数。
如果在抽象类中定义一个受保护构造函数,则在创建派生类的实现时,基类可执行初始化任务。
实例:
抽象方法和虚方法的区别在于:虚拟方法有一个实现部分,并为派生类提供了覆盖该方法的选项,相反,抽象方法没有提供实现部分,强制派生类覆盖方法(否则派生类不能具体类)
abstract方法只能在抽象类中声明,虚方法则不是。
abstract方法必须在派生类中重写,而virtual则不必。
abstract方法不能声明方法实体,虚方法则可以。
包含abstract方法的类不能被实例化,而包含virtual的类则可以。
接口(interface)
接口(iterface)定义了一个可由和结构实现的协定。接口可以包含方法、属性一、事件和索引器。接口不提供它所定义的成员实现——它仅指定实现该接口类或结构必须提供的成员。
一个接口声名可以声名零个或多个成员。
接口的成员必须是方法、属性、事件或索引器。
接口不能包含常量、字段、运算符、实例构造函数、析构函数或类型,也不能包含任何种类的静态成员。
所有接口成员都隐式地具有public访问属性一。
接口成员声明中包含任何修饰都属于编译时错误。具体来说,不能使用修饰符abstract、public、protected、internal、private、virtual、override或static来声明接口成员。
完全限定接口成员名
接口成员有时也用它的完全限定名(fully qualified name)来引用。接口成员的完全限定名是这样组成的:声明该成员的名称,后跟一个点,再后跟该成员的名称。成员的完全限定名将引用声明该成员的接口。
显示接口成员实现
为了实现接口,类或结构可以声明显示接口成员实现(explicit interface member implementation)。
显示接口成员实现就是一种方法、属性、事件或索引器声明,它使用完全限定接口成员名称作为标识符。
显示接口成员实现有两个主要用途
由于显示接口成员实现不能通过类或结构实例来访问,因此它们就不属于类或结构的自身的公共接口。当需在一个公用的类或结构中实现一些仅供内部使用(不允许外界访问)的接口时,这就是特别有用。
显示接口成员实现可以消除因同时含有多个相同签名的接口成员所引起的多义性。如果没有显示接口成员实现,一个类或结构就不可能为具有相同签名和返回类型的接口成员分别提供相应的实现,也不可能为具有相同签名和不同返回类型的所有接口成员中的任何一个提供实现。
显示接口成员实现
在方法调用、属性访问或索引器访问中,不能直接访问“显示接口成员实现”的成员,即使用它的完全限定名也不行。“显式接口成员实现”的成员只能通过接口实例访问,并且在通过接口实例访问时,只能用该接口成员的简单名称来引用。
显示接口成员实现中包含访问修饰符属于编译时错误,而且如果包含abstract、virtual、override或static修饰符也属于编译时错误。
显示接口成员实现具有与其他成员不同的可访问性特征。由于显示接口成员实现永远不能在方法调用或属性访问中通过它们的完全限定名来访问,因此,它们似乎是private(私有的)。因为它们可以通过接口实例来访问,所以它们似乎又是public(公共的)。
实例:
接口的多继承 接口重新实现
一个类若继承了某个接口的实现,则只要将该接口列入它的基类列表中,就可以重新实现(re-implemant)该接口。
接口的重新实现与接口的初始实现遵守循完全相同的接口映射规则。因此,继承的接口映射不会对为重新实现该接口而建立的接口映射产生任何影响。
实例:
接口与多态 接口与抽象类
1、类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类。接口只是行为的规范或规定。接口表述的是“我能做”(can-do),抽象类更多的是定义在系列紧密相关的类间(Is-a),而接口大多数是关系疏松但都实现某一个功能的类中
2、接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法;
3、一个类一次可以实现若干个接口,但是只能扩展一个父类
4、抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可娈的,而把可变的由子类去实现。
5、好的接口定义应该是具有有专一功能性的,而不是多功能的,否则造成污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其它方法,就叫接口污染。
6、如果抽象类实现接口,则可以把接口方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口方法。
实例:
接口与抽象类
1、抽象类用于部分部分实现一个类,再由用户按需求对其进行不同的扩展和完善;接口只是定义一个行为的规范或规定。
2、抽象类在组件的所有实现间提供通用的已实现功能;接口创建在大范围全异对象间使用的功能。
3、抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。
4、抽象类主要用于设计大的功能单元;而接口用于设计小而简练的功能块。
接口应用
面向接口编程
设计模式
用接口实现排序
实现IComparable接口时,我们应该使用显式接口实现,同时为接口中的方法提供一个公有的强类重载版本。强类型的重版本在提高代码性能的同时,能减少误本。强类型的重载版本在提高代码性能的同时,能减少误用CompareTo()方法的可能。
实例:
类型(Type)
C# 中有两种类型:
值类型(value type)
引用类型(reference type)
值类型的变量直接包含它们的数据,而引用类型的变量存储对它们的数据的引用,后者称为对象。
C#的值类型进一步划分为简单类型(simple type)、枚举类型(enum type)和结构类型(struct type).
C#的引用类型进下一步划分为类类型(class type)、接口类型(interface type)、数组类型(array type)和委托类型(delegate type).
当类型是一个十分简单的类型,其中没有成员会修改类型的任何实例字段。
类型不需要从其他任何类型继承。
类型不会派生出其他任何类型。
类型的实例较小。
类型不作为方法参数传递,也不作为方法的返回类型使用。
实例:
装箱与拆箱
装箱(boxing)和拆箱(unboxing)的概念是C#的类型系统的核心。它在value-type和reference-type之间的架起了一座桥梁,使得任何value-type的值都可以转换为object类型的值,反过来转换也可以。装箱和拆箱使我们能够统一地来考察类型系统,其中任何类型的值最终都可以按对象处理。
装箱操作
1、从托管堆中分配好内存。分配的内存量是值类型的各个字段所需要的内存量加上同步和方法表指针所需要的内存量。
2、值类型的字段复制到新分配的堆内存。
3、返回对象的地址。现在,这个地址是对一个对象的引用,值类型现在是一个引用类型。
拆箱操作
1、检查装箱引用,如果为null则抛出NullReferenceException异常
2、根据显示类型转换过程中的声明,来检查装箱对象的类型,如果不是,则抛出InvalidCastException异常。
3、将该值从实例复制到值类型变量中。
实例:
泛型(Generic)
泛型是2.0版本C#语言和公共语言运行库(CLR)中的一个新功能。泛型将类型参数的概念引入.NET Framewrok,类型参数使得设计如下类和方法成为可能:这些类和方法将一个或多个类型的指定推迟到客户端代码声名并实例化该类或方法的时候。例如,通过使用泛型类型参数T,你可以编写其它客户端代码能够使用的单个类,而不至于引入运行时强制转换或装箱操作的成本或风险。
栈(Stack) 进栈:Push 出栈:Pop
实例:
博客 http://cgbluesky.blog.163.com
运行c#,只需要安装.net组件包,Microsoft.NET
安装目录:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
.net Framework类库由命名空间组成,每个命名空间可以在程序中使用的类型:类、结构、枚举、委托和接口。
Helloworld实例:
- using System;
- class Hello
- {
- static void Main(){
- Console.WriteLine("Hello world!");
- }
- }
C#开发工具:SharpDevelop
下载地址:http://sourceforge.net/projects/Sharpdevelop/
数组:
- using System;
- class ArrayTest
- {
- static void Main(){
- //int[] arr = new int[]{1,2,3};
- int[] arr = {1,2,3};
- //foreach(int i in arr){
- //Console.WriteLine(i);
- for(int i=0;i<arr.Length;i++){
- Console.WriteLine(arr[i]);
- Console.WriteLine("arr[{0}]={1}",i,arr[i]);
- }
- Console.WriteLine(Console.ReadLine());
- }
- }
ArrayList
ArrayList位于System.Collections命名空间中
ArrayList 对象是比较复杂的数组。ArrayList类提供了Array类未提供的一些功能。
Array和ArrayList
Array的容量是固定的,而ArrayList的容量可以根据需要自动扩充。
ArrayList提供添加、插入或移除某一范围元素的方法。在Array中,只能一次获取或设置一个元素的值。
Array可以具有多个维度,而ArrayList始终只是一维。
实例1:
- using System;
- using System.Collections;
- class ArrList
- {
- static void Main(){
- ArrayList arr = new ArrayList();
- String str1;
- while(true){
- Console.WriteLine("please add a string to ArrayList:");
- str1 = Console.ReadLine();
- if(str1 == "end")break;
- arr.Add(str1);
- Console.WriteLine();
- for(int i=0;i<arr.Count;i++)
- Console.Write("{0}",arr[i]);
- Console.WriteLine("\n");
- }
- }
- }
实例2:
- using System;
- class Matrix
- {
- static void Main(){
- int[,] arr = newint[4,6];
- for(int i=0;i<4;i++){
- for(int j=0;j<6;j++){
- arr[i,j] = (i+1)*10+j+1;
- Console.Write("{0} ",arr[i,j]);
- }
- Console.WriteLine();
- }
- }
- }
命名空间
.net Framework类库由命名空间组成。每个命名空间都包含可在程序中使用的类型:类、结构、枚举、委托和接口。
使用命名空间的好处
代码可分布在多个文件中。
命名空间具有扩展性。
可以堆砌出层次式的类组织结构
同时编译多个文件 csc Test1.cs Test2.cs
C#可以直接编译成dll文件(命令 csc /target:library Test.cs).
dll跟主文件关联(命令csc /reference:Test.dll Test2.cs
实例:
- using System;
- namespace CG
- {
- class Test
- {
- static void Main(){
- Console.WriteLine("my namespance is CG");
- A.A1.PrintName a = new A.A1.PrintName();
- a.intro();
- A.A2.PrintName b = new A.A2.PrintName();
- b.intro();
- }
- }
- }
- namespace A
- {
- namespace A1
- {
- public class PrintName
- {
- public void intro(){
- Console.WriteLine("My namespace is A1");
- }
- }
- }
- namespace A2
- {
- public class PrintName
- {
- public void intro(){
- Console.WriteLine("My namespace is A2");
- }
- }
- }
- }
别名(Alias)
使用using前缀指令可以有效地解决命名空间冲突问题,不过它还有一个特性,使用using加上简称,可以让定义在其它命名空间的类型更容易使用,大多是应用在使用简短的名称来代替欲使用的类。
实例:
- using System;
- using A=ParentNamespace.ChildNamespace;
- namespace CG
- {
- class Test
- {
- static void Main(){
- //ParentNamespace.ChildNamespace.PrintNamespace print = new ParentNamespace.ChildNamespace.PrintNamespace();
- A.PrintNamespace print = new A.PrintNamespace();
- print.intro();
- }
- }
- }
- namespace ParentNamespace
- {
- namespace ChildNamespace
- {
- public class PrintNamespace
- {
- public void intro(){
- Console.WriteLine("My name is namespace");
- }
- }
- }
- }
方法(Method)
方法是包含一系列语句的代码块。它实现了一定的功能,并拥有一个便于识别的名称,也便于在程序中调用。
声名方法的语法
成员访问修饰符 返回值 方法名称(参数列表){
//方法的内容
}
方法名称不可重复,大小写视为不同
在小括号中编写参数列表
方法的内容包含在{}之中
实例:
- using System;
- class Mathod
- {
- static void Main(){
- Console.WriteLine("Now time is {0}",MyMethod());
- }
- public static DateTime MyMethod(){
- return DateTime.Now;
- }
- }
共享变量
如果两个以的方法共享一个变量则将此变量声名在类(Class)阶层。和局部变量不同的地方在于,类级别变量的生命周期是在此类加载到内存时就会自动地分配内存空间,要等到此对象被Common Language Runtime的垃圾回收器(Garbage Collector)回收时,才会释放掉所占用的内存空间。
- using System;
- class A{
- public staticint i=0;
- public void addi(){
- i=i+1;
- }
- static void Main(){
- A a = new A();
- a.addi();
- A b = new A();
- b.addi();
- Console.WriteLine(A.i);
- }
- }
方法的参数传递机制
值参数(Value Parameter)
方法名称(参数类型 参数名称 [,参数类型 参数名称])
引用参数(Reference Parameter)
方法名称(ref 参数类型 参数名称 [,ref 参数类型 参数名称])
输出参数(Output Parameter)
方法名称(out 参数类型 参数名称[,out 参数类型 参数名称])
实例:
- using System;
- class Method
- {
- public staticvoid ValueMethod(int i){
- i++;
- }
- public staticvoid ReferenceMethod(refint i){
- i++;
- }
- public staticvoid OutputMethod(outint i){
- i=0;
- i++;
- }
- static void Main(){
- int i=0;
- ValueMethod(i);//局部i与Main中不是同一个变量,i的值仍然为0;
- Console.WriteLine("i="+i);
- int j=0;
- ReferenceMethod(ref j);
- Console.WriteLine("j="+j);//引用传值,i的值1
- int k;
- OutputMethod(out k);
- Console.WriteLine("k="+k);//out传值,i的值1
- }
- }
输出参数和引用参数的区别
从CLR有角度,关键字out和关键字ref是等效的,这就是说,无论使用哪一个关键字,都会生成相同的元素,数据和IL代码。但是,C#编译器将两个关键字区别对待,在C#中,这两个关键字的区别在于哪个方法负责初始化引用对象。如果方法的参数标记为out,那个调用者不希望在调用方法之前初始化对象,被调用者不希望调用方法不能读取对象的值,而且被调用的方法必须在返回之前为对象赋值。如果方法的参数标记为ref,那么调用者必须在调用方法之前首先初始化参数的值,被调用的方法可以读取参数或者参数赋值。
向方法传递可变数量的参数
为了将方法声明可以接受变量参数的方法,使用params关键字
实例:
- using System;
- class Method
- {
- static int addi(paramsint[] values){
- int sum=0;
- foreach(int iin values){
- sum+=i;
- }
- return sum;
- }
- static void Main(){
- Console.WriteLine(addi(1));
- Console.WriteLine(addi(1,2,3,4,5));
- }
- }
值类型和引用类型
类型区别为两大类的主要原因是在于执行性能与内存资源管理的不同。由于值类型变量直接在堆栈(stack)中存储该类型的值,此类类型在内存的使用上以及访问的效能上引用类型更好,因为引用类型变量存放的是指向实际对象的指针,因此访问对象时必须多进行一次内存引用的操作方可以获取数据。且引用类型的对象必须分配多余的内存来放虚函数指针及线程同步块,对于内存的需求较大。而使用引用类型的优点是回收站会自动替你管理分配在托管(Managed Heap)当中的内存。
值类型 引用类型
变量中存放 真正的数据 指向数据的引用指针
内存空间分配 堆栈(Stack) 托管堆(Managed Heap)
内在需求 一般来说较少 较大
执行效能 较快 较慢
内存释放 执行超过定义 由回收站负责回收
时间点 变量的作用域
可以为null 不可 可以
整数是值类型,数组、字符串是引用类型,但是字符串例外,它不可改变,特性与值传递相似
字符(Char)
在.net Framework中,字符总是表示成16位Unicode代码值,这简化了全球应用程序的开发。一个字符表示成System.Char结构(一个值类型)的一个实例。
扩展ASCII编码--ISO8859
0-127 与ASCII兼容 128-159 保留
160 空格 161-255 新增字符代码
中文编码
GB2312-80 6768个汉字
GBK 21003个汉字
GB18030 可变字长编码
UNICODE 全称:Universal Multiple-Octet Coded Character Set
简称为:UCS
UTF编码 传输编码UTF-8 (可变长度)
Char属性
Char.IsDigit //数字
Char.IsLetter //字母
Char.IsUpper //大写字母
Char.IsLower //小写字母
。。。。
Char.IsLetterOrDigit //字母或数字
实例:
- using System;
- using System.Text;
- class Test
- {
- static void Main(){
- char c = 'A';
- Console.WriteLine((int)c); //输出ASCII码
- Console.WriteLine('\x41'); //65-转十六进制-41 输出A
- Console.WriteLine((char)65); //输出A (速度快)
- Console.WriteLine(Convert.ToChar(65)); //输出A
- Console.WriteLine();
- for(int i=0;i<128;i++){
- if(i%10==0) Console.WriteLine();
- Console.Write("{0,3}:{1,-3}",i,(char)i);
- }
- Console.WriteLine();
- Console.WriteLine((int)'你'); //输出uncode 20320
- Console.WriteLine((char)20320);
- Console.WriteLine('\u4F60');
- Console.WriteLine();
- StringBuilder s = new StringBuilder(0xFFFF*3);
- for(int i=128;i<0xFFFF;i++){
- if(i%50==0) s.Append("\r\n");//Console.WriteLine();
- s.AppendFormat("{0,3}",(char)i);
- }
- Console.WriteLine(s.ToString());
- Console.WriteLine();
- Console.WriteLine(string.Format("{0:x},{1:x}",(int)'一',(int)'龥')); //中文范围(一——龥)4e00,9fa5
- string str = "sfrfe";
- foreach(char csin str){
- int i = (int)cs;
- if(i<0x4e00 || i>0x9fa5)
- Console.WriteLine("名字非法,不是中文名字!");
- }
- }
- }
类(Class)
类是面向对象程序设计的核心部分,在逻辑上它是一些相依的功能(或称方法)和数据的组合体。
面向对象(Object Oriented)
OO的特点:
继承 封装 多态
构造器
构造器是允许将类型实例初始化为有效状态的特殊方法。
类的继承用 “:”表示 :this() //继承当前无参构造器
局部变量使用时必须赋值,引用变量可以不赋初值
使用base关键字,可以调用父类构造器 :base()
静态构造器不能有参数,只能是私有,只执行一次,形式 static A(){}
实例:
- using System;
- class A{
- public A(){
- Console.WriteLine("我是A类无参构造方法");
- }
- }
- class B:A{
- public B(){
- Console.WriteLine("我是B类无参构造方法");
- }
- }
- class Test{
- static void Main(){
- B b =new B();
- }
- }
析构(Destructor)
垃圾回收机制托管堆(Finalize的操作
析构器类前加“~”,只能有一个析构函数。
哪些情况会导致Finalize方法的调用
第0代对象充满
代码显示地调用System.GC的Collect方法
Windows报告内存不足
CLR卸载应用程序域
CLR被关闭
实例:
- using System;
- class A{
- public A(){
- Console.WriteLine("类A被创建");
- }
- ~A(){
- Console.WriteLine("类A被释放");
- }
- }
- class Test{
- static void Main(){
- A a = new A();
- A b =new A();
- GC.Collect();
- A c =new A();
- for(string s="";s!="end";s=Console.ReadLine()){
- new A();
- for(int i=0;i<50;i++){
- byte[] bt = newbyte[1000];
- }
- }
- }
- }
终结(finalization)
文件 网络连接 套接字 互斥体
释放模式
手动释放 Dispose()方法或Close()方法
自动释放 finalize()方法
实例:
- using System;
- public class MyResource:IDisposable{
- private bool disposed =false;
- public void Dispose(){
- }
- public void Close(){
- Dispose(true);
- }
- ~MyResource(){
- Dispose(false);
- }
- private void Dispose(bool disposing){
- if(!this.disposed){
- if(disposing){
- Console.WriteLine("调用引用对象所使用的非托管资源");
- }
- Console.WriteLine("释放类本身所使用的非托管资源");
- disposed = true;
- if(disposing){
- GC.SuppressFinalize(this);
- }
- }
- }
- }
- public class Test{
- static void Main(){
- MyResource mr = new MyResource();
- //mr.Dispose();
- mr.Close();
- try{
- Console.WriteLine("调用mr做一些事件");
- }finally{
- mr.Dispose();
- }
- using(MyResource mrs = new MyResource()){
- Console.WriteLine("调用mr做一些事件");
- }
- }
- }
访问控制修饰符
内部 子类 程序内 程序外
default V
public V V V V
private V
internal V V V
protected V V
protected internal(组合) V V V
类修饰
sealed 不能被继承的类
partial 可以声名在不同文件中的同一个类
实例:
- using System;
- class Mod{
- void defaultMethod(){
- Console.WriteLine("this is default");
- }
- public void publicMethod(){
- Console.WriteLine("this is public");
- }
- private void privateMethod(){
- Console.WriteLine("this is private");
- }
- internal void internalMethod(){
- Console.WriteLine("this is internal");
- }
- protected void protectedMethod(){
- Console.WriteLine("this is protected");
- }
- protected internalvoid protectedinternalMethod(){
- Console.WriteLine("this is protected internal");
- }
- static void Main(){
- Mod mod = new Mod();
- mod.defaultMethod();
- mod.publicMethod();
- mod.privateMethod();
- mod.internalMethod();
- mod.protectedMethod();
- mod.protectedinternalMethod();
- }
- }
C#类型转换
C#中类型转换的机制分为两种:
隐式转换(Implicit Conversions)
显示转换(Explicit Conversions)
隐式转换不需要指明欲转变的目的类型;而显示转换明确使用转换运算符(cast)指定要转换成哪一种类型。
checked和unchecked的使用准则
写代码时,将可能发生非预期溢出的代码放到一个checked块中。
写代码时,将允许发生溢出的代码显式地放到一个unchecked块中。
实例:
- using System;
- using System.Windows.Forms;
- class Conversions{
- static void Main(){
- int a = 5;
- long b;
- b=a; //隐式转换
- long c = 7000000000000000;
- //a=(int)c;
- Byte d=100;
- d=(Byte)(d+100);
- try{
- //a=checked((int)c); //显示转换
- checked{
- a=(int)c;
- }
- }catch(System.OverflowException){
- MessageBox.Show("发生溢出");
- return;
- }
- Console.WriteLine(b);
- Console.WriteLine(c);
- Console.WriteLine("int的最大值"+int.MaxValue);
- Console.WriteLine("long的最大值"+long.MaxValue);
- Console.WriteLine(d);
- }
- }
引用类型的转换
将一个对象从一种类型转换成其他类型,CLR允许将一个对象强制转换成它类型或者它的任何基类型。
实例:
- using System;
- class A{
- }
- class B:A{
- public int i=0;
- }
- class C{
- static void Main(){
- A a = new B(); //成功
- //B b = new A(); //失败
- //Console.WriteLine(b.i);
- Type t = a.GetType();
- Console.WriteLine(t.FullName);
- B b = (B)a;
- Console.WriteLine(b.i);
- Console.WriteLine(((B)a).i);
- A d = new A();
- B f = new B();
- Console.WriteLine(d is A);
- Console.WriteLine(d is B);
- Console.WriteLine(f is A);
- Console.WriteLine(f is B);
- A e = new B();
- if(e is B){
- Console.WriteLine(((B)e).i);
- }
- B g = e as B;
- if(g != null){
- Console.WriteLine(g.i); //速度快
- }
- }
- }
属性(property)
属性是类、结构和接口的命名成员。它们提供了通过访问器(accessor)读、写或计算私有字段值的灵活机制。
属性是这样的成员:它们提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公用数据成员一样使用属性,但实际上它们是称为“访问器”的特殊方法。这使得数据在可被轻松访问的同时,仍能提供方法的安全性和灵活性。
属性的种类
属性的四个种类:
可读写(Read/Write)属性:需要现get与set访问器。
只读(Readonly)属性:需实现get访问器。
只写(Writeonly)属性:需实现set访问器。
Static属性:只能访问静态数据。
实例:
- using System;
- using System.Windows.Forms;
- class User{
- private string m_name;
- private string m_sex;
- private DateTime m_Birthday;
- private staticint m_LogicCount;
- public User(){
- m_LogicCount++;
- }
- public string Name{
- get{
- return m_name;
- }
- set {
- m_name = value;
- }
- }
- public string GetSex(){
- return m_sex;
- }
- public void SetSex(string sex){
- this.m_sex = sex;
- }
- public DateTime Birthday{
- set{
- if(value<Convert.ToDateTime("1900-1-1") || value.Year>DateTime.Now.Year-3)
- MessageBox.Show("用户的年龄非法");
- else
- m_Birthday = value;
- }
- get{
- return m_Birthday;
- }
- }
- public staticint LogicCount{
- get {
- return m_LogicCount;
- }
- }
- }
- class Property{
- static void Main(){
- User zs = new User();
- zs.Name = "张三";
- zs.SetSex("男");
- Console.WriteLine("姓名:"+zs.Name+"\n性别:"+zs.GetSex());
- zs.Birthday=Convert.ToDateTime("2011-12-6");
- User zs2 = new User();
- User zs3 = new User();
- Console.WriteLine(User.LogicCount);
- }
- }
索引器(indexer)
索引器允许类或结构的实例按照与数组相同的方式进行索引。索引器类似于属性,不同之处在于它们的访问器采用参数。
实例:
- using System;
- class ArrClass{ //没带索引器的使用
- private readonlystring name;
- public ArrClass(string name){
- this.name = name;
- }
- public string Name{
- get{ return name;}
- }
- }
- class IndexClass{ //带索引器的使用
- private string[] name =newstring[10];
- public stringthis[int index]{
- get{ return name[index]; }
- set{ name[index] = value; }
- }
- }
- class test{
- static void Main(){
- ArrClass[] a = new ArrClass[10];
- a[0] = new ArrClass("张三");
- a[1] = new ArrClass("李四");
- a[2] = new ArrClass("王五");
- Console.WriteLine("a[0]="+a[0].Name);
- Console.WriteLine("a[1]="+a[1].Name);
- Console.WriteLine("a[2]="+a[2].Name);
- //索引器的使用
- IndexClass b = new IndexClass();
- b[0] = "张三";
- b[1] = "李四";
- b[2] = "王五";
- Console.WriteLine("b[0]="+b[0]);
- Console.WriteLine("b[1]="+b[1]);
- Console.WriteLine("b[2]="+b[2]);
- }
- }
Hashtable
在.NET Framewrok中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key/value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hahtable中key/value键值对均为object类型,所以Hashtable可以支持类型的key/value键值对。
实例
- using System;
- using System.Collections;
- class IndexClass{ //带索引器的使用
- private Hashtable name = new Hashtable();
- public stringthis[int index]{
- get{ return name[index].ToString(); }
- set{ name.Add(index, value); }
- }
- public intthis[string aName]{
- get{
- foreach(DictionaryEntry din name){
- if(d.Value.ToString() == aName){
- return Convert.ToInt32(d.Key);
- }
- }
- return -1;
- }
- set{ name.Add(value,aName); }
- }
- }
- class test{
- static void Main(){
- //索引器的使用
- IndexClass b = new IndexClass();
- b[1001] = "张三";
- b[1002] = "李四";
- b[1003] = "王五";
- Console.WriteLine("编号为1001的员工是:"+b[1001]);
- Console.WriteLine("编号为1002的员工是:"+b[1002]);
- Console.WriteLine("编号为1003的员工是:"+b[1003]);
- Console.WriteLine("张三的编号是:"+b["张三"]);
- Console.WriteLine("李四的编号是:"+b["李四"]);
- Console.WriteLine("王五的编号是:"+b["王五"]);
- b["马六"] = 1004;
- b["钱七"] = 1005;
- Console.WriteLine("编号为1004的员工是:"+b[1004]);
- Console.WriteLine("编号为1005的员工是:"+b[1005]);
- Console.WriteLine("马六的编号是:"+b["马六"]);
- Console.WriteLine("钱七的编号是:"+b["钱七"]);
- }
- }
索引器与数组的比较
索引器的索引值(Index)类型不受限为整数
用来访问数组的索引值(index),其类型一定为整数,然而索引可以定义其它判刑的索引值。
索引器允许重载(Overloadin)
一个类不限只能够定义一个索引器,只要索引器的函数签名不同,一个类就可以拥有多个索引器,你可以重载它的功能。
索引器不是一个变量
索引器和数组不同的地方在于,索引器并没有对应应用数据存储的地方,而数组则有。索引器有get访问与set访问器,用来指明要读取或写入索引器元素时,需要执行的代码。
索引器与属性的不同点
标识方式:属性以名称来标识;索引器则以函数签名来标识。
索引器可以被重载:因为属性是以名称标识的,所以不能能被重载;索引器是用函数签名标识的,因此可以重载。
索引器不可以声明为static:属性可以为static,而索引器永远属于实例成员,不能为static.
实例:
- using System;
- using System.Collections;
- class CourseScore{
- private string name;
- private int courseID;
- private int score;
- public CourseScore(string name,int courseID,int score){
- this.name = name;
- this.courseID = courseID;
- this.score = score;
- }
- public string Name{
- get{return name;}
- set{name = value;}
- }
- public int CouresID{
- get{return courseID;}
- set{courseID = value;}
- }
- public int Score{
- get{ return score;}
- set{ score = value;}
- }
- }
- class CourseScoreIndex{
- private ArrayList arrCourseScore;
- public CourseScoreIndex(){
- arrCourseScore = new ArrayList();
- }
- public intthis[string name,int courseID]{
- get{
- foreach(CourseScore cs in arrCourseScore){
- if(cs.Name==name && cs.CouresID==courseID){
- return cs.Score;
- }
- }
- return -1;
- }
- set{
- arrCourseScore.Add(new CourseScore(name, courseID, value));
- }
- }
- public ArrayList this[string name]{
- get{
- ArrayList tempArr = new ArrayList();
- foreach(CourseScore cs in arrCourseScore){
- if(cs.Name == name){
- tempArr.Add(cs);
- }
- }
- return tempArr;
- }
- }
- }
- class Test{
- static void Main(){
- CourseScoreIndex csi = new CourseScoreIndex();
- csi["张三",1]=90;
- csi["张三",2]=80;
- csi["张三",3]=85;
- csi["李四",1]=80;
- Console.WriteLine(csi["张三",2]);
- Console.WriteLine("张三的所有课程成绩为:");
- ArrayList tempArr = csi["张三"];
- foreach(CourseScore cs in tempArr){
- Console.WriteLine("姓名:"+cs.Name+" 课程编号:"+cs.CouresID+" 分数:"+cs.Score);
- }
- }
- }
《C#语言规范》
安装Microsoft Visual Studio 2005后位于 ...\Microsoft Visual Studio 8\VC#\Specifications\2052
BitArray(索引类)
一个Int32占用4个Byte的空间
1byte=8bit
一个Int32=4*8bit=32bit
存放 int[] bits;
实例:
BitArray.cs
- using System;
- namespace Indexer
- {
- public class BitArray
- {
- int[] bits;
- int length;
- public BitArray(int length)
- {
- if(length<0){
- throw new ArgumentException();
- }
- bits = new int[((length-1)>>5)+1]; //(length-1)/32+1
- this.length=length;
- }
- public boolthis[int index]{
- get{
- if(index<0 || index>=length){
- throw new IndexOutOfRangeException();
- }
- return (bits[index>>5] & 1<<index)!=0;//1<<(index%32)
- }
- set{
- if(index<0 || index>=length){
- throw new IndexOutOfRangeException();
- }
- if(value){
- bits[index>>5] |=1<<index; //bits[index>>5]=bits[index>>5]|1<<index
- }else{
- bits[index>>5] &=~(1<<index); //bits[index>>5]=bits[index>>5]&~(1<<index)
- }
- }
- }
- }
- }
MainForm.cs核心代码
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Windows.Forms;
- namespace Indexer
- {
- public partial class MainForm
- {
- private Label[] label = new Label[32];
- [STAThread]
- public staticvoid Main(string[] args)
- {
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- Application.Run(new MainForm());
- }
- public MainForm()
- {
- InitializeComponent();
- }
- void MainFormLoad(object sender, System.EventArgs e)
- {
- for(int i=0;i<32;i++){
- label[i] = new System.Windows.Forms.Label();
- label[i].Location = new System.Drawing.Point(i*12+20, 40);
- label[i].Size = new System.Drawing.Size(10, 10);
- label[i].BackColor=Color.Green;
- Controls.Add(label[i]);
- }
- }
- void Button1Click(object sender, System.EventArgs e)
- {
- BitArray ba = new BitArray(32);
- int ctrl;
- try{
- ctrl = int.Parse(textCtrl.Text);
- }catch(System.FormatException){
- MessageBox.Show("你所输入的不是数字");
- return;
- }catch(System.OverflowException){
- MessageBox.Show("你所输入的数字太大");
- return;
- }
- for(int i=0; i<32;i++){
- ba[i]=(ctrl & 1<<i)!=0 ? true :false;
- }
- for(int i=0; i<32;i++){
- label[31-i].BackColor = ba[i] ? Color.Red : Color.Green;
- }
- }
- }
- }
预览效果如下图:
委托(delegate)
委托声明定义了一种类型,它用一组特定的参数以及返回类型来封装方法。对于静态方法,委托对象是封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果你有一个委托和一组适当的参数,则可以用这些参数调用该委托。
实例:
- using System;
- delegate void EatDelegate(string food);
- class Man{
- private string name;
- public Man(string name){
- this.name = name;
- }
- public void eat(string food){
- Console.WriteLine(name+"吃"+food);
- }
- }
- class MyDelegate{
- static void zsEar(string food){
- Console.WriteLine("张三吃"+food);
- }
- static void lsEar(string food){
- Console.WriteLine("李四吃"+food);
- }
- static void wwsEar(string food){
- Console.WriteLine("王五吃"+food);
- }
- static void Main(){
- EatDelegate zs = new EatDelegate(zsEar);
- //zs("西瓜");
- EatDelegate ls = new EatDelegate(lsEar);
- EatDelegate ww = new EatDelegate(wwsEar);
- EatDelegate eatChian = zs+ls+ww;
- eatChian("西瓜");
- eatChian -=ls;
- eatChian("香蕉");
- eatChian +=ls;
- eatChian("桔子");
- eatChian = null;
- eatChian +=delegate(string food){Console.WriteLine("张三吃"+food);};
- eatChian +=delegate(string food){Console.WriteLine("李四吃"+food);};
- eatChian +=delegate(string food){Console.WriteLine("王五吃"+food);};
- eatChian("苹果");
- eatChian = null;
- Man m1 = new Man("张三");
- Man m2 = new Man("李四");
- Man m3 = new Man("王五");
- EatDelegate e1 = new EatDelegate(m1.eat);
- EatDelegate e2 = new EatDelegate(m2.eat);
- EatDelegate e3 = new EatDelegate(m3.eat);
- eatChian = e1+e2+e3;
- eatChian("梨");
- eatToghter("西瓜",e1,e2);
- eatToghter(null,e1);
- eatToghter(null,null);
- }
- static void eatToghter(string foot,params EatDelegate[] values){
- if(values==null){
- Console.WriteLine("没有委托对象");
- }else{
- EatDelegate eatChian = null;
- foreach(EatDelegate ed in values){
- eatChian += ed;
- }
- eatChian(foot);
- }
- }
- }
事件(event)
事件是类在发生其关注的事情时用来提供通知的一种方式。
事件发行者(Publisher)
一个事件发行者,也称作发送者(sender),其实就是一个对象,这个对象会自行维护本身的状态信息。当本身状态信息变动时,便触发一个事件,并通知所有的事件订阅者。
事件订阅者(Subscriber)
对事件感兴趣的对象,也称为接收者(Receiver),可以注册感兴趣的事件,通常需要提供一个事件处理程序,在事件发行者触发一个事件后,会自动执行这段代码的内容。
实例:
- using System;
- class Publisher{
- public delegatevoid Publish();
- public delegatevoid PubComputer(string magazineName);
- public delegatevoid PubLife(string magazineName);
- public event Publish OnPublish;
- public event PubComputer OnPubComputer;
- public event PubLife OnPubLife;
- public void issue(){
- if(OnPublish!=null){
- Console.WriteLine("触发事件");
- OnPublish();
- }
- }
- public void issueComputer(){
- if(OnPubComputer!=null){
- Console.WriteLine("触发事件OnPubComputer");
- OnPubComputer("OnPubComputer");
- }
- }
- public void issueLife(){
- if(OnPubLife!=null){
- Console.WriteLine("触发事件OnPubLife");
- OnPubLife("OnPubLife");
- }
- }
- }
- class Subscriber{
- private string name;
- public Subscriber(){
- }
- public Subscriber(string name){
- this.name = name;
- }
- public void Receiver(){
- Console.WriteLine("订阅者已经接收到");
- }
- public void Receiver(string magazineName){
- Console.WriteLine(name+"已经接收到"+magazineName);
- }
- }
- class Story{
- static void Main(){
- Publisher pub = new Publisher();
- Subscriber zs = new Subscriber();
- pub.OnPublish += new Publisher.Publish(zs.Receiver);
- pub.issue();
- Console.WriteLine();
- pub = null;
- pub = new Publisher();
- Subscriber zsp = new Subscriber("张三");
- pub.OnPubComputer += new Publisher.PubComputer(zsp.Receiver);
- Subscriber lsp = new Subscriber("李四");
- pub.OnPubComputer += new Publisher.PubComputer(lsp.Receiver);
- pub.OnPubLife += new Publisher.PubLife(lsp.Receiver);
- pub.issueComputer();
- pub.issueLife();
- Console.WriteLine();
- pub.OnPubComputer -= new Publisher.PubComputer(lsp.Receiver);
- pub.issueComputer();
- pub.issueLife();
- }
- }
事件的四个步骤
事件件发行者中定义一个事件
在事件发行者中触发事件
在事件订阅者中定义事件处理程序
向事件发行者订阅一个事件
.NET Framework事件设计准则
事件的命名准则应使用PascalCasing命名方式
声名delegate时,使用void类型当作返回值,EventName事件的事件委托是EventNameEventHandler,事件接受两个传入参数,一律为sender与e.
定义一个提供事件数据的类。对类以EventNameEventArgs进行命名,从System.Eventargs派生该类,然后添加所有事件特定的成员。
(public delegate void EventNameEventHandler(object sender,EventNameEventArgse);
下面的过程说明如何在类中实现事件。如果事件不需要传递具体条件如何数据,也要声明两参数,e参数可以使用系统提供的System.EventArgs类。如果需要传递数据,则要从System.EventArgs继承一个类,并把数据放在里面。
public delegate void EventNameEventHandler(object sender,EventArgs e);
public event EventNameEventHandler EventName;
在引发事件的类中提供一个受保护的方法。以OnEventName进行命名。在该方法中引发该事件
引用
protected virtual void OnEventName(EventArgs e){
if(EventName != null){
EventName(this,e);
}
}
实例:
- using System;
- class PubEventArgs : EventArgs{
- private readonlystring m_magazineName;
- private readonly DateTime m_pubDate;
- public PubEventArgs(string magazineName,DateTime pubDate){
- m_magazineName = magazineName;
- m_pubDate = pubDate;
- }
- public string magazineName{
- get { return m_magazineName; }
- }
- public DateTime pubDate{
- get { return m_pubDate; }
- }
- }
- class Publishers{
- public delegatevoid PubComputerEventHandler(object sender,PubEventArgs e);
- public delegatevoid PubLifeEventHandler(object sender,PubEventArgs e);
- public event PubComputerEventHandler PubComputer;
- public event PubLifeEventHandler PubLife;
- protected virtualvoid OnPubComputer(PubEventArgs e){
- PubComputerEventHandler handler = PubComputer;
- if(handler != null){
- handler(this,e);
- }
- }
- protected virtualvoid OnPubLife(PubEventArgs e){
- PubLifeEventHandler handler = PubLife;
- if(handler != null){
- handler(this,e);
- }
- }
- public void issueComputer(string magazineName,DateTime pubDate){
- Console.WriteLine("触发" + magazineName);
- OnPubComputer(new PubEventArgs(magazineName,pubDate));
- }
- public void issueLife(string magazineName,DateTime pubDate){
- Console.WriteLine("触发" + magazineName);
- OnPubLife(new PubEventArgs(magazineName,pubDate));
- }
- }
- class Subscriber{
- private string name;
- public Subscriber(string name){
- this.name = name;
- }
- public void Receiver(object sender,PubEventArgs e){
- Console.WriteLine(e.pubDate + " " + name +"订阅者已经接收到" + e.magazineName);
- }
- }
- class Story{
- static void Main(){
- Publishers pub = new Publishers();
- Subscriber zs = new Subscriber("张三");
- pub.PubComputer += new Publishers.PubComputerEventHandler(zs.Receiver);
- Subscriber ls = new Subscriber("李四");
- pub.PubComputer += new Publishers.PubComputerEventHandler(ls.Receiver);
- pub.PubLife += new Publishers.PubLifeEventHandler(ls.Receiver);
- pub.issueComputer("电脑杂志",Convert.ToDateTime("2012-1-12"));
- pub.issueLife("生活杂志",Convert.ToDateTime("2012-1-12"));
- Console.WriteLine();
- Console.WriteLine("一天以后");
- pub.PubComputer -= new Publishers.PubComputerEventHandler(ls.Receiver);
- pub.issueComputer("电脑杂志",Convert.ToDateTime("2012-1-13"));
- pub.issueLife("生活杂志",Convert.ToDateTime("2012-1-13"));
- }
- }
虑方法
virtual 关键字用于修饰方法、属性、索引器或事件声名明,并且允许在派生中重写这些对象。
实例:
- using System;
- class A{
- public virtualvoid F(){
- Console.WriteLine("A.F");
- }
- }
- class B:A{
- public overridevoid F(){//public new void F()
- Console.WriteLine("B.F");
- }
- }
- class C : B{
- public overridevoid F(){
- Console.WriteLine("C.F");
- }
- }
- class Test{
- static void Main(){
- B b = new B();
- A a = b;
- a.F();
- b.F();
- Console.WriteLine();
- C c = new C(); //多态
- A a1 = c;
- B b1 = c;
- a1.F();
- b1.F();
- c.F();
- }
- }
多态是指两个或多个属于不同的对象,对同一个消息做出不同响应的能力。
实例:
- using System;
- class Employee{
- protected string _name;
- public Employee(){}
- public Employee(string name){
- _name = name;
- }
- public virtualvoid StartWork(){
- Console.Write(_name+"开始工作:");
- }
- }
- class Manager:Employee{
- public Manager(string name):base(name){}
- public overridevoid StartWork(){
- base.StartWork();
- Console.WriteLine("给员工下达任务");
- }
- }
- class Secretary:Employee{
- public Secretary(string name):base(name){}
- public overridevoid StartWork(){
- base.StartWork();
- Console.WriteLine("协助经理");
- }
- }
- class Seller:Employee{
- public Seller(string name):base(name){}
- public overridevoid StartWork(){
- base.StartWork();
- Console.WriteLine("销售产品");
- }
- }
- class Accountant:Employee{
- public Accountant(string name):base(name){}
- public overridevoid StartWork(){
- base.StartWork();
- Console.WriteLine("管理公司财务");
- }
- }
- class Test{
- static void Main(){
- Employee[] emp = new Employee[5];
- emp[0] = new Manager("张三");
- emp[1] = new Secretary("李四");
- emp[2] = new Seller("王五");
- emp[3] = new Seller("马六");
- emp[4] = new Accountant("钱七");
- Console.WriteLine("上班时间开始工作");
- foreach(Employee e in emp){
- e.StartWork();
- }
- }
- }
抽象类(abstract)
abstract修饰衔可以和类、方法、属性、索引器及事件一起使用。在类声明中使用abstract修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象派生的类来实现。
抽象类的特性
抽象类不能实例化。
抽象类可以包含抽象方法和抽象访问器。
不能用sealed修饰符修改抽象类,这意味着抽象类不能被继承。
从抽象类派生的非抽象类必须包括继承的所有抽象方法的抽象访问器的实现
抽象方法是隐式的虚方法。
只允许在抽象类中使用抽象方法声明。
因为抽象方法声明不提供实际的实现,所以没有方法体;方法声明只是以一个号结束,并且在签名后没有大括号({}),实现由一个重写方法提供,此重写方法是非抽象类的成员。
在抽象方法声明中使用static或virtual修饰符是错误的。
除了在声明和调用语法上不同外,抽象属性的行为与抽象方法一样。
在静态属性上使用abstract修饰符是错误的。
在派生类,通过包括使用override修饰符的属性声明,可以重写抽象的继承属性。
实例:
- using System;
- abstract class A{
- protected int _x;
- public abstractvoid F();
- public abstractint X{
- get;
- set;
- }
- }
- class B:A{
- public overridevoid F(){
- }
- public overrideint X{
- get{
- return _x;
- }
- set{
- _x = value;
- }
- }
- }
- class Test{
- static void Main(){
- A a = new B();
- a.X=10;
- Console.WriteLine(a.X);
- B b = new B();
- b.X = 11;
- Console.WriteLine(b.X);
- }
- }
任何情况下,抽象类都不应进行实例化,因此,正确定义其构造函数就非常重要。确保抽象类功能的正确性和扩展性也很重要。下列准则有助于确保抽象类能够正确地设计并在实现后可以预期方式工作。
不要在抽象类型中定义公共的或受保护的内部构造函数,具有public或protected internal可见性的构造函数用于能进行实例化的类型。任何情况下抽象类型都不能实例化。
应在抽象类中定义一个受保护(protected)构造函数或内部(private)构造函数。
如果在抽象类中定义一个受保护构造函数,则在创建派生类的实现时,基类可执行初始化任务。
实例:
- using System;
- abstract class Employee{
- protected string _name;
- protected Employee(){}
- protected Employee(string name){
- _name = name;
- }
- public abstractvoid StartWork();
- }
- class Manager:Employee{
- public Manager(string name):base(name){}
- public overridevoid StartWork(){
- Console.WriteLine(_name+"给员工下达任务");
- }
- }
- class Secretary:Employee{
- public Secretary(string name):base(name){}
- public overridevoid StartWork(){
- Console.WriteLine(_name+"协助经理");
- }
- }
- class Seller:Employee{
- public Seller(string name):base(name){}
- public overridevoid StartWork(){
- Console.WriteLine(_name+"销售产品");
- }
- }
- class Accountant:Employee{
- public Accountant(string name):base(name){}
- public overridevoid StartWork(){
- Console.WriteLine(_name+"管理公司财务");
- }
- }
- class Test{
- static void Main(){
- Employee[] emp = new Employee[5];
- emp[0] = new Manager("张三");
- emp[1] = new Secretary("李四");
- emp[2] = new Seller("王五");
- emp[3] = new Seller("马六");
- emp[4] = new Accountant("钱七");
- Console.WriteLine("上班时间开始工作");
- foreach(Employee e in emp){
- e.StartWork();
- }
- }
- }
抽象方法和虚方法的区别在于:虚拟方法有一个实现部分,并为派生类提供了覆盖该方法的选项,相反,抽象方法没有提供实现部分,强制派生类覆盖方法(否则派生类不能具体类)
abstract方法只能在抽象类中声明,虚方法则不是。
abstract方法必须在派生类中重写,而virtual则不必。
abstract方法不能声明方法实体,虚方法则可以。
包含abstract方法的类不能被实例化,而包含virtual的类则可以。
接口(interface)
接口(iterface)定义了一个可由和结构实现的协定。接口可以包含方法、属性一、事件和索引器。接口不提供它所定义的成员实现——它仅指定实现该接口类或结构必须提供的成员。
一个接口声名可以声名零个或多个成员。
接口的成员必须是方法、属性、事件或索引器。
接口不能包含常量、字段、运算符、实例构造函数、析构函数或类型,也不能包含任何种类的静态成员。
所有接口成员都隐式地具有public访问属性一。
接口成员声明中包含任何修饰都属于编译时错误。具体来说,不能使用修饰符abstract、public、protected、internal、private、virtual、override或static来声明接口成员。
完全限定接口成员名
接口成员有时也用它的完全限定名(fully qualified name)来引用。接口成员的完全限定名是这样组成的:声明该成员的名称,后跟一个点,再后跟该成员的名称。成员的完全限定名将引用声明该成员的接口。
显示接口成员实现
为了实现接口,类或结构可以声明显示接口成员实现(explicit interface member implementation)。
显示接口成员实现就是一种方法、属性、事件或索引器声明,它使用完全限定接口成员名称作为标识符。
显示接口成员实现有两个主要用途
由于显示接口成员实现不能通过类或结构实例来访问,因此它们就不属于类或结构的自身的公共接口。当需在一个公用的类或结构中实现一些仅供内部使用(不允许外界访问)的接口时,这就是特别有用。
显示接口成员实现可以消除因同时含有多个相同签名的接口成员所引起的多义性。如果没有显示接口成员实现,一个类或结构就不可能为具有相同签名和返回类型的接口成员分别提供相应的实现,也不可能为具有相同签名和不同返回类型的所有接口成员中的任何一个提供实现。
显示接口成员实现
在方法调用、属性访问或索引器访问中,不能直接访问“显示接口成员实现”的成员,即使用它的完全限定名也不行。“显式接口成员实现”的成员只能通过接口实例访问,并且在通过接口实例访问时,只能用该接口成员的简单名称来引用。
显示接口成员实现中包含访问修饰符属于编译时错误,而且如果包含abstract、virtual、override或static修饰符也属于编译时错误。
显示接口成员实现具有与其他成员不同的可访问性特征。由于显示接口成员实现永远不能在方法调用或属性访问中通过它们的完全限定名来访问,因此,它们似乎是private(私有的)。因为它们可以通过接口实例来访问,所以它们似乎又是public(公共的)。
实例:
- using System;
- interface DrivingLicenceB{
- void GetLicence();
- }
- interface DrivingLicenceA:DrivingLicenceB{
- new void GetLicence();
- }
- class Teacher:DrivingLicenceA{
- void DrivingLicenceA.GetLicence(){
- Console.WriteLine("老师获得A驾驶执照");
- }
- void DrivingLicenceB.GetLicence(){
- Console.WriteLine("老师获得B驾驶执照");
- }
- public void GetLicence(){
- Console.WriteLine("这个不是接口内的方法");
- }
- }
- class Student:DrivingLicenceB{
- public void GetLicence(){
- Console.WriteLine("学生获得B驾驶执照");
- }
- }
- class Test{
- static void DriveCar(string name,DrivingLicenceB o){
- DrivingLicenceB dl = o as DrivingLicenceB;
- if(dl != null){
- Console.WriteLine(name+"汽车被开动了");
- }else{
- Console.WriteLine(name+"没有驾驶执照,不能开车");
- }
- }
- static void DriveBus(string name,DrivingLicenceB o){
- DrivingLicenceA dl = o as DrivingLicenceA;
- if(dl != null){
- Console.WriteLine(name+"卡车被开动了");
- }else{
- Console.WriteLine(name+"没有驾驶执照,不能开卡车");
- }
- }
- static void Main(){
- Teacher t = new Teacher();
- t.GetLicence();
- DriveCar("老师",t);
- DriveBus("老师",t);
- Student s = new Student();
- DriveCar("学生",s);
- DriveBus("学生",s);
- Console.WriteLine();
- ((DrivingLicenceB)t).GetLicence();
- ((DrivingLicenceA)t).GetLicence();
- }
- }
接口的多继承 接口重新实现
一个类若继承了某个接口的实现,则只要将该接口列入它的基类列表中,就可以重新实现(re-implemant)该接口。
接口的重新实现与接口的初始实现遵守循完全相同的接口映射规则。因此,继承的接口映射不会对为重新实现该接口而建立的接口映射产生任何影响。
实例:
- using System;
- interface IA{
- void F();
- }
- interface IB:IA{
- new void F();
- }
- interface IC:IA{
- void G();
- }
- interface IBC:IB,IC{
- }
- class Dervice:IBC{
- public void F(){
- Console.WriteLine("IB.F()");
- }
- public void G(){
- Console.WriteLine("IC.G()");
- }
- }
- class B:IA{
- void IA.F(){
- Console.WriteLine("B.F()");
- }
- }
- class C:B,IA{
- public void F(){
- Console.WriteLine("C.F()");
- }
- }
- class Test{
- static void Main(){
- Dervice d = new Dervice();
- d.F();
- ((IA)d).F();
- ((IB)d).F();
- ((IC)d).F();
- ((IBC)d).F();
- Console.WriteLine();
- B c = new C();
- ((IA)c).F();
- }
- }
接口与多态 接口与抽象类
1、类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类。接口只是行为的规范或规定。接口表述的是“我能做”(can-do),抽象类更多的是定义在系列紧密相关的类间(Is-a),而接口大多数是关系疏松但都实现某一个功能的类中
2、接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法;
3、一个类一次可以实现若干个接口,但是只能扩展一个父类
4、抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可娈的,而把可变的由子类去实现。
5、好的接口定义应该是具有有专一功能性的,而不是多功能的,否则造成污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其它方法,就叫接口污染。
6、如果抽象类实现接口,则可以把接口方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口方法。
实例:
- using System;
- interface IEmployee{
- void StartWork();
- }
- class Manager:IEmployee{
- private string _name;
- public Manager(string name){
- _name = name;
- }
- public void StartWork(){
- Console.WriteLine(_name+"给员工下达任务");
- }
- }
- class Secretary:IEmployee{
- private string _name;
- public Secretary(string name){
- _name = name;
- }
- public void StartWork(){
- Console.WriteLine(_name+"协助经理");
- }
- }
- class Seller:IEmployee{
- private string _name;
- public Seller(string name){
- _name = name;
- }
- public void StartWork(){
- Console.WriteLine(_name+"销售产品");
- }
- }
- class Accountant:IEmployee{
- private string _name;
- public Accountant(string name){
- _name = name;
- }
- public void StartWork(){
- Console.WriteLine(_name+"管理公司财务");
- }
- }
- class Test{
- static void Main(){
- IEmployee[] emp = new IEmployee[5];
- emp[0] = new Manager("张三");
- emp[1] = new Secretary("李四");
- emp[2] = new Seller("王五");
- emp[3] = new Seller("马六");
- emp[4] = new Accountant("钱七");
- Console.WriteLine("上班时间开始工作");
- foreach(IEmployee e in emp){
- e.StartWork();
- }
- }
- }
接口与抽象类
1、抽象类用于部分部分实现一个类,再由用户按需求对其进行不同的扩展和完善;接口只是定义一个行为的规范或规定。
2、抽象类在组件的所有实现间提供通用的已实现功能;接口创建在大范围全异对象间使用的功能。
3、抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。
4、抽象类主要用于设计大的功能单元;而接口用于设计小而简练的功能块。
接口应用
面向接口编程
设计模式
用接口实现排序
实现IComparable接口时,我们应该使用显式接口实现,同时为接口中的方法提供一个公有的强类重载版本。强类型的重版本在提高代码性能的同时,能减少误本。强类型的重载版本在提高代码性能的同时,能减少误用CompareTo()方法的可能。
实例:
- using System;
- using System.Collections;
- class Student:IComparable{
- private string _name;
- private int _age;
- public Student(string name,int age){
- _name = name;
- _age = age;
- }
- public string Name{
- get{return _name;}
- set{_name = value;}
- }
- public int Age{
- get{return _age;}
- set{_age = value;}
- }
- int IComparable.CompareTo(object right){
- if(!(right is Student)){
- throw new ArgumentException("参数必须为Strudent类型");
- }
- return _name.CompareTo(((Student)right)._name);
- }
- public int CompareTo(Student right){
- return _name.CompareTo(right._name);
- }
- private static AgeComparer _ageCom =null;
- public static IComparer AgeCom{
- get{
- if(_ageCom==null){
- _ageCom=new AgeComparer();
- }
- return _ageCom;
- }
- }
- private class AgeComparer:IComparer{
- int IComparer.Compare(object left,object right){
- if(!(left is Student) || !(rightis Student)){
- throw new ArgumentException("参数必须为Strudent类型");
- }
- return ((Student)left)._age.CompareTo(((Student)right)._age);
- }
- }
- public overridestring ToString()
- {
- return _name;
- }
- }
- class Test{
- static void Main(){
- int[] arr = {5,3,1,1,2,4};
- Array.Sort(arr);
- foreach(int iin arr){
- Console.WriteLine(i);
- }
- Console.WriteLine("对姓名进行排序");
- Student[] st = new Student[5];
- st[0] = new Student("张三",5);
- st[1] = new Student("李四",3);
- st[2] = new Student("王五",2);
- st[3] = new Student("马六",4);
- st[4] = new Student("钱七",1);
- Array.Sort(st);
- foreach(Student j in st){
- Console.WriteLine(j +" "+j.Age);
- }
- Console.WriteLine("对年龄进行排序");
- Array.Sort(st,Student.AgeCom);
- foreach(Student k in st){
- Console.WriteLine(k +" "+k.Age);
- }
- }
- }
类型(Type)
C# 中有两种类型:
值类型(value type)
引用类型(reference type)
值类型的变量直接包含它们的数据,而引用类型的变量存储对它们的数据的引用,后者称为对象。
C#的值类型进一步划分为简单类型(simple type)、枚举类型(enum type)和结构类型(struct type).
C#的引用类型进下一步划分为类类型(class type)、接口类型(interface type)、数组类型(array type)和委托类型(delegate type).
当类型是一个十分简单的类型,其中没有成员会修改类型的任何实例字段。
类型不需要从其他任何类型继承。
类型不会派生出其他任何类型。
类型的实例较小。
类型不作为方法参数传递,也不作为方法的返回类型使用。
实例:
- using System;
- class MyClass{ //struct
- public int i;
- }
- class Test{
- static void Seti(MyClass m){
- m.i=300;
- }
- static void Main(){
- MyClass mc = new MyClass();
- mc.i=100;
- MyClass mc1 = mc;
- //mc1.i=200;
- Seti(mc1);
- Console.WriteLine("mc.i="+mc.i);
- Console.WriteLine("mc1.i="+mc1.i);
- }
- }
using System;
class MyClass{ //struct
public int i;
}
class Test{
static void Seti(MyClass m){
m.i=300;
}
static void Main(){
MyClass mc = new MyClass();
mc.i=100;
MyClass mc1 = mc;
//mc1.i=200;
Seti(mc1);
Console.WriteLine("mc.i="+mc.i);
Console.WriteLine("mc1.i="+mc1.i);
}
}
装箱与拆箱
装箱(boxing)和拆箱(unboxing)的概念是C#的类型系统的核心。它在value-type和reference-type之间的架起了一座桥梁,使得任何value-type的值都可以转换为object类型的值,反过来转换也可以。装箱和拆箱使我们能够统一地来考察类型系统,其中任何类型的值最终都可以按对象处理。
装箱操作
1、从托管堆中分配好内存。分配的内存量是值类型的各个字段所需要的内存量加上同步和方法表指针所需要的内存量。
2、值类型的字段复制到新分配的堆内存。
3、返回对象的地址。现在,这个地址是对一个对象的引用,值类型现在是一个引用类型。
拆箱操作
1、检查装箱引用,如果为null则抛出NullReferenceException异常
2、根据显示类型转换过程中的声明,来检查装箱对象的类型,如果不是,则抛出InvalidCastException异常。
3、将该值从实例复制到值类型变量中。
实例:
- using System;
- using System.Collections;
- interface IName{
- string Name{
- set;get;
- }
- }
- struct Person : IName{ //结构体
- private string _name;
- public Person(string name){
- _name = name;
- }
- public string Name{
- get{ return _name; }
- set{ _name = value; }
- }
- }
- class Test{
- static void Main(){
- object o = 123; //装箱操作
- int i = (int)o;
- Int64 j = (Int64)(int)o;
- i = 456;
- Console.WriteLine(i+", "+(int)o);
- Person p = new Person("张三");
- p.GetType(); //装箱操作
- p.ToString(); //没有执行装箱--重写ToString
- ArrayList arr = new ArrayList();
- arr.Add(p); //装箱操作
- p.Name = "李四";
- Console.WriteLine(p.Name);
- Console.WriteLine(((Person)arr[0]).Name);
- Person p1 = (Person)arr[0];
- p1.Name = "李四";
- Console.WriteLine(p1.Name);
- Console.WriteLine(((Person)arr[0]).Name);
- Person p2 = new Person("李四");
- arr[0]=p2;
- Console.WriteLine(p2.Name);
- Console.WriteLine(((Person)arr[0]).Name);
- ((IName)arr[0]).Name="张三";
- Console.WriteLine("interface: "+((Person)arr[0]).Name); //接口方式装箱值,更简单方法是将struct修改class
- ((IName)p).Name ="李四"; //装箱操作
- Console.WriteLine("接口装箱:"+((IName)p).Name);
- Person[] arrs = new Person[1];
- arrs[0] = new Person("张三");
- Console.WriteLine(((Person)arrs[0]).Name);
- arrs[0].Name = "李四";
- Console.WriteLine(((Person)arrs[0]).Name);
- }
- }
泛型(Generic)
泛型是2.0版本C#语言和公共语言运行库(CLR)中的一个新功能。泛型将类型参数的概念引入.NET Framewrok,类型参数使得设计如下类和方法成为可能:这些类和方法将一个或多个类型的指定推迟到客户端代码声名并实例化该类或方法的时候。例如,通过使用泛型类型参数T,你可以编写其它客户端代码能够使用的单个类,而不至于引入运行时强制转换或装箱操作的成本或风险。
栈(Stack) 进栈:Push 出栈:Pop
实例:
- using System;
- class Stack<T>{
- private T[] items;
- private int count;
- public Stack(int size){
- items = new T[size];
- count = 0;
- }
- public void Push(T x){
- items[count++]=x;
- }
- public T Pop(){
- return items[--count];
- }
- }
- class Test{
- static void Main(){
- Stack<int> s= new Stack<int>(10);
- s.Push(111);
- s.Push(222);
- Console.WriteLine(s.Pop() +" : "+ s.Pop());
- }
- }
博客 http://cgbluesky.blog.163.com