C#/.NET 基础学习

与 Java有不同,借鉴 Delphi的特点,与 COM(组件对象模型)直接集成,是 .NET windows网络框架的主角。 

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]. 构造函数中不能调用虚方法
[2]. 构造函数初始化器关键字:this,base,控制类中构造函数的执行顺序
[3]. 静态构造函数只会被执行一次(在创建第一个实例或引用任何静态成员之前,且由.NET自动调用)
[4]. 静态构造函数在程序域(AppDomain)的层级确保只会执行一次,且线程安全
[5]. 静态构造函数非常适于在单件模式中(或只需要单一对象的地方)

关于两者的具体信息参见: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. 拆箱:显式转换,从对象中提取值类型;

implicitexplicitisas

[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)
10. 泛型
类型是实例对象的模板,泛型类型是类型的模板。
类型参数的约束用 where 子句列出:where 参数:constraint, constraint, …
  • 构造函数约束 new() 必须放在最后;
  • 主约束(class/struct)至多一个,且必须放在第一位; 
  • 接口约束可以有多个;
只有 Type 类型或派生于 Type 类型的实参才能用于受约束的参数。
11. 字符串
规则字符串:在双引号中的零个或多个字符组成,并且可包含简单转义字符序列。("str")
逐字字符串:由 @ 字符后跟普通的双引号字符串。(@"str")
  • 将 当作 普通字符 处理、而非转义字符,但局部有效性(@"xxxabc" + "\");
  • 可任意换行(常用于SQL字符串),但是换行符、缩进、空格都计算在字符串长度之内;
  • 在str中,用两个连续的英文双引号表示一个英文双引号(注意是必须在str中);
:C#中Environment.NewLine表示为当前环境定义的换行字符串(非Unix平台为字符串“ ”,Unix平台为字符串“ ”),推荐使用。 
IsNullOrEmpty:判断字符串是否为null或者string.Empty;
IsNullOrWhiteSpace:判断字符串是否为null、空(string.Empty)还是仅由空白字符组成;
首先,确认string.Empty为空字符串,即string.Empty = "";对于" "、" "这样的仅由空白字符组成的字符串,IsNullOrWhiteSpace可以直接判定,而IsNullOrEmpty需要str.Trim().Length搭配使用。

委托 - 事件

 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

原文地址:https://www.cnblogs.com/wjcx-sqh/p/5929901.html