读书笔记:改善C#程序的建议1-15

建议一:正确操作字符串
(1)、确保尽量少的装箱
(2)、避免分配额外的内存空间

装箱拆箱的步骤:
1)首先会为值类型在托管堆中分配内存。除了值类型本身所分配的内存外,内存总量还要加上类型对象指针和同步块索引所占用的内存。
2)将值类型的值复制到新分配的堆内存中。
3)返回已经成为引用类型的对象的地址。

完成运行时的字符串拼接,用StringBuilder类型比String更佳。


建议二:使用默认转型方法
(1)、使用类型的转换运算符。
    隐式转换和显式转换;普通基元类型普遍都提供了转换运算符;
    (基元类型:编译器直接支持的数据类型,即直接映射到FCL中的类型。)
    用户自定义的类型也可以通过重载转换运算符的方式来提供这一类转换。


(2)、使用类型内置的Parse、TryParse,或者如ToString、ToDouble、ToDateTime等方法。
(3)、使用帮助类提供的方法。
    使用System.Convert类、System.BitConverter类来进行类型的转换;
    System.Convert支持将任何自定义类型转换为任何基元类型,只要自定义类型继承了IConvertible接口。
(4)、使用CLR支持的转型。
    基类和子类之间的相互转换;子类向基类转型的时候支持隐式转换;而基类向子类转型必须是显式转换;


建议三:区别对待强制转型与as和is

secondType = (SecondType)firstType;
两种可能:
1)FirstType和SecondType彼此依靠转换操作符来完成两个类型之间的转型;
2)FirstType是SecondType  的基类;
不可能同时既是继承关系,又提供了转型符。

基类转型为子类使用as,子类之间的转型,则应该提供转换操作符,以便进行强制转型。

as操作符永远不会抛出异常,如果类型不匹配(被转换对象的运行时类型既不是所转换的目标类型,也不是其派生类型),或者转型的源对象为null,那么转型之后的值也为null。

as操作符不能操作基元类型,如果涉及基元类型的算法,就需要通过is转型前的类型来进行判断,以避免转型失败。

建议四:TryPase比Parse好

除了string外的所有基元类型都有两个将字符串转型为本身的方法:Parse和TryParse。
1)public static double Parse(string s);
2)public static bool TryParse(string s,out double result);

如果字符串格式不满足转换的要求,Parse方法将会引发一个异常;
TryParse方法则不会引发异常,它将返回false,同时将result置为0;

Parse和TryParse方法如果执行成功,它们的效率在一个数量级上,甚至有些时候TryParse的效率比Parse高;但若执行失败Parse的执行效率相比于TryParse效率低太多;

Parse如果转换失败会报错,但是TryParse有返回值可以判断是否转换成功

建议五:使用int?来确保值类型也可以为null

Nullable<int> i1 = 4;
// i2 和 i1 的定义方式一样 只是写法不同 下面的int?是一个语法糖
int? i2 = null;
int i3 = 0;
//int类型可以默认转为int?类型
i2 = i3;
//int?类型需要强转成int类型,如果是null则变为0
i3 = (int)i2;

***
int j = i ?? 0 ; //如果i不为空,则 j = i ,否则,就给 j 赋值为0;

建议六:区别readonly和const 的使用方法

const效率高,readonly灵活性高;
const是编译器常量,readonly是运行时常量;
 
readonly所代表的运行时含义有一个重要的作用,就是可以为每个类的实例指定一个readonly的变量。

const只能修饰基元类型、枚举类型或字符串类型,readonly则没有限制;
const天然是static 不能再增加static;
readonly的值一般在构造函数里面赋值,每一个类的对象都可以拥有不同的readonly值,但由于const是静态的,所以所有的类该值都是一样的;

建议七:将0值作为枚举的默认值

允许使用枚举的类型byte 、sbyte、short、ushort、int、uint、long和ulong。
应该始终将0值作为枚举类型的默认值。

建议八:避免给枚举类型的元素提供显式的值

不确定地为枚举类型的元素设定显式的值,会带来意想不到的错误。

建议九:习惯运算符重载

在构建自己的类型时,我们应该始终考虑该类型是否可以用于运算符重载。

建议十:创建对象时需要考虑是否实现比较器

建议十一:区别对待 == 和 Equals

CLR中将“相等性”分为:“值相等性”和“引用相等性”。
“值相等性”:用来比较的两个变量所包含的数值相等;
“引用相等性”:比较的两个变量引用的是内存中的同一个对象;

两者的原则:
对于值类型,如果类型的值相等,返回True;
对于引用类型,如果类型指向同一个对象,则返回True;
两者都可以被重载;
在FCL(Framework Class Library)中,string的比较被重载为针对“类型的值”的比较,而不是针对"引用本身"的比较;

一般来说,对于引用类型,我们要定义值相等的特性,应该仅仅重写Equals方法,同时让==表示引用相等,这样我们想比较哪个都是可以的。

由于操作符“==”和“Equals”都可以被重载为“值相等”和“引用相等”,所以为了明确,FCL提供了object.ReferenceEquals(); 来比较两个实例是否为同一个引用。

建议十二:重写Equals时也要重写GetHashCode

除非考虑到自定义类型会被用作基于散列的集合的键值;否则,不建议重写Equals方法,因为这会带来一系列的问题。
如果重写Equals方法的时候不重写GetHashCode方法,在使用如FCL中的Dictionary类时,可能隐含一些潜在的Bug。
GetHashCode永远只返回一个整型类型,而整型类型的容量显然无法满足字符串的容量。
重写Equals方法的同时,也应该实现一个类型的安全的接口IEquatable<T>。

建议十三:为类型输出格式化字符串

最简单的字符串输出是为类型重写ToString方法,如果没有为类型重写该方法,默认会调用Object
的ToString方法,它会返回当前类型的类型名称。

建议十四:正确实现浅拷贝和深拷贝

浅拷贝:将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用类型的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。

深拷贝:将对象中的所有字段复制到新的对象中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。

建议十五:使用dynamic来简化反射实现

dynamic的出现让C#具有了弱语言类型的特性。编译器在编译的时候不再对类型进行检查,编译器默认dynamic对象支持开发者想要的任何特性。

var一旦被编译,编译器会自动匹配var变量的实际类型,并用实际类型来替换该变量声明,这看上去就好像我们在编码的时候就是用实际类型进行声明的。

而dynamic被编译后,实际是一个object类型,只不过编译器会对dynamic类型进行特殊处理,让它在编译期间不进行任何的类型检查,而是将类型检查放到了运行期。

使用dynamic的简化反射实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值