C#高效编程--改进C#代码的50个行之有效的办法笔记

该书并不是简单的语言新特性介绍,而是介绍如何使用这些特性帮你解决每天都要遇到的实际问题。

Item1 使用属性而不是可访问的数据成员

属性这个语言元素可以让你像访问数据成员一样使用,但其底层却使用方法实现。因为属性是使用方法来实现的,所以添加多线程支持也非常简单。很容易即可在属性的get和set访问器中作出如下的修改,从而支持对数据的同步访问:

public class Customer
{
	private object syncHandle = new object();
	private string m_name;
	public string Name
	{
		get
		{
			lock(syncHandle)
				return m_name;
		}
		set
		{
			if(string.IsNullOrEmpty(value))
			{
				throw new ArgumentException("Name cannot be blank","Name");
				lock(syncHandle)
				name=value;
			}
		}
	}
}

Item2 用运行时常量(readonly)而不是编译期常量(const)

C#有两种类型的常量:编译期常量和运行时常量。虽然编译期常量略微快一些,但是却没有运行时常量那么灵活。编译期常量(const)仅能用于数字和字符串。运行时常量 (readonly)也是一种常量可以为 任意类型,运行时常量必须在构造函数或者初始化器中初始化。

二者最重要的区别在于,readonly值是在运行时解析的。 引用 一个readonly常量时生成的IL引用 的是readonly变量,而不是其值。编译期常量将生成同样的IL,就像直接在代码中给出数字一样,即使跨程序集也是如此,即使另一个程序集中引用 了某个程序集中的某个常量 ,相应常量也会被直接替换成这个值。这将对程序运行时的兼容性产生深远影响。


Item3 正确地初始化静态成员变量

静态构造函数是一个特殊的函数,将在其他所有方法执行之前以及 变量或属性被第一次访问之前执行。你可以使用这个函数来初始化静态变量、实现单例模式或执行类可用之前必须进行的任何操作。你不应该使用实例构造函数、专门的私有函数或其他什么方式来初始化静态变量。

和实例初始化一样,也可以使用初始始化器语法来替代静态的构造函数。若只是某个静态成员 分配空间,那么不妨使用初始化器语法。而若是以更复杂 一些的逻辑来初始化成员变量,那么可以使用静态构造函数。

在C#中实现单例模式是静态构造函数最常用的一个场景。只需要将实例构造函数声明为私有,然后添加一个初始化器即可:

public class Mysingleton
{
	private static readonly Mysingleton m_instance = new Mysingleton();
	public static Mysingleton Instance
	{
		get {return m_instance;}
	}
	private Mysingleton()
	{
	}	
}
单例模式也可以如此简单地实现,除非你还有更加复杂的用来初始化单例的逻辑:

public class Mysingleton2
{
	private static readonly Mysingleton2 m_instance;
	static Mysingleton2()
	{
		m_instance = new Mysingleton2();
	}
	public static Mysingleton2 GetInstance
	{
		get {return m_instance}
	}
	private Mysingleton2()
	{
	}	
}
与实例的初始化器类似,静态初始化器在任何静态构造函数调用之前执行。而且,静态初始化器在调用基类的静态构造函数之前执行。

在应用程序作用域内,在你的类型被第一次访问之前,CLR会自动调用你的静态构造函数。若某个异常被静态构造函数抛出,那么CLR将终止你的程序。CLR无法通过静态构造函数初始化该类型,而且也不会再次尝试,导致该类型并没有被正确初始化。

使用静态构造函数而不是静态初始化器的最觉见理由是处理异常。在使用静态初始化器时,我们无法自己捕捉异常,而在静态构造函数中却可以做到。


Item4 避免创建非必要的对象

GC能够帮你很好地管理内存,也会以一种非常高效的方式来移除内存中的垃圾对象。不过不管有多高效,分配和销毁在堆上的对象总会花费掉时间。若是在某个方法中创建了太多的引用对象,那么将会对程序的性能产生严重的影响。

因此,不要给GC带来太多的负担。这里有一些规则,可以让你尽量降低GC的工作量。所有的引用类型,包括那些局部变量,都会分配在堆上。在函数之后,函数内的所有局部变量都会立即变成垃圾。一个很觉的错误做法就是在窗体的Paint处理函数中创建GDI对象:

protected override void OnPaint(PainEventArgs e)
{
using (Font pFont = new Font("Arial",10.0f))
{
e.Graphics.DrawString(DateTime.Now.ToString(),
pFont,Brushes.Black,new PointF(0,0));
}
base.OnPaint(e);
}
不过若是将Font之类实现了IDisposable接口的局部变量提升成成员变量,那么类本身也要实现IDisposable接口。前面使用的静态属性Brushes.Black演示了另一个避免重复创建类似对象的方法。创建静态成员变量可以让引用类型在类的各个实例中共享。Brushes类中包含了一系列的静态Brush对象,每个都包含了一种常用 的颜色。Brushes类的内部使用延迟求值的方式根据具体的需要创建笔刷,其简要的实现如下:

private static Brush blackBrush
public static Brush Black
{
get
{
if(blackBrush==null)
blackBrush=new SolidBrush(Color.Black);
return blackBrush;
}
}

Item5 实现标准的销毁模式

类型继承体系中的基类应该实现IDisposable接口来释放资源。该类型也应该添加一个终结器作为最后的保护手段。这两具处理均应该把具体的释放资源工作转交给一个虚方法执行,且该 虚方法可由其派生类根据自己的需要覆写。仅在派生类必须释放自己的非托管资源时,才应该覆写该虚方法,且覆写时也必须调用基类的版本。

在GC运行时,它会立即清理掉那些没有提供终结器的垃圾对象。而提供了终结器的垃圾对象会停留在内在中,被添加到一个叫做“终结队列”(finalization queue)的地方。GC会使用另一个线程来执行队列中的对象的终结器。终结器完成工作之后,这些垃圾对象才能从内存中清理出去。那些需要终结的对象停留在内存中的时间要远远长于普通对象,不过也只能这样。

实现IDisposable接口是一种标准的做法,用来通知使用者和运行时系统该对象包含的资源需要及时释放。IDisposable接口仅定义了一个方法:

public interface IDispoable
{
	void Dispose();
}
IDisposable.Disposable方法的实现中需要完成如下4个任务:

1、释放所有非托管资源。

2、释放所有托管资源,包括释放事件监听程序。

3、设定一个状态标志,表示该对象已经被销毁。若是在销毁后再次调用对象的公有方法,那么应该抛出ObjectDisposed异常。

4、跳过终结操作,调用GC.SuppressFinalize(this)即可。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值