编写高质量C#代码学习笔记(2)
2011-11-06 20:28
423 查看
建议6: 区别readonly和const的使用方法
很多初学者分不清readonly和const的使用场合。在我看来,要使用const的理由只有一个,那就是效率。但是,在大部分应用情况下,“效率”并没有那么高的地位,所以我更愿意采用readonly,因为readonly赋予代码更多的灵活性。const和readonly的本质区别如下:const是一个编译期常量,readonly是一个运行时常量。
const只能修饰基元类型、枚举类型或字符串类型,readonly没有限制。
关于第一个区别,因为const是编译期常量,所以它天然就是static的,不能手动再为const增加一个static修饰符,下面的代码将会导致编译通不过:
static const int ConstValue = 100; //常量“ConstValue”不能标记为 static
而之所以说const变量的效率高,是因为经过编译器编译后,我们在代码中引用const变量的地方会用const变量所对应的实际值来代替,如:
Console.WriteLine(ConstValue);
和下面的代码生成的IL代码是一致的:
Console.WriteLine(100);
readonly变量是运行时变量,其赋值行为发生在运行时。readonly的全部意义在于,它在运行时第一次被赋值后将不可以改变。当然,“不可以改变”分为两层意思:
1)对于值类型变量,值本身不可改变(readonly,只读)。
2)对于引用类型变量,引用本身(相当于指针)不可改变。
来看值类型变量,查看如下代码:
class Sample { public readonly int ReadOnlyValue ; public Sample( int value) { ReadOnlyValue = value; } }
Sample的实例ReadOnlyValue在构造方法中被赋值后就不可以改变,下面的代码将不会编译通过:
Sample sample = new Sample(200); sample.ReadOnlyValue = 300; //无法对只读的字 //段赋值(构造函数或变量初始值指定项中除外)
针对引用类型变量,查看如下代码:
class Sample2 { public readonly Student ReadOnlyValue; public Sample2(Student value) { ReadOnlyValue = value; } }
Sample2的ReadOnlyValue是一个引用类型变量,赋值后,变量不能再指向任何其他的Student实例,所以,下面的代码将不会编译通过:
Sample2 sample2 = new Sample2(new Student() { Age = 10 }); sample2.ReadOnlyValue = new Student() { Age = 20 }; //无法对只读的字段赋值(构造函数或变量初始值指定项中除外)
但是,我们之前已经提到过了,引用本身不可改变,引用所指的实例的值,却是可以改变的,下面的代码将会被允许:
Sample2 sample2 = new Sample2(new Student() { Age = 10 }); sample2.ReadOnlyValue.Age = 20;
readonly所代表的运行时含义有一个重要的作用,就是可以为每个类的实例指定一个readonly的变量。以Sample这个类为例,可以在运行时生成多个实例,而同时,又可以为每个实例生成自己的readonly变量,如下:
Sample sample1 = new Sample(100); Sample sample2 = new Sample(200); Sample sample3 = new Sample(300);
这就是readonly变量所代表的运行时含义,也正是readonly变量的灵活之处。
注意 有人说,readonly变量不能被重新赋值是不正确的。下面的代码表明了它可以被改变:
class Program { static void Main(string[] args) { Sample sample = new Sample(200); Console.WriteLine(sample.ReadOnlyValue); } } class Sample { public readonly int ReadOnlyValue = 100; public Sample(int value) { ReadOnlyValue = value; } }
ReadOnlyValue首先在初始值指定项(也称为初始化器)中被赋值为100,后来,在构造方法中又被赋值为200。实际上,应该把初始化器理解成构造方法的一部分,它其实是一个语法糖。在构造方法内,我们确实可以多次对readonly赋值。
建议9: 习惯重载运算符
在开发过程中,应该习惯于使用微软提供给我们的语法特性。我想每个人都喜欢看到这样的语法特性:int x = 1; int y = 2; int total = x + y;
而不是用下面的语法来完成一样的事情:
int x = 1; int y = 2; int total = int.Add(x, y);
同理,在构建自己的类型时,我们应该始终考虑该类型是否可以用于运算符重载。如果考虑类型Salary,下面的这段代码看起来就不是那么舒服了:
Salary mikeIncome = new Salary() { RMB = 22 }; Salary roseIncome = new Salary() { RMB = 33 }; Salary familyIncome = Salary.Add(mikeIncome, roseIncome);
应该使类型支持:
Salary familyIncome = mikeIncome + roseIncome;
后者读起来一目了然。CLR支持在类型中,通过使用operator关键字定义静态成员函数来重载运算符,让开发人员可以像使用内置基元类型一样使用该类型。Salary重载“+”运算符的版本看起来应该像以下形式:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Salary minkIncome = new Salary() { RMB = 22 }; Salary roseIncome = new Salary() { RMB = 33 }; Salary familyIncome = minkIncome + roseIncome; Console.WriteLine(string.Format("familyIncome.RMB:{0}", familyIncome.RMB)); Console.ReadLine(); //outPut:"familyIncome.RMB:55 } } /* * CLR支持在类型中,通过使用operator关键字定义静态成员函数来重载运算符,让开发人员可以像使用内置基元类型一样使用该类型。 * Salary重载“+”运算符的版本看起来应该像以下形式: */ class Salary { public int RMB { get; set; } //重载+运算符 public static Salary operator +(Salary s1, Salary s2) { s2.RMB += s1.RMB; return s2; } } }
相关文章推荐
- 编写高质量C#代码学习笔记(5)
- 编写高质量C#代码学习笔记(6)
- 编写高质量C#代码学习笔记(4)
- 编写高质量C#代码学习笔记(3)
- 编写高质量C#代码学习笔记(1)
- 《编写高质量代码改善C#程序的157个建议》学习
- 编写高质量代码、学习笔记——Javascript篇
- 深入理解javascript学习笔记(一) 编写高质量代码
- Effective Python:编写高质量Python代码的59个有效方法的学习笔记
- 编写高质量代码、学习笔记——CSS篇
- 编写高质量代码:改善Java程序的151个建议-学习笔记(1-4章)
- 编写高质量代码:改善Java程序的151个建议-学习笔记(5-8章)
- 编写高质量代码:改善Java程序的151个建议-学习笔记(9-12章)
- 深入理解javascript学习笔记(一) 编写高质量代码
- 编写高质量代码:改善Java程序的151个建议-学习笔记(1-4章)
- 《编写高质量代码改善Java程序的151个建议》学习笔记 第6章 枚举和注解
- C# 《编写高质量代码改善建议》整理&笔记 --(一)基本语言篇
- 编写高质量代码改善C#程序的157个建议——建议47:即使提供了显式释放方法,也应该在终结器中提供隐式清理
- 十二、编写高质量的代码——思想为源(笔记)
- 编写高质量代码改善C#程序的157个建议——建议54:为无用字段标注不可序列化