求知若饥,虚心若愚
值类型
- 值类型的传递需要拷贝内存(除非用out ref之类的关键字),所以建议不要超过16字节,拷贝引用一般也就4/8字节(取决于系统),超过拷贝引用消耗的四倍就要考虑是否能换成引用。
- 内建类型除了string和object都是值类型(结构体、枚举和常用的字面值等等)
- 好的设计实践是自定义的值类型(结构体)要使其不可变,像其他值类型一样操作不影响自身,而是返回一个新结果。(当然编译器不强求),装拆箱时会造成没有修改成功的误导。一般值类型都是内存拷贝,对拷贝进行修改并不生效还容易产生误解。
- 值类型并不总在栈上,参考资料,取决于生命周期,像闭包就会创建一个类。
结构
- 结构中的构造函数必须初始化结构中的所有字段
- 不允许在结构声明中初始化字段
- 除枚举(多了一层System.Enum)外的值类型都派生自System.ValueType,意味着结构的继承链总是从Object到System.ValueType再到结构
- 值类型的GetHashCode的默认实现是将调用转发到结构的第一个非空字段。Equals还使用了大量反射。所以如果值类型在集合中频繁使用,尤其是使用了哈希码的字典类型的集合,可考虑重写这两个函数以获得更佳性能
枚举
- 使用枚举性能和字面值差不多,但是枚举更具有可读性
- 值能转成基础类型,就能转换成枚举类型,即使没定义。(除了0能隐式转换,其他都需要显式)
- 与字符串互相转换
1
2
3
4
5
6Console.WriteLine($"Current Anim State is {AnimType.Idle}");
//AnimType animType = (AnimType) Enum.parse(typeof(AnimType), "Idle")
if(Enum.TryParse("Idle", out AnimType animType))
{
Console.WriteLine(animType);
} - 枚举作为标志使用
- 枚举值不仅独一无二,还能组合成复合值
- 关键在于位运算,每个枚举值都是位置独一的二进制位,需要用特性FlagsAttribute来标记
1
2
3
4
5
6[Flags]
public enum FileAttributes
{
ReadOnly = 1 << 0,
Hidden = 1 << 1,
}
装箱
- 值类型转换成它实现的某个接口或Object时
- 装箱会触发堆内存的分配,以及内存拷贝。而拆箱涉及类型检查和拷贝
- 要避免来回装拆箱,以装箱的结果可以多利用,最后再拆箱回原有
- 调用基类Object的方法,如果没有重写的情况下可能触发装箱