当前位置:编程学习 > C#/ASP.NET >>

C#高效编程话题集3

不如来编码-luminjis web

本期话题:

1:使用属性还是字段

 首先重大区别就是属性实质是方法,所以:
1:可以为属性添加代码;
2:可以让属性支持线程安全;见effective c#第一版的第一章;
3:属性得到了VS编辑器的支持,得以实现自动属性这种功能。
4:自动属性的特点在LINQ中得到了广泛应用,尤其是匿名类型中,只能实现只读的自动属性,匿名类型不支持字段;
5:从设计的角度,也就是面向对象的角度,建议使用属性;
6:如果某个属性仅仅作为类型内部使用,而且不涉及到上面5点内容,则建议使用字段

还有一点,属性在序列化中有点尴尬,由于属性是方法,所以不能为其指定[NonSerialized]特性。

2:利用泛型拓宽方法的应用范围

假设存在方法:

static void PrintSalary(ISalary<Employee> s){    s.Pay();}
然后像下面这样使用它:

ISalary<Programmer> s = new BaseSalaryCounter<Programmer>();PrintSalary(s);
注意,几个主要类的代码:

interface ISalary<T>{    void Pay();}class BaseSalaryCounter<T> : ISalary<T>{    public void Pay()    {        Console.WriteLine("Pay base salary");    }}class Employee{    public string Name { get; set; }}class Programmer : Employee{}
运行结果是:

无法从“ConsoleApplication4.ISalary<ConsoleApplication4.Programmer>”转换为“ConsoleApplication4.ISalary<ConsoleApplication4.Employee>”

显然我们认为PrintSalary方法因为能支持ISalary<Employee> ,所以肯定能支持ISalary<Programmer>,这是一种很容易犯的尝试错误。

改进方法先提供一种思路,就是将该方法改成泛型。

3:利用协变关键字out

 要解决话题2中的问题,还可以使用out关键字。即修改接口为:

interface ISalary<out T>{    void Pay();}

4:减少使用类型的静态变量

1:静态变量一旦被创建不被释放;

2:静态变量不是线程安全的,它不像类型变量只对创建类型的那个线程有效;

5:线程同步有多少种方法?

 所谓线程同步,采用的技术,就是在某个对象上等待(也理解为锁定该对象)。C#中类型对象的分类:引用类型和值类型。在这两种类型上的等待是不一样的,我们也可以简单的理解为在CLR中,值类型是不能被锁定的(参考MSDN的volatile关键字)。

在引用类型上的等待,又分为两种技术:锁定和信号机制。

锁定使用:关键字lock和类型Monitor,前者其实是后者的语法糖。这是最常用的同步技术,小伙们应该都用过;

信号机制:信号机制中涉及的类型都继承自抽象类WaitHandle,这些类型有EventWaitHandle(类型化为AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex。它们之间有一定的区别,值得一提的是Mutex能同步不同应用程序域。其它几个类型的不同点MSDN写的很伤人,而且未进行横向比较,简单来说,就是EventWaitHandle通过布尔型进行同步,其中AutoResetEvent又自动恢复类型的阻滞状态,ManualResetEvent则必须手动恢复阻滞状态。Semaphore通过整型进行同步。

6:让对象=null能否加速对象被垃圾回收器回收?

 注意,以下描述都只针对引用类型

1:在方法内,方法参数和局部变量,赋值为null无助于加速被垃圾回收器标识为垃圾;

2:实例变量,随着实例=null,实例变量也会自动=null。所以,一般情况下,无需显式为实例变量=null,除非你的实例生存周期较长,并且实例变量是个大对象;

3:静态变量,如果不显式置为null,就永远不会被回收;

7:不建议lock(this)

 1:如果两个对象的实例分别执行了lock(this)这样的代码,实际锁定的是两个对象,完全不能达到同步的目的。

2:最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。

8:区分计算密集型和I/O密集型的多线程应用场景

I/O密集型操作。硬盘、网卡、声卡、显卡等都是。CLR所提供的异步编程模型就是让我们充分利用硬件的DMA功能来提高CPU的利用率。

一个在大多数情况下正确的技巧是,凡是FCL中类型提供了类似BeginDoSomething方法的,都建议使用这个异步调用来完成多线程编码,异步在后台调用线程池线程完成调度,最大化的节约了系统的性能。

9:使用Parallel的一个陷阱

 以下的代码的输出是什么?

int[] nums = new int[] { 1, 2, 3, 4 };long total = 0;Parallel.For<long>(0, nums.Length, () =>{    return 1;}, (i, loopState, subtotal) =>{    subtotal += nums[i];    return subtotal;},    (x) => Interlocked.Add(ref total, x));Console.WriteLine("The total is {0}", total);
实际上,它有可能是11,较少的情况下会是12,几乎不可能出现13,14。

要从方法的最后一个参数Action<TLocal> localFinally讲起,它表示的其实是并行中如果新起了一个线程,那么这个线程结束时会调用这个localFinally。 而我们知道并行在后台采用的是线程池,也就是,我们不知道上面代码所发起的这个并行实际会发起多少个线程,如果是1个,自然就是11,如果是2,结果自然就是12了,依此类推。之所以不可能为14,是因为,并发的循环体就只有4个循环,并发显然不会傻到新起4个线程来执行并发。

10:不建议lock(类型的type)

 由于typeof(SampleClass),是SampleClass的所有实例所共有的,这会导致当前应用程序中的所有SampleClass的实例的线程,将会全部被同步。

 

补充:软件开发 , C# ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,