C#面向对象
一、面相对象基本概念
类:创建对象的模具,抽象的(对象的抽象),是对具体对象的描述
当程序启动的时候,加载程序集到CLR中 ,调用main函数,在第一次在代码中遇到某一个类的时候加载类的时候加载类的静态成员和信息,目的是节约资源(有可能这个静态资源一直不被访问,所以只有用到的时候才加载)。
对象:具体的事物,按照类的描述,来创建我们具体的实例
字段:类的状态
方法:类执行的动作
类的继承:类之间可以有继承关系,父类 基类 子类
二、面向对象特征:
封装(隐藏代码的实现/复用/修改方便)、
方法
参数:ref(传递前就要赋值,方法外赋值,在方法内使用。侧重于修改)、out(在方法中必须赋值,侧重于输出)、params(修饰数组参数,在参数列表中只能有一个,并且必须是放在最后)
封装方法的原则,把不变的放在方法里,变化的放到参数中
类:引用类型
成员:字段(全局变量)、属性(_get _set的两个方法)、索引
继承
目的:
1.复用:子类继承父类的所有成员,但是只能访问非私有的成员
2.多态:LSP 父类obj =new 子类();
2.1如果子类和父类存在相同的方法
2.1.1参数列表不相同:构成子类中的方法重载,也就是子类中包含两个同名方法(参数列表不同)。
2.1.2参数列表相同:
2.1.2.1如果同名方法不存在重写关系(子类没有重写父类方法)
父类 obj=new 子类();obj.Ttest();//执行父类的方法
子类 obj=new 子类();obj.Ttest();//执行子类的方法(此时子类对象隐藏了父类的方法,也可用new来显示隐藏)
2.1.2.1如果同名方法村咋重写关系(前提:父类的方法必须是虚方法或抽象方法,子类通过override)
父类 obj=new 子类();obj.Ttest();//执行子类的方法
子类 obj=new 子类();obj.Ttest();//执行子类的方法
2.2多态的常用方式
2.2.1简单工厂方式,以返回值的方式来使用
父类 obj=Factory(“a”);工厂方法中,通过判断参数创建相应的子类对象,并返回父类对象使用
obj.Test();
好处父类不需要关心具体的实现(new),只要针对父类中能够访问到的成员来编写代码就可以了
2.2.2父类做参数使用
Public void Test(父类 o)
{
o.Method();//此方法中不关心传进来的对象具体的实现代码,只关心能调用什么方法。
}
子类 obj=new 子类();
Test(obj);
多态:同上
类的成员:字段、方法、属性、索引
三、成员访问级别
类成员(字段、方法、属性)都需要定义类的访问级别
Public(公共的):该类或成员可以在任何地方访问
Private:默认级别,只能由本类中的成员访问
Internal:程序集内共享
Protected:受保护的,只能在本类和子类中访问
*extern:供外部语言访问的
类的默认访问修饰符 Internal
四、属性
属性的目的:1、为了设置和修改访问成员变量值得规划,2、为了能够在持久化框架中,对应列的名字(持久化:将内存中的数据存入(硬盘)数据库中)。
属性的本质:两个方法Set方法 与Get方法
注意点:
1、属性访问器(get,set)上应用的访问修饰符,必须“小于”属性上应用的访问修饰符。
2、只能有一个属性访问器上的访问修饰符比属性上的访问修饰符小。
3、接口中的属性访问器不能指定访问修饰符。
4、属性访问器的保护级别规则,完全适用于索引器
方法
类创建对象时,在堆中是不创建方法的,方法是放在一个方法表中,然后用对象对方法表中的方法进行引用
五、对象的引用
Int 、datetime、bool、char 赋值是copy传递
String、class等 赋值是引用传递
六、构造函数
访问修饰符一般是public
没有返回类型
方法名和类名相同
在该类被new时(实例化)使用
在定义类时,若没有定义构造函数,则在CLR运行时会自动添加一个无参的构造函数
使用构造函数的目的:用来创建对象的函数,并且可以在构造函数对对象进行初始化,可以使得类中对传入的参数进行操作,起到面相对象的封装的作用
构造函数的重载:可以传不同的的参数
值类型变量,在内存中的存储
1、 当变量是类的成员变量的时候,那么该变量跟随类的对象存在于内存中,当对象的引用断开时,等待垃圾回收器进行清理时便被销毁。
2、 当变量是类中方法的变量时,该变量在方法被调用时存在于栈中,在方法执行完毕后,立即销毁。
this
代表当前实例对象
可以在类中调用类的成员
public Class1():this("泡菜")//this调用本类的构造函数 { } public Class1 (string eat) { this.吃 = eat; }
七、继承
Base 关键字调用继承类的父类的构造函数
public 中国人(string name,int height,string hk) :base(name,height)
{
this.户口 = hk;
}
子类对象的base关键字其实就是子类本身,只不过类型是父类的
相当于:BaseClass base=( BaseClass)this;
new子类时,先开辟空间,创建子类对象,调用子类构造函数时 创建父类对象,调用父类构造函数,然后返回来执行子类构造函数方法体代码。
子类构造函数默认调用父类无参的构造函数。
八、异常
使用try Catch的地方:网络操作、文件操作、数据库操作、除法操作、强制类型转换操作等
try
{
有可能出现异常的代码
}
Catch ( Exception ex)
{
Try中的代码出错时才执行
}
Ex.message 异常信息
Ex.Stacktrace 异常堆栈信息
Environment.NewLine 获取为此环境定义的换行字符串。
使用Environment.NewLine + E.Message使用比较好
throw new Exception("自定义异常信息");
九、常量const与static readonly
程序中不可改变的量,在程序中编译后,就存在于程序集中。
const 类型 常量名=常量值
static readonly 运行时常量
十、静态成员static
静态成员属于类,不用new,就可以使用的成员。
在程序启动时,把程序里的静态成员加载到静态成员表中
Static中不能直接调用非sataic的成员。静态成员属于类,非静态成员必须要在实例后才能使用
非静态方法里面可以调用静态成员。其他类的方法都可以调用,本类的方法也可以调用
静态成员变量的使用条件:整个程序内部共享的数据。
静态成员方法的使用条件:当方法在整个程序中处于工具方法的地位的时。
静态类
静态类中的成员都是静态的
静态类的构造函数,在第一次调用静态成员时初始化(只初始化一次),不带参数
一般用来实现纯函数库, 不可以实例化
尽量少的使用静态类、静态成员,只会在程序退出的时候释放内存空间
静态类不能继承其他类,只能继承Object类。
静态类的本质:abstract+sealed
十一、命名空间
1、命名空间相当于C#中虚拟的文件夹,导入命名空间即为导入文件夹,不是导入类
2、using 导入命名空间
3、作用:组织类接口资源,防止命名冲突
4、命名空间别名限定符(::)
using SC = System.Collections;
using OC = Our.Collections;
Class Program{
Static void Main(){
OC::ArrayList list = new OC::ArrayList();
}
}
11、索引
数组的下标访问
抽象类
的后向子类继承抽象父类就是继承父类的规则,子类本身可以添加新的规则
不能被new
不能被new的类: 抽象类、static、私有构造函数的类
抽象类既想约束子类的行为(通过抽象成员来实现),也想传递一些父类的行为特性给子类(通过非抽象成员来实现)
抽象类的特点:
//1.需要用abstract关键字标记
//2.抽象方法不能有任何方法实现。
//3.抽象成员必须包含在抽象类中。
//4.由于抽象成员没有任何实现,所以子类必须将抽象成员重写。
//5.抽象类不能实例化,既然不能实例化,
//抽象类的作用:抽象类的作用就是为了让子类继承。
//6.抽象类中可以包括抽象成员,可以包括有具体代码的成员。
//7. 还有抽象方法不能用static修饰
public abstract void ShowNationality();
做网站的公司(抽象类) 你给我活我去开发网站,但是网站需要一部分flash,我公司里都是程序员(抽象类中有实现的方法)。没人会做flash(抽象方法),于是我把做flash这部分工作给其它公司去做(重写抽象方法的类)
抽象类定义的是公共的实现和能力
抽象类不能被实例化
抽象类为子类提供所需要的成员
抽象类中的成员既可以有实现也可以无实现
抽象类必须由其子类实现它的抽象成员(除非子类也是抽象类)
一个类只能继承一个抽象类(类的单根继承性)
抽象方法(成员)不能有实现,必须被子类重写override(除非子类也是抽象类)
抽象方法(成员)的只能出现在抽象类中。
多态性的含义:使得能够利用基类的指针来引用不同子类的对象,以及根据所引用对象的不同,以不同的方式执行相同的操作。
多态的作用:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。(多态)
十三、方法
抽象方法
必须放在抽象类中,子类继承时一般必须有实现
抽象方法被子类重写时,父类调用时会调用子类的方法
没有实现部分
虚方法
必须有实现部分
抽象是为了定义规则,不是具体的实现,(为了约束子类的行为)具体的实现交给子类完成
多态是指不同对象收到相同消息时,会产生不同行为
同一类在不同场合下表现出不同行为特征
面向对象五大原则
里氏替换原则(对修改封闭,对扩展开放)、开放封闭原则、接口单一原则
is a :用来检验继承关系是否合理。(can do接口)
as : 类型转换,只能转换引用类型
new: 子类方法中的new关键字,显式隐藏父类中的同名方法
十四、接口
默认的成员访问修饰符为public, 在定义成员时不能添加pubic修饰符
接口和抽象类同时被继承时,抽象类写在接口的前面,中间用逗号分隔
接口的成员包括:方法、属性、索引和事件的声明
显式接口只能被接口变量调用
抽象类和接口的区别
接口定义类的公共行为,抽象类定义类的公共实现
一个类只能继承自一个类(抽象类),但是可以同时实现多个接口
接口中不能有实现,抽象类中可以有未实现成员也可以有实现的成员
接口中未实现的方法必须在子类中实现,抽象类中未实现的成员必须在子类中重写
好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染。
什么时候使用抽象类,什么时候使用接口
抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。
接口使用建议
– 接口→抽象类→父类→具体类
– 能使用接口就不用抽象类,能使用抽象类就不用类,能用父类就不用子类。面向接口编程(不关心具体实现)。
– 避免定义“体积庞大的接口”、“多功能接口”,会造成“接口污染”。只把相关联的一组成员定义到一个接口中(尽量在接口中少定义成员)。单一职责原则
– 定义多个职责单一的接口(小接口)(组合使用)。(印刷术与活字印刷术)
- 随着学习的深入再继续体会接口的作用。
十五、结构
存在的意义:允许用户自定义一个值类型,用来组织业务相关的少量的数据
结构中只有const、static修饰的变量才可以在结构中初始化值
类型都是派生自ValueType
在使用new关键字创建对象后,所有的成员变量都已经存在,并有默认值(值类型)。
如果没有使用new关键字,则需要写代码为所有的成员变量赋值,之后才能调用结构的方法属性
值类型(Struct)和引用类型(类)的区别
值类型(Struct):主要用来封装一组数据,并提供一些简单的处理数据的方法
引用类型(类):主要用来封装数据和行为(对对象的封装)
当需要使用面向对象的特性时,或者是需要封装很多的变量时使用类,否则使用结构(因为栈空间比较小)
访问级别约束
1.子类的访问级别不能比父类的高。(会暴露父类的成员)
2.方法参数的访问级别必须>=方法的访问级别
十六、类库
- 字符串(strings)
String一旦赋值或实例化后就不可更改,如果赋予新值将会重新开辟内存地址进行存储
属性
Length
方法
IsNullOrEmpty(); 静态方法 判断是否为null或“”
IndexOf()用来查找某个字符或字符串,返回要查找的字符或字符串的下标
LastIndexOf
Substring()截取字符串 (正则表达式也可实现,但是消耗资源比较大)
Split()把字符串分隔成字符数组
Join()把字符数组拼接成字符串
+ 字符串连接
Format() 格式化字符串(1.可以把值类型的数据格式化成人民币,2.可以格式化sql语句防止注入漏洞攻击)
Replace() 字符串替换
字符串比较
==比较时
1.如果比较的是值则比较两个对象的值
2.如果比较的是引用类型,则比较两个对象的引用地址是否相同
Equals比较时
1.此方法是Object类里的虚方法,默认用==进行比较。
2.但是大部分微软的类和用户自定义的类都重写了该虚方法,也就是微软和用户自己定义了是否相等的比较规则。
class Program { static void Main(string[] args) { Person p1 = new Person("小?明¡Â", 15); Person p2 = new Person("小?明¡Â", 15); Console.WriteLine("p1=p2是º?:êo{0}", p1 == p2); Console.WriteLine("p1.Equals(p2)是º?:êo{0}", p1.Equals(p2)); Console.ReadKey(); } } public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string name, int id) { this.Name = name; this.Age = Age; } public override bool Equals(object obj) { Person p=obj as Person; return (this.Name == p.Name) && (this.Age == p.Age); } }
字符串的恒定性:当字符串在内存中已经被创建后,程序员再次创建相同值的字符串对象时,CLR做了优化,直接把第一个字符串的引用赋给了第二个变量。也就是说前后两个字符串变量保存了相同的字符串对象的引用。
如果代码里是直接将两个字符串相加,那么CLR编译IL的时候,就会直接将相加拼接后的新字符串作为变量的值。
如果代码里是将两个字符串变量相加的话,那么CLR会在编译IL时,调用string.Concat(string,string)方法来拼接两个字符串变量的值,最终返回一个新的字符串变量。
System.String
.NetFrameWork 中的类和C#中的string对应
StringBuilder 用于大量拼接字符串(开销比较小)
Append()
Insert()
Remove()
Replace()
转义字符
\r\n 换行
\t 制表符
@ 字符转声明可以写多行
日期时间(Datetime)s
当前年份加一:ssDateTime.Today.AddYears(1)
集合
ArrayList
长度可变
其实里面真正存储数据的也就是一个object数组(0个长度),在第一次添加数据时object被初始化成4个长度的数组对象,当Add()到容量已满是,进行扩容(大小是:数组长度*2)。
属性:Capacity 集合的容量
Count 实际元素个数
方法
Add()
Range()添加数组
Remove() 根据引用删除(查找要删除对象时的比较方法是Object里的Equals,比较方式是比较的地址)
RemoveAt()
Clear()
Contains() //使用Object里的Equals进行比较
ToArray() 转换为数组
HashTable
Hashtable中的数据存在于一个结构类型的bucket(key,value,hashcode)数组中,当add()的时候,会创建一个bucket对象,设置键值,并且把键值得hachcode也保存起来
方法
Add
GetEnumerator() 返回一个IDictionaryEnumerator类型的可遍历的对象
Hashtable ht = new Hashtable(); ht.Add("上?", "心学"); ht.Add("中", "艺术"); ht.Add("下", "技术"); IDictionaryEnumerator ide = ht.GetEnumerator(); while (ide.MoveNext()) { Console.WriteLine("ide.Key=" + ide.Key + "ide.Value" + ide.Value); } Console.ReadKey();
当我们向hashtable中add元素时,会自动计算一个hash_code存在这个元素后面
Hashtable里的键是不可以重复的:
1.Hashtable的下标是根据key的hash值计算出来的当我们向hashtable中add元素时,元素存储在HashTable的数组里的下标是根据添加Key的hash值算出来的(但是因为hash值取模数组长度,所以肯定不会超过当前数组长度)。
2.注意每个对象算出的hashcode并不是唯一的,有可能多个对象的hashcode是相同的,解决方法是:1.再次hash一次、2.桶装模式,将两个相同hashcode的对象放在array中装入同一个位置。
3.当新增时,如果hashtable里的元素已经满了,则以数组的两倍长度扩容。
4.当我们从hashtable里取元素时,会根据key的值计算出要取的元素的下标,并且比较元素里的key和当前要找的key参数的hash值是否相等,同时还要比较两个key的引用是否一致,如果都满足则确定找到要取的元素。
泛型
通过参数化类型来实现在同一份代码上操作多种数据类型。它是利用对C#类型的一种抽象化,在C#中做的并不是很好,我们一般用于实现多参数。
C#泛型赋予了代码更强的类型安全(object转类型的时候有可能会导致问题),更好的复用(一份代码可以包含多个类型),更高的效率(装箱拆箱),更清晰的约束
C#泛型能力由CLR在运行时支持,是在程序运行的时候
泛型的实例化工作以“on-demand”的时候,放在JIt编译时
C#泛型编译机制
第一轮编译时,编译器只为泛型类产生“泛型版”的IL与元数据,并不进行实例化,T在中间只作为一个占位符。
JIT第一次编译遇到泛型类时,将用int替换“泛型版”的IL与元数据中的T——进行泛型类型的实例化。
CLR为所有类型参数为引用类型的泛型类型产生同一份代码,为不同的值类型产生独立的代码。string 和StringBuider 本身都是引用类型占位都是一样的可以共享同一份代码,实际上最后都引到object上,int和byte所占的字节不同,不能转换为object,只能用两份代码
泛型类型
泛型支持类、结构、接口、委托、方法
C#除了可单独声明泛型类型(包括类与结构)外,也可在基类中包含泛型类型的声明。但基类如果是泛型类,她的类型参数要么已经实例化,要么来源于子类(同样是泛型类型)声明的类型参数
泛型方法
泛型方法可包含在泛型类中,也可不包含在泛型类中
泛型方法可重载、重写
泛型方法重写是不能添加额外的约束
泛型约束
C#泛型要求对“对所有泛型类型或泛型方法的类型参数”进行约束,都要进行“显式的约束”(如果没有指定显式的约束,则泛型类型参数将只能访问System.Object类型中的共有方法),
“显式约束”包括:“基类约束”、“接口约束”、“构造器约束”、“值类型/引用类型约束”。由于C#是在基于运行时的,所以如果不是类型安全的,在JIT编译时,就会遇到一系列的错误。
泛型集合:List<>带类型约束的集合
方法
Reverse()集合反转
字符编码
ASCII 美国的标准编码
GB2312简体汉字
BIG5 繁体编码
UT-8 web网站用,汉字占两个字节,英文字母占一个字节
Unicode
Partial 局部类型
CLR会把此修饰符修饰的多个类型(类、结构、或者接口)文件,编译为一个类
窗体中的控件都是窗体类的成员变量
IO
Path
ChangeExtension();//改变字符串路径后缀
Combine(); //拼接字符串,获得文件路径
GetDirectoryName();//获得文件所在的文件夹路径
GetExtension();//获得文件扩展名
GetFileName();//获得文件名
GetFielNameWidthOutExtension();//获得文件路径的文件名,不带文件后缀
GetFullPath();//获得(当前程序集的)相对路径的绝对路径
GetTempFileName();//得到一个临时文件
GetTempPath();//得到操作系统临时文件夹的路径
GetFileName();//返回指定路径字符串的文件名和扩展名
Directory
Move();//移动文件或文件夹
CreateDirectory();//新建文件夹
GetFiles();//搜索文件夹中的文件
Assembly.GetExecutingAssembly();//获取包含当前执行的代码的程序集。
File
进行文件读写是一次性读写,如果文件非常大,会比较占内存、慢
AppendAllText();追加
WriteAllLines();写入数组
WriteAllText();写入字符串
ReadAllText();读取字符串
ReadAllLines();读取数组
Copy();
Exist();
Move();
Delete();
Create();
FileStream 可操作任意类型的文件数据
文件流进行文件读写,不会将文件一次性读写到内存中,读写时,有一个偏移量,会根据偏移量的位置读写文件
Write();write后只是把文件写入到字节数组中,只有在FileStream Close的时候,文件才会写到硬盘中
Close(); close方法中执行Dispose方法
Postion;标示每次写文件的位置
StreamReader 只能用来操作字符串
StreamWriter
所有要使用using释放资源的类,都必须实现IDispose接口
using(){}的本质是调用了try{} finaly{}
序列化Serializable
二进制序列化
将对象(比如Person对象)转换为二进制数据;通俗的说就是将对象里的字段及值以“文本”的方式保存成文件。
反序列化:将二进制数据还原为对象;通俗的说就是先创建一个相同的对象,然后将“文本”里保存的字段的值,设置到对应的字段值中。
序列化对象的作用:对象是稍纵即逝的,不仅程序重启、操作系统重启会造成对象的消失,就是退出函数范围等都可能造成对象的消失,序列化/反序列化就是为了保持对象的持久化。就像用DV录像(序列化)和用播放器播放(反序列化)一样。
只有加了[Serializable]特性的类才能被序列化
BinaryFormatter
Serialize(); //序列化到文件流
Deserialize();//反序列化过程:读取文件中的类型和字段信息,通过反射创建类对象,以object方式返回,并设置字段的值。
XML序列化
http://blog.csdn.net/violet_day/article/details/10012099
System.MarshalByRefObject 调用外部程序时需要继承的类,一般用于做驱动开发时使用
委托(Delegate)
委托的目的有两个:
一、 能够将方法作为参数传递
- 作为方法的参数的方法的签名必须与方法的签名一致
- 委托里包含的方法访问修饰符与委托本身的访问权限无关(MulticastDeletage类的访问修饰符为private)
- 凡是将方法直接赋给委托对象的地方,在编译时,都会帮我们动态生成对应的new委托对象(方法)。
//这种用法叫做语法糖
//DelegateFunction dg = new DelegateFunction(Function1);//实例委托
//DelegateFunction dg1 = Function1;//不用new关键字实例委托
二、(多播委托)调用一个委托,执行N个方法。
自定义委托相当于创建一个继承自System.MulticastDelegage的sealed 类,继承自Delegate(在Delegate类中有一个IntPtr类型的变量用来存方法的指针,IL中可以看到),委托帮我们封装了一个函数指针,供程序员安全调用
匿名方法
并不是没有名字的方法,它在CLR编译时会产生一个临时方法。
匿名方法产生后,方法的那个指针会存放在委托变量中,供程序调用
对对象进行排序可以有两种方法来实现
1.继承IComparer接口。
2.调用list.sort(委托Comparison)方法。
多播委托:
1.可以向委托上注册多个方法
2.也可以从委托上移除已注册的方法
3.如果委托上注册了多个有返回值的方法,那么调用后委托返回的是最后一个方法的返回值
事件
用于封装(限制)委托对象的操作,对外只提供+=和-=操作
空属类型
允许一个值类型具有“空值”意义
int?
空属类型实际上是一个泛型类型:System.Nullable<T>
DBNULL.Value;//判断DataRow是否为空
AppDomain应用程序域
http://www.cnblogs.com/foman/archive/2009/10/18/1585655.html
MSDN: http://msdn.microsoft.com/zh-cn/library/system.appdomain.aspx
反射
Assenbly
http://www.cnblogs.com/muou/archive/2009/07/08/1518971.html
http://blog.csdn.net/lyncai/article/details/8621880
Thread 线程
冒泡排序