Part 1 C# 语言

Core C#

常量总是静态的。

byte类型默认无符号,有符号的叫sbyte

decimal类型不是基本类型,在计算时候会有性能损失。

stringSystem.String):引用类型,对象分配在堆上。字符串不可变,修改一个字符串会创建一个新的string对象,原来字符串不变。

objectSystem.Object)。

case子句必须包含break语句,除非子句为空。

属性和方法的使用

  • 客户端应能读取它的值,最好不要使用只写属性。
  • 读取值不应花太长的时间。
  • 读取值不应有任何明显的和不希望的负面效应。
  • 可以按照任何顺序设置属性。
  • 顺序读取属性也应有相同的效果。

字段应总是私有的,某些情况可以把常量或只读字段设置为公有。

对象和类型

重载方法的限制

  • 方法不能仅在返回类型上有区别。
  • 方法不能仅根据参数是声明为refout来区分。

在何处内联代码完全由CLR决定(与C++不同)。

私有构造函数的作用

  • 类仅用作某些静态成员或属性的容器,因此永远不会实例化它。
  • 希望类仅通过调用某个静态成员函数来实例化(对象实例化的类工厂方法)。

静态构造函数

  • 无参数,无访问修饰符,只执行一次,在代码引用类之前调用它。通常在第一次调用类的任何成员之前执行静态构造函数。
  • 类有一些静态字段或属性,需要在第一次使用类之前,从外部源中初始化这些静态字段和属性。

结构 struct:值类型,存储在栈或者内联(如果它们是存储在堆中的另一个对象的一部分),new只是调用相应构造函数,并不分配堆中内存。

  • 不支持继承,可以继承接口。
  • 编译器总是提供一个无参构造函数,不允许自定义无参构造函数。
  • 可以指定字段如何在内存中布局。
  • 字段默认私有(与C++不同)。

弱引用 WeakReference

静态类 static class

继承

除非显式指定,否则函数就不是虚拟的(在Java中所有的函数都是虚拟的)。

泛型

泛型的好处

  • 性能。不再进行装箱和拆箱操作。
  • 类型安全。
  • 允许更好地重用二进制代码,可以在一种语言中定义,在任何其他.NET语言中使用。

泛型类的静态成员只能在类的一个实例中共享。

运算符和类型转换

C#要求所有的运算符重载都声明为public static(与C++不同);某些运算符应该成对重载。

强制类型转换:public static implicit operator int (A a) { ... }

LINQ

推迟查询:迭代在查询定义时不会进行,而是在执行每个foreach语句时进行。

异步编程

/* Refer to CLR via C#. */

内存管理

后台内存管理

值数据类型:栈存储不是对象成员的值数据类型;在调用一个方法时,也使用栈存储传递给方法的所有参数的副本。

引用数据类型:托管堆,在垃圾回收器的控制下工作。

垃圾回收

  • 垃圾回收器的压缩操作是托管堆与非托管的旧堆的区别所在。使用托管的堆只需要读取堆指针的值,不需遍历地址的链表来查找一个地方来放置新数据。因此在.NET下实例化对象要快得多。访问也较快(对象压缩到相同内存区域,页面交换少)。MS相信GC压缩、修改移动对象的引用值的开销是值得的。
  • 可以调用System.GC.Collect()强迫运行回收器(不保证删除所有未引用的对象)。
  • 大对象堆。
  • 垃圾回收的负载均衡。

释放非托管资源

非托管资源:文件句柄、网络连接、数据库连接等。

托管类在封装对非托管资源的直接或间接引用时,需要制定专门的规则,确保非托管的资源在回收类的一个实例时释放。

释放非托管资源的两种机制

1) 声明析构函数或终结器,作为类的一个成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyClass
{
~MyClass()
{
// destructor implementation
}
}

// 编译后的等价Finalize()方法...

protected override void Finalize()
{
try
{
// destructor implementation
}
finally
{
base.Finalize();
}
}

C#与C++在析构上的区别:在销毁C++对象时析构函数会立即执行。由于C#垃圾回收器的工作方式,无法确定C#对象的析构函数何时执行。

2) 在类中实现System.IDisposable接口(推荐)

Dispose()方法显式释放由对象直接使用的所有非托管资源,并在所有也实现IDisposable接口的封装对象上调用Dispose()方法,为何时释放非托管资源提供了精确的控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// V1
ResoureGobbler instance = null;

try
{
instance = new ResoureGobbler();
// ...
}
finally
{
instance.Dispose();
}

// V2
// instance在超出作用域后自动调用Dispose()方法,即使出现异常。
using(ResoureGobbler instance = new ResoureGobbler())
{
// ...
}

实现IDisposable接口+使用析构函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ResourceHolder : IDisposable
{
private bool isDisposed = false;

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
// Clean up managed objects by calling their Dispose() methods.
}
// Clean up unmanaged objects.
}

isDisposed = true;
}

~ResourceHolder()
{
Dispose(false);
}

public void Foo()
{
// Ensure object not already disposed before execution of any methods
if(isDisposed)
{
throw new ObjectDisposedException("ResourceHolder");
}

// ...
}
}