改善C#编程的50个建议(11-15)

-------------------------翻译 By Cryking-----------------------------
-----------------------转载请注明出处,谢谢!------------------------ 

11 理解使用简短函数的好处
.NET运行时调用JIT编译器来转换IL代码为本地机器码:
    public string BuildMsg(bool takeFirstPath)
    {
        StringBuilder msg = new StringBuilder();
        if (takeFirstPath)
        {
            msg.Append("A problem occurred.");
            msg.Append("\nThis is a problem.");
            msg.Append("imagine much more text");
        }
        else
        {
            msg.Append("This path is not so bad.");
            msg.Append("\nIt is only a minor inconvenience.");
            msg.Append("Add more detailed diagnostics here.");
        }
        return msg.ToString();
    }


第一次运行BuildMsg时,两条路径都被JIT编译了,但只有一条是需要的,但如果你按下面代码重写它:
    public string BuildMsg2(bool takeFirstPath)
    {
        if (takeFirstPath)
        {
            return FirstPath();
        }
        else
        {
            return SecondPath();
        }
    }


此时哪条路径被JIT编译取决于第一次调用时走哪条。
简短函数使得JIT编译器更容易支持封装,也就是更容易使用寄存器保存局部变量而不是堆栈。
而且局部变量越少,使用寄存器的机会也就越大。记住简单就是好的,越简短的函数越容易被JIT来优化。
而且越简短的函数,越容易被JIT优化为内联函数。


12 偏爱成员初始化而不是赋值语句
类通常不止一个构造函数,久而久之成员变量和构造函数会变得不同步.避免这种情况的最好方式是在声明时就初始化变量而不是在构造函数里
使用变量初始化可以简单的避免在类中使用未初始化的变量,但这也不是完全的,以下3种情况你不应该使用
初始化方式:
1.在你的代码执行前,系统缺省的会初始化所有的变量为0(使用低级的CPU指令来初始化内存块),
此时任何额外的0初始化就是多余的,并且此时C#编译器还会再次将该内存块设置为0,值类型一般都不需要初始化,它会自动初始化为0:
MyValType myVal1; // initialized to 0
MyValType myVal2 = new MyValType(); // also 0
myVal1会被系统缺省地初始化为0.而myval2会使用IL指令initobj来初始化,并且此时myval2的初始化会产生一个装箱和拆箱的操作,非常损耗性能。


2.你只应在所有构造函数里都获得相同的初始化时使用变量初始方式,否则你应使用构造函数来初始化:
public class MyClass2
{
// declare the collection, and initialize it.
private List<string> labels = new List<string>();
  MyClass2()
  {
  }
  MyClass2(int size)
  {
    labels = new List<string>(size);
  }
}


当你以指定长度来实例化MyClass2时,你创建了2个数组列表,一个会立即成为垃圾.变量初始化会在每个构造函数执行前执行。
此时可以使用下面的代码来解决:
public class MyClass2
{
  // declare the collection, and initialize it.
  private List<string> labels;
  MyClass2()
  {
    labels = new List<string>();
  }
  MyClass2(int size)
  {
    labels = new List<string>();
    labels = new List<string>(size);
  }
}

3.最后一个不使用变量初始化的是异常处理,当你要使用try/catch块时,你应当使用构造函数的方式来初始化成员变量。
总之成员初始化是确保成员变量被初始的最简单的方式,它会在所有构造函数执行前就被执行,使用成员初始化方法使你避免因新增构造函数
而忘记初始化成员变量带来的问题,并且这种方式简单、易于维护。


13 为静态成员使用适当的初始化
你应当在实例化类之前就为类的静态成员进行初始化。C#让你使用静态初始化和静态构造函数来达到此目的。
静态构造函数是一个特殊的函数,它会在任何其他的方法、变量、属性访问前执行,你不能使用实例构造函数、其他私有函数等来初始化静态变量。
和实例成员变量初始化一样,当你有复杂的逻辑来初始化静态成员变量或有异常处理时,你应使用静态构造函数,否则你应使用初始化方式来初始化成员变量。




