C#是一种语言,.net是一个平台。
C#不但可以开发基于.net的应用程序,也可以开发基于WinForm的程序。
. Net 是 Microsoft 的 XML Web 服务平台,XML Web 服务能使应用程序在 Internet 上传输和共享数据。
特点
- 强类型语言,安全稳定;
- 事件驱动、完全面向对象的可视化编程语言;
- 便捷的面向组件编程的支持;
- VB简单的可视化操作和C++的高运行效率的结合;
- 托管代码和垃圾回收 (garbage collection);
// .NET ~ C# ~ ASP.NET
C#是.net平台下的一种开发语言,用于开发桌面应用程序
asp.net是开发web程序的技术
.net是平台,该平台下包含很多技术如:asp.net ado.net winform WCF
.NET框架
CLR:公共语言运行库,Common Language Runtime,.NET框架的核心组件,在操作系统的顶层并在运行期调用基类库(BCL, Base Class Library)管理程序的执行。
- 内存管理机制
- 垃圾回收机制GC (Garbage Collector)
- JIT编译器
CTS:公共类型系统,Common Type System,重要特性是所有类型都继承自公共的基类object。
基础学习
4个基础命名空间
// .NET框架中的基础类库,用于实现一些基本的类。
using System; .NET应用程序中使用的大多数基本类型
using System.Collections.Generic; 处理集合的泛型类型
using Syatem.Text; 字符串处理和编码相关的类型
using System.Linq; 提供支持使用语言集成查询(LINQ)进行查询的类和接口,用于对集合进行查询
0. 结构 - 类
struct 是值类型,隐式密封的、不能被继承,但可以实现接口。struct 成员默认是 public,有构造函数(实例、静态),没有析构函数,不允许字段初始化。
class 是引用类型,单继承,可实现接口。class 成员默认是 private。
- 数据成员:字段、常量;
- 函数成员:属性、方法、事件、索引器、构造函数、析构函数、操作符;
1. 字段 - 属性 - 索引
字段 - 通常private,属性 - public;
属性 是指定的一组2个匹配的、称为访问器 (get 和 set) 的方法。属性是函数成员,访问器只能被隐式调用,执行代码,但不为数据存储分配内存。公有属性提供对私有字段的受控访问。
索引 是一组 get 和 set 访问器,类似属性,索引是函数成员;索引通常用于访问多个数据成员,类似数组利用索引运算符;索引不能声明为 static。访问器只能被隐式调用,可以重载,参数列表必须不同。
- 索引没有名称,但 this 是必须的;
- 参数列表中至少必须声明一个参数;
ReturnType this[参数列表] {
get {...}
set {...}
}
2. 静态构造函数 - (普通的)实例构造函数
实例构造函数初始化类的每个新实例,static 构造函数初始化类层次的项目。static 构造函数不能访问类的实例成员,通常用于初始化类的静态字段,静态构造函数被系统自动调用。静态字段先于实例成员被初始化,类只能有一个 static 构造函数,不能带参数、不能有访问修饰符、也不能使用 this 访问器。
[1]. 构造函数中不能调用虚方法
关于两者的具体信息参见:http://www.cnblogs.com/jiagoushi/p/3775046.html
3. 继承
单继承。
a. 重载:同一个类内的函数,函数名相同、参数列表不同;
b. 重写(覆盖):父子类之间的函数,签名相同、返回类型相同;父类用 virtual 标识,子类用 override 标识;
c. 隐藏:默认或通过 new 显式隐藏。base.数据成员/函数名 显式访问被隐藏的成员。
- 字段:名称相同,类型相同;
- 函数:签名相同(函数名、参数列表(个数、顺序、类型、修饰符));
4. 抽象类 abstract - 接口 interface (抽象类 - 自底向上,接口 - 自顶向下)
- 抽象类可以给出某些成员的一些实现,接口不能包含成员实现;
- 抽象类的抽象成员可以被子类部分实现,接口的成员必须被实现类全部实现;
- 一个类只能继承一个抽象类(类单继承),但是可以实现多个接口;
- 类是对对象的抽象,抽象类是对类的抽象,接口是对行为的抽象;
- 从设计角度,抽象类和接口设计的思维过程不同(相反):
接口
引用类型,接口可以继承接口,类和结构可以实现接口。
接口允许访问修饰符 public、protected、internal、private,接口成员不允许访问修饰符,默认 public static。
接口声明不能包含数据成员,只能包含 属性、方法、事件、索引。
类 A 实现接口 IA,将类 A 的对象引用转换为接口 IA 的引用:
- 强制类型转换:IA ia = (IA)objA;但是若类 A 未实现接口 IA,则抛出异常。
- as 运算符:IA ia = objA as IA;若类 A 未实现接口 IA,返回 null、不抛出异常。
实现接口的类可以从它的基类继承实现代码。类实现 2 个接口,2 个接口包含同名方法,类的单一实现就可以满足 2 个接口 或 显式实现每一个接口。同时可以分别获得每一个接口的独立引用。
- 接口正常实现:接口方法不包含实现代码,实现在类级别的方法中;
- 接口显式实现:接口方法包含实现代码,没有类级别的方法;
注:接口的显式实现成员只能被相应的接口引用访问,需要强制转换操作。
5. 密封类 - 抽象类 - 静态类
- a. 密封类:sealed,只能被用作独立的类,不能被继承,可实例化;
- b. 抽象类:abstract,只能被继承,不可实例化;
- c. 静态类:static,静态类是密封的。不能被继承,不可实例化;
6. 装箱 - 拆箱
- a. 装箱:隐式转换,把值类型打包到Object引用类型的一个实例中;
- b. 拆箱:显式转换,从对象中提取值类型;
implicit、explicit 和 is、as
[1]. implicit - 隐式转换,explicit - 显式转换;
public static implicit/explicit operator 目标类型(源类型 源类型变量)
注:用户自定义转换仅针对于类和结构。is-as 不能用于用户自定义转换。is-as 不能重载。
[2]. is:检查对象类型兼容性并判断能否成功转换,返回bool,不抛出异常;
- 适应范围:引用转换、装箱转换、拆箱转换,if(obj is Type) Type t = (Type)obj;
[3]. as:类似强制转换,检查对象类型兼容性并返回转换结果,不抛出异常,失败时返回null;
- 适应范围:引用转换、装箱转换,Type t = obj as Type; if(null !=t){…}
- true/成功:obj 是 Type 类型或者 obj 是 Type 类型的子类型;
对于 is,CLR 对对象类型检查了两次:is操作首先检查obj是否和Type类型兼容。若兼容,在if语句内执行转换时CLR再检查obj是否为一个Type引用并转换。对于 as,CLR 对对象类型检查了一次:as操作检查兼容性并直接转换,效率高、性能好。
参考:is - as 详解;
7. object sender - EventArgs e
- a. object sender:保存触发事件的对象的引用,指向发送通知的对象;
- b. EventArgs e:EventArgs 是包含事件数据的类的基类,在事件触发时传递数据,但是 EventArgs 不包含任何数据,所有的事件参数类都必须从 EventArgs 类派生;
8. 变体
变体分为 协变 - 抗变 两种,针对引用类型:
- a. 协变:covariance,父=子,out,只能用作方法的返回值或属性get的访问器;
- b. 抗变:contravariance,子=父,in,只能用作方法的参数;
9. 可空类型修饰符 ? - 空接合运算符 ??
a. 可空类型允许创建普通值类型的变量并标注其有效性。可空类型是对普通值类型的 private 封装,与普通值类型可以相互转换。 Type ? nullableType; 其问号语法是通过 System.Nullable<T> 利用泛型实现,? 是System.Nullable<T>的缩写。
针对值类型,不能创建引用类型的可空类型。2个只读属性:
-HasValue:bool 类型,标识是否有效;
-Value:变量值;(普通值类型的值、可空类型的值、null)
b. 定义可空类型和引用类型的默认值,允许在可空类型的变量为 null 时返回一个给定值。单元运算符,左右两边数据类型必须相同或能隐形转换,右结合运算。首先检测左边的值,若为Null,则整个表达式取值为右侧的值,否则为左侧的值。
- string str = null; Console.Write(str ?? "abc"); 将输出:"abc"
- string str = "s"; Console.Write(str ?? "abc"); 将输出:"s"
- a??b??c == a??(b??c)
类型是实例对象的模板,泛型类型是类型的模板。
类型参数的约束用 where 子句列出:where 参数:constraint, constraint, …
- 构造函数约束 new() 必须放在最后;
- 主约束(class/struct)至多一个,且必须放在第一位;
- 接口约束可以有多个;
只有 Type 类型或派生于 Type 类型的实参才能用于受约束的参数。
- 将 \ 当作 普通字符 处理、而非转义字符,但局部有效性(@"xxx\abc" + "\\");
- 可任意换行(常用于SQL字符串),但是换行符、缩进、空格都计算在字符串长度之内;
- 在str中,用两个连续的英文双引号表示一个英文双引号(注意是必须在str中);
委托 - 事件
a. 委托 delegate:对函数的封装,一种引用方法的类型 (引用类型),代表一类方法,具有相同参数列表和返回类型;
b. 事件 event:委托的一种特殊情形,事件的类型是委托,事件是委托类型的变量,事件不是类型,事件是成员(变量)且被隐式自动初始化为null;
利用”+=”增加委托的实例/静态方法,利用”-=”移除委托的实例/静态方法。事件被触发时,执行被委托的方法(调用委托来依次调用调用列表中的方法)。此处引用大话设计模式中的例子:
public delegate void CatShoutEventHandler(object sender, EventArgs e);
public event CatShoutEventHandler CatShout;
委托是面向对象、类型安全的并且是可靠受控、安全的。
- 当委托被调用时,它调用有序方法列表中的每一个方法。委托是恒定的,委托对象被创建后就不会再被改变。
- 调用空委托会抛出异常,通过把委托和null比较判断委托的调用列表是否为空,进而判断委托是否为null。
匿名方法 -> Lambda表达式
匿名方法,anonymous method,可以避免创建使用独立的具名方法,允许在创建并初始化委托或为委托增加方法时包含小段的内联inline代码。
delegate(参数列表){语句块};
Lambda表达式避免冗余信息、简化匿名方法的语法。
总结: 从 委托事件 到 观察者模式;
枚举 ~ 枚举数 ~ 可枚举类型
枚举 enum,值类型,成员是整数值常量,可以显式为其赋值初始化,但不能使用修饰符。枚举可用于实现位标志,注意添加 [Flags] 特性。
可枚举类型是实现了GetEnumerator()方法的类型,返回用于(读取并返回)集合中数据项的枚举数,枚举数是可以依次返回集合中数据项的类对象。
参考:迭代器学习系列;自定义类实现foreach;
[-1-]. IEnumerable / IEnumerator
非泛型枚举数和可枚举类型,枚举数类通常声明为类中的嵌套类。
IEnumerator
- Current:当前位置对应的数据项;
- MoveNext():下移位置,初始位置为-1;
- Reset():复位;
IEnumerable
- IEnumerator GetEnumerator():
[-2-]. IEnumerable<T> / IEnumerator<T>
泛型枚举数和可枚举类型,类型安全。
总结:IEnumerable / IEnumerator 学习 - sqh
关键字/修饰符/运算符
0. object 类
C#中所有的类(型)都直接/间接继承自System.Object类(型),值类型数据可以隐式转换为Object类型;object是引用类型,关键字object就是System.Object的别称。
■ 静态方法
[1]. public static bool Equals(object objA, object objB){}
调用实例方法Equals(object obj),判断是否相等;
[2]. public static bool ReferenceEquals(object objA, object objB){}
判断两个对象是否引用相等;
■ 实例方法
[1]. public virtual bool Equals(object obj){}
方法需要重写用于实现根据值来判断对象是否相等;
[2]. public virtual int GetHashCode(){}:获取对象的Hash值;
[3]. public Type GetType(){}
获取当前实例的Type,查询对象的元数据来确定对象的运行时类型;
[4]. public virtual string ToString(){}:获取当前实例的字符串信息,对象的字符串表达形式;
■ 受保护方法
[1]. protected virtual void Finalize(){}
类或派生类可以访问,允许 Object 在“垃圾回收”机制回收 Object 之前尝试释放资源并执行其他清理操作;
[2]. protected object MemberwiseClone(){}:创建当前实例的浅表副本;
1. partial
a. 把类定义放在多个代码文件中;
b. 用于创建部分方法(定义声明和方法实现),不能有访问修饰符,返回值必须为 void;
2. internal
类和类成员的访问修饰符,同一程序集权限。类默认是 internal,类成员默认是 private。
protected internal:受保护的内部成员,同一程序集 or 子类权限。
参考:internal - 举例参考
嵌套类:嵌套是指类声明的位置,而不是类实例的位置。
嵌套类具有成员访问级别,默认 private,可见性具体地:
· 嵌套类型的成员对封闭类型的成员具有完全访问权限;
· 封闭类型的成员只能访问嵌套类型的public和internal成员,不能访问private和protected成员;
嵌套类型的对象访问封闭类型,需要维护封闭类型的引用。
3. using
a. using 指令:命名空间指示符
b. using 别名:类型别名指示符
一个.cs文件引用了两个不同的命名空间,但两个空间都包括一个相同名字的类型,使用别名更简洁。
using aClass = NameSpaceA.MyClass;
using bClass = NameSpaceB.MyClass;
c. using语句:资源的包装和管理 -> 隐式的 try…finally 块
定义一个范围,在范围结束时自动处理对象,自动调用这个类实例的 Dispose 方法。资源是一个实现 System.IDisposable 接口的类或结构。
4. 异常:try…catch…finally
结构化异常处理语法,标记出能处理异常的代码和指令:
■ try:包含可能会抛出异常的代码;
■ catch:抛出异常后要执行的代码,catch块可多个;
■ finally:始终一定会执行的代码,释放资源;
try 块是必须的,catch 和 finally 必须至少有一个。所有的异常类均派生于 System.Exception 类。
异常嵌套的处理:如果异常出现在 Method2 方法内部,但是其 catch 块没有匹配的异常处理程序, 系统沿着调用栈向上搜索到 Method1,如果找到匹配的 catch 块,系统先回到栈顶 Method2 处执行其 finally 块,然后把 Method2 从调用栈中 pop(),最后执行 Method1 的相应 catch 块和 finally 块。
public void Method2() public void Method1()
{ {
try{ try{
... Method2();
} }
catch{...} catch{...}
finally{...} finally{...}
} }
■ throw:显式抛出异常;
- throw Exception;异常抛出后,异常实例可以被 catch 块捕获。
- throw;此种只能在 catch 块内,捕获后再重新抛出。
5. String、StringBuffer 与 StringBuilder
- String是字符串常量、定长,StringBuffer与StringBuilder是字符串变量、可变长、避免产生额外的临时变量;
- StringBuffer线程安全,StringBuilder是非线程安全
三者的执行速度 StringBuilder > StringBuffer > String
具体区别详见:String - StringBuffer - StringBuilder
string - String
- String是.NET Framework中的类,string是C#中的类,
- C#的string映射为.NET Framework的String;
- string是C#中的关键字,可以作为String或System.String的别名;
6. const 与 readonly
- const只能在声明语句中初始化,readonly可以在声明语句或构造函数中初始化
- const是编译时常量、在内存中没有存储位置,readonly是运行时常量、在内存中有存储位置
- const是静态的,readonly可以是静态字段也可以是实例字段。
7. typeof 与 GetType
- typeof:一元运算符, typeof(classA) 返回作为它的参数的任何类型的 System.Type 对象,不能重载。
- GetType:System.Object的方法, obj.GetType(); 可以调用 typeof 运算符,对任意类型的任意对象都有效。
8. Marshal.SizeOf和sizeof
参考:http://www.cnblogs.com/jxnclyk/archive/2010/06/09/1754438.html,同时考虑内存对齐的问题,特别是结构体中包含引用对象时,最好使用Marshal.SizeOf。
常用函数
1. Convert.ToInt32 - int.Parse(Int32.Parse)- int.TryParse - (int)
- Convert.ToInt32与int.Parse类似,Convert.ToInt32 内部调用了int.Parse,Convert.ToInt32 可以转换的类型较多,int.Parse只能转换数字类型的字符串;
- int.TryParse与int.Parse类似,但不会抛出异常,返回值为bool以指示解析是否成功,从而可以免去添加异常处理代码的麻烦,out参数为转换输出值;
此四者都可以解释为将类型转换为 int,eg:举例参考.
注:所有预定义的简单类型均包含静态方法 Parse,将字符串解析为相应的数据值。
2. Split
String类的内置方法,分割函数,参数可以为单个字符、多个字符、字符串。
参考:Split - 常用举例参考,Split的不同重载方法.
3. CompareOrdinal:(推荐)
将整个字符串每5个字符(10个字节)分成一组,然后逐个比较,找到第一个不相同的ASCII码后退出循环,并求出两者的ASCII码差。虽然实现麻烦,但在CLR via C#上有表明:该方法比其他方法都要快。
4. DateTime
· 与字符串string的转换
- DateTime.Parse(timeString);
- Convert.ToDateTime(timeString);
- if (DateTime.TryParse(timeString, out datetime)) {
DateTime dm = datetime;
}
· DateTime
集合类:数据存储和检索
与 ArrayList 对应的泛型集合是 List,与 HashTable 对应的泛型集合是 Dictionary。
ArrayList:是Array的复杂版本,动态数组,实现了ICollection和IList接口,针对任意类型、任意长度,非类安全型的;
具体地属性方法类似List,此处不再赘述。
HashTable:每个元素都是一个存储在DictionaryEntry对象中的键值对。keyvalue键值对均为object类型,支持任何类型的keyvalue键值对,非类安全型的;线程安全的;
遍历哈希表元素: foreach(DictionaryEntry de in ht)
哈希表排序:
ArrayList KeysAList = new ArrayList(ht.Keys);
KeyAList.Sort();
1. 泛型List
- mList.Count:对链表mList元素计数
- mList.Add(T item):添加元素
- mList.Insert(int pos, T item):指定位置插入元素
- mList.AddRange(List list):链接2个List
- mList.Contains(T item):测试List是否包含元素item
- mList.Item(int idx):索引器,通过指定索引获取或设置元素
- mList.Remove(T item):删除指定的元素
- mList.RemoveAt(int pos):删除指定位置的元素(推荐)
- mList.RemoveRange(int b, int n):删除从b开始的n个元素
- mList.Clear():清空List
- mList.Reverse():反转List
- mList.Sort():排序List
2. 泛型Dictionary
在C#中,Dictionary提供快速的基于键值的元素查找。Dictionary<[key], [value]>,键必须唯一且不能为空引用null,值若为引用类型则可以为空。
- mDict.Count:对字典mDict元素计数
- mDict.Add(T1 key, T2 value):添加元素(键, 值)对
- mDict.ContainsKey(T1 key):字典是否包含键为key的元素
- mDict.ContainsValue(T2 value):字典是否包含值为value的元素
- mDict.Remove(T1 key):移除键为key的元素
- mDict.Clear():清空Dict
- 遍历字典元素
1. By KeyValuePair
foreach (KeyValuePair<T1, T2> kvp in mDict) 或 foreach(var kvp in mDict)
2. By Key
Dictionary<T1, T2>.KeyCollection keyCol = mDict.Keys;
foreach (T1 key in keyCol) 或 foreach(T1 key in mDict.Keys)
3. By Value
Dictionary<T1, T2>.ValueCollection valueCol = mDict.Values;
foreach (T2 value in valueCol) 或 foreach(T2 value in mDict.Values)
- mDict[key] = value:通过索引器读写键值对
- mDict.TryGetValue(T1 key, out value_T2):获取与指定的键相关联的值。通过键取值,包括两个参数,一个是要查询的键,另一个是获取的值,注意值前面使用out关键字。
注:“判断键存在”和“根据键取值”两步转化为一步,键的哈希值只计算一次,效率高。
以下三个集合类,可以进一步参考Stack - Queue - SortedList.
3. SortedList
System.Collections.SortedList类表示按键排序的键/值对的集合,可以按键或索引访问,是数组和哈希表的组合。
遍历排序列表元素:foreach(DictionaryEntry de in sList)
泛型SortedList
4. 堆栈 Stack
System.Collections.Stack类表示对象的LIFO集合,处理顺序多变。
st.Peek:取栈顶元素,但不将其移除;
st.Push(object obj):栈顶入栈;
st.Pop():出栈,移除并返回位于Stack栈顶处的对象;
泛型Stack
5. 队列 Queue
System.Collections.Queue类表示对象的FIFO集合,顺序处理集合中的对象
qu.Peek:取队首元素,但不将其移除;
qu.Enqueue(object obj):队尾入队;
qu.Dequeue():出队,移除并返回位于Queue开始处的对象;
泛型Queue
集合与多线程
当有多个线程并发访问集合时,应该用System.Collections.Concurrent命名空间代替上述命名空间中的对应类型,线程安全的集合类可由多个线程同时访问:
- ConcurrentDictionary
- ConcurrentQueue
- ConcurrentBag
有关集合类的详细内容参见:http://www.cnblogs.com/wjcx-sqh/p/6049314.html
参考:[1]. 经典.Net面试题; [2]. .Net面试题系列 0-9;