Type类型
1 | var i = 2.0; |
输出:类型:System.Double,类型码(enum):Double,类型码的值:14
1 | var t = typeof(Person); |
Type的成员方法
1 | GetConstructor[s]() //返回ConstructorInfo |
Assembly和Type
1 | Assembly ass = Assembly.Load("ConsoleApplication1"); |
程序集
dll,逻辑单元。包含模块,资源等。
程序集强名:共享程序集使用强名唯一地标识该程序集,保证全局唯一,包括:
- 程序集本身的名称
- 版本号。不同版本可以共存于一个进程。
- 公钥。保证独一无二。
- 文化
应用程序域
图解
不同域加载程序集
1 | namespace AssemblyA |
当前域加载程序集
1 | //动态加载程序集,创建实例 |
方法和函数
函数>方法。
函数包括:方法,非数据成员:索引器,构造函数,析构函数,属性。
构造函数初始化器
1 | public Car(string des) : this(des , 4) |
实现构造函数之间的重用: this 或: base 之后的构造函数先执行,然后再执行此构造函数。
partial关键词
编译时,两个不分类的属性,XML注释,接口,泛型类型的参数属性,特性和成员会合并。
泛型中的default关键字
初始化泛型参数T时,可以使用detault(T)。在不知道T是引用类型还是值类型的情况下,分别赋予引用类型null或者值类型0
委托
类似于C++中的函数指针(但类型不安全,参数和返回值未知)。
面向对象编程,没有方法是孤立的。如果要传递方法, 就要把方法细节(签名和返回类型)封装在一种新类型对象中,即委托。委托是一种特殊对象,普通对象都包含数据,而委托包含的只是一个或多个方法的地址(指针)。
委托的定义和使用:(面向对象的角度)
定义一个委托(类似定义一个类)
private delegate string GetAString();
实例化一个GetAString的委托实例(实例化一个对象)。委托语法上总是接收一个参数的构造函数。
GetAString me=new GetAString(x.ToString);
或者委托推断:
GetAString me=x.ToString;
me();或者me.Invoke();
编译器会用me.Invoke();代替me();
浅表复制,深度复制
浅表复制,成员对象,直接复制引用。成员值,创建值副本。
深度复制,成员对象,创建副本。成员值,创建值副本。
1 | [//可被序列化,实现深度复制 ] |
Array抽象类
1 | Type t=...; |
事先不知类型,可由此创建数组。
Array.Sort(数组,IComparer
Enumerator
GetEnumerator()方法用IEnumerable接口定义。foreach语句并不真的需要集合实现这个接口。有一个名为GetEnumerator()的方法,返回实现了IEnumerator接口的对象就行了。
IEnumerator接口定义了Reset()方法,以与COM交互操作。
foreach
解析为下面的代码段:
1 | var enumerator = list.GetEnumerator(); |
yield语句
包含yield语句的方法或属性也成为迭代块。必须声明为返回IEnumerator或IEnumerable接口。可包含yield return或yeild break,不能有return语句。
使用迭代块,编译器会生成一个yield类型,包含一个状态机。记录了迭代的当前位置。
foreach访问迭代器,一次访问一个。无需一次加载完所有数据。可以把yeild类型看作内部类Enumerator。
1 | class My |
默认迭代是:定义为返回Enumerator的GetEnumerator()方法,是foreach默认采用的方法。自定义命名迭代返回IEnumerable。
集合特性
- IEnumerabel
:GetEnumerator(),返回一个实现了IEnumerator接口的枚举。可以被foreach。 - ICollection
:由泛型集合实现。Count属性。CopyTo()方法(把集合复制到数组中)。Add(),Remove(),Clear()方法。 - IList
:派生自ICollection 。定义了索引器,可通过指定位置,Insert()或RemoveAt()。 - ISet
:派生自ICollection 。由集实现。合并,交集,并集。 - IDictionary
:键值集合。索引器。 - ILookup
:一个键包含多个值。 - IComparer
:比较器实现。Compare()对集合中的元素排序。 - IEqualityComparer
:比较器,数组,元祖实现。该比较器可用于字典中的键。 - IProducerConsumerCollection
:.NET4.0。支持新的线程安全的集合类。 - IReadOnlyCollection
,IReadOnlyList ,IReadOnlyDictionary :初始化后不能修改的,只能检索。 - IImmutableArray
,IImmutableList ,IImmutableQueue ,IImmutableSet , - IImmutableDictionary
:初始化后不能修改。不可变接口定义了不可变集合的方法和属性。
队列
先进先出(FIFO)容器。
Queue
Enqueue(),一端添加元素。Dequeue(),另一端读取和删除元素。
Peek(),从头部读取一个元素,但不删除。
栈
后进先出(LIFO)容器。
Push(),添加元素。Pop()获取最近添加的元素。
Peek(),返回栈顶的元素,但不删除它。
Lookup类
一键对多值
1 | List<Book> list = new List<Book> |
集
HashSet 不重复无序,SortedSet 不重复有序
1 | aSet.Add() //是否成功添加了元素。 |
Immutable
不可变集合,需要引用System.Collections.Immutable(.NET4.5)
1 | ImmutableList<string>.Add("") //每次返回一个新的不变集合,不改变本身。 |
并发集合
- IProducerConsumerCollection
:TryAdd(),和TryTake()。 - ConcurrentQueue
:免锁定,内部链表。TryTake(),Enqueue(),TryDequeue(),TryPeek() - ConcurrentStack
:链表。Push(),TryPeek(),TryPop(),TryPopRange() - ConcurrentBag
:没有定义添加或提取项的任何顺序,线程映射到内部使用的数组,尝试减少锁定。 - ConcurrentDictionary
:线程安全,非阻塞键值集合。TryGetValue(),TryRemove(),TryUpdate()。没有实现IProducerConsumerCollection 。 - BlockingCollection
:阻塞线程。Add(),Take()。
dynamic类型
dynamic类型允许编写忽略编译期间的类型检查代码。
var对象类型的确定会延迟,确定后不可变。dynamic类型可以改变多次。int->string->object
async
只能用于返回Task或void的方法。不能作为程序的入口点。
Task
启动Task的3种方式:
1.1
2
3
4
5
6
7Action act = () =>
{
Console.WriteLine("任务开始");
Thread.Sleep(2000);
};
Task t = new Task(act);
t.Start();
2.1
Task t = Task.Factory.StartNew(act);
3.1
2
3
4
5
6
7
8Task t = Task.Run(act); //对Factory的封装
//完成通知,回调
t.ContinueWith(task =>
{
Console.WriteLine("完成状态");
Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
});
Task组合器
- Task.WhenAll(t1,t2):等待,直到全部任务都完成。
- Task.WhenAny(t1,t2):等待,直到其中一个任务完成就会返回。
应用CancellationTokenSource,取消Task
1 | class Program |
Task
①进行逻辑判断,任务正常结束后,进入TaskEnded,结果:IsCanceled:false,IsFaulted:false 。
②通过抛AggregateException异常,立即结束任务,进入TaskEnded,结果:IsCanceled:true,IsFaulted:false。
③通过抛自定义异常,立即结束任务,进入TaskEnded,结果:IsCanceled:false,IsFaulted:true。
虚拟寻址系统
32位处理器上的每个进程都可以使用4GB的内存,无论计算机实际有多少物理内存(64位更大)。包含了:可执行代码,dll,变量等。
栈(stack)
栈指针指向为栈保留的内存块末尾。栈实际上是向下填充的。即从高内存地址向低内存地址填充。数据入栈后,指针调整,始终指向下一空闲存储单元。数据释放(变量超出作用域),指针向上递增。
变量的生存期总是嵌套的,保证了栈指针是有序的。
堆(heap)
堆上的内存是向上分配的。空闲空间在已用空间的上面。
垃圾回收
gc运行时,会从堆中删除不再引用的所有对象。只要释放了能释放的所有对象,它就把其他对象移动回堆的端部。再次形成一个连续的内存块。因此,堆可以确定在什么地方存储新对象。对象移动后,需要新地址更新引用,gc会处理更新问题。
大对象堆
不同于主堆,存储使用大于85000个字节对象。压缩大对象比较昂贵。故大对象对上的对象不执行压缩过程。
第二代和大对象堆上的回收放在后台线程执行,应用程序仅为第0代和第1代回收而阻塞,减少了暂停时间。
非托管资源
文件句柄,网络连接,数据库连接等。
托管资源
栈,堆上的数据。
析构函数
C#不常使用,无法确定执行时机。有析构函数的对象需要两次处理才能销毁。析构函数运行时间长,非常耗性能。
IDisposable接口
C#推荐IDisposable接口替代析构函数。声明Dispose(),释放非托管资源,控制更精准。
如果显式调用需要try catch,防止异常而没有执行。
代替用using()语句,更简单。
unsafe代码块
unsafe可以修饰 类成员方法等。标记为不安全代码,可以使用指针语法提高性能。但带来不安全性和不方便等。
指针语法
1 | int x=10; |
x内容改为20。pY与x中间没有任何关系,pY碰巧指向存储x的存储单元。
//pX,pY也占用4个字节,因为32位处理器上,4个字节存储一个地址。
&表示取地址,把一个值类型转换为指针。
*表示获取地址的内容,把一个指针转换为值类型。
强制转换:
1 | uint y=(uint)pX; |
自定义特性
1 | [//FieldNameAttribute,Attribute可以省略,自动添加。搜索指定名称的类,实例化。 ] |
使用:
1 | [ ] |
Exception
- 属性
Data,可以添加的额外信息字典。
HelpLink,连接帮助文件上。
InnerException,如异常在catch中抛出,则inner为把代码发送到catch块的异常对象。
Source,导致异常程序或对象名。
StackTrace,调用栈信息。
TargetSite,抛出异常的方法的反射对象。 .ReflectedType获得类的Type对象。
调用者特性
1 | public class Book |
ThreadPool
1 | Book b = new Book() { Title = "t", Price = 1 }; |
线程池的限制
- 线程池中的所有线程都是后台线程。并且不能把入池的线程改为前台线程。
- 不能设置池中线程的优先级或名称。
- 所有线程都是多线程单元(MTA)线程,许多COM对象都需要单线程单元(STA)线程。
- 入池的线程只能用于时间短的任务。
如果需要一直运行(如word拼写检查),应创建Thread或Task使用LongRunning选项。
控制线程
1 | var t1 = new Thread(() => { }); |
Interlocked类
速度快,简单的同步问题。原子操作。
1 | Interlocked.CompareExchange(); |
Monitor类
lock语句会被编译器解析成以下代码:
1 | object obj = new object(); |
Monitor的好处是可以指定等待时间:
1 | bool lockToken = false; |
读写互斥锁
1 | class Program |