14 减少重复的初始化逻辑
编写构造函数通常一个重复的工作,很多开发者编写第一个构造函数后,然后采用复制粘贴的方式来生成其他构造函数。
有经常的C++程序员可能会将重复的逻辑提取成公共方法,这两种方式在C#里都是不可取的,C#提供了一个特定的语法来减少重复的代码,
如下:
    public class MyClass
    {
        // collection of data
        private List<ImportantData> coll;
        // Name of the instance:
        private string name;
        public MyClass() :
            this(0, "")
        {
        }
        public MyClass(int initialCount) :
            this(initialCount, string.Empty)
        {
        }
        public MyClass(int initialCount, string name)
        {
            coll = (initialCount > 0) ?
            new List<ImportantData>(initialCount) :
            new List<ImportantData>();
            this.name = name;
        }
    }


this(0,"")调用了带2个参数的构造函数,这样可以减少重复代码而又不必提取公共方法。C#4.0增加了缺省参数的功能,使得你在构造函数中更加减少
重复的代码。
如果你使用公共方法方式,改写代码如下:
    public class MyClass
    {
        // collection of data
        private List<ImportantData> coll;
        // Name of the instance:
        private string name;
        public MyClass()
        {
            commonConstructor(0, "");
        }
        public MyClass(int initialCount)
        {
            commonConstructor(initialCount, "");
        }
        public MyClass(int initialCount, string Name)
        {
            commonConstructor(initialCount, Name);
        }
        private void commonConstructor(int count,
        string name)
        {
            coll = (count > 0) ?
            new List<ImportantData>(count) :
            new List<ImportantData>();
            this.name = name;
        }
    }


这看起来好像减少了代码,但其实IL生成的代码如下:
    public class MyClass
    {
        // collection of data
        private List<ImportantData> coll;
        // Name of the instance:
        private string name;
        public MyClass()
        {
	    object();
            commonConstructor(0, "");
        }
        public MyClass(int initialCount)
        {
   	    object();
            commonConstructor(initialCount, "");
        }
        public MyClass(int initialCount, string Name)
        {
  	    object();
            commonConstructor(initialCount, Name);
        }
        private void commonConstructor(int count,
        string name)
        {
            coll = (count > 0) ?
            new List<ImportantData>(count) :
            new List<ImportantData>();
            this.name = name;
        }
    }


这样的代码仍是低效的.并且如果你将private string name改写为private readonly string name;此处this.name = name;将会报错。
也就是更改成员变量,你必须去公共方法里更改对应的变量。所以仍推荐使用C#的this构造函数来减少重复的代码。
PS:第一次实例化类时各个操作的顺序:
1.未赋值的静态成员变量被系统缺省设置为0.
2.静态成员变量初始化被执行.
3.基类的静态构造函数被执行
4.类的静态构造函数被执行
5.未赋值的实例成员变量被系统缺省设置为0.
6.实例成员变量初始化被执行.
7.相应的基类实例构造函数被执行
8.类的实例构造函数被执行


15 使用using和try/finally来清理资源
非系统托管资源应该使用IDisposable接口的Dispose()方法来释放。为了确保Dispose()总是被调用,最好的方法是使用using语句或try/finally
,所有有非托管资源的类型都应当实现IDisposable接口,实际上当你忘记使用或不适当地使用dispose时,系统会创建一个finalizer来清理。
如果你忘记dispose,那些非内存资源会被finalizer来清理,但是在内存中的对象会存在更长时间,这会导致你的应用会越来越慢。
幸运的是,C#设计了显示资源释放将会成为一个公共的任务,增加了如using、finally等关键字来使得这个任务更容易。
using语句其实就是生成了一个try/finally块,finally块里通常就是用来做资源清理的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值