提高C#质量与性能
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了提高C#质量与性能相关的知识,希望对你有一定的参考价值。
这篇随笔,用来记录提高C#质量和性能的方法。
基本
使用字符串应避免两种性能开销。
1.确保尽量少的装箱:值类型转换引用类型,会进行装箱操作。但是值类型的ToString(),是非托管方法,直接操作内存进行转换。故此不会进行装箱操作。9.ToString() 性能高于 string s = 9;
2.避免分配额外的内存空间:因字符串不可变性,对字符串进行任何操作或运算都会创建新对象,需要分配新的内存空间。使用常量进行+ 拼接,不会分配内存空间。尽量使用StringBuilder。string.Format方法在内部使用StringBuilder进行字符串的格式化。
默认转型方法:大部分情况下需要对FCL提供的类型进转型时,都应该使用FCL提供的转型方法。
1.使用类型的转换运算符:分为两类 隐式转换、显示转换。基元类型普遍都提供了转换运算符。
2.使用类型内置的方法:Parse、TryParse、ToString、ToDouble、TODateTime等
3.使用帮助类提供的方法:System.Convert、System.BitConverter
4.使用CLR支持的转型:子类转换成父类,支持隐式转换。父类转换成子类,必须显示转换。
强制转型与as、is
类型与类型之间的强制转型,存在两种互斥的事情
1.彼此依靠转换操作符来完成两个类型之间的转型。如果存在转换操作符,则应使用显式转换或隐式转换。
2.基类与派生类。如果是基类转为子类本身,应该使用as 操作符。使用is 操作符,可以判断是否能转换。从效率上讲 as 操作符,优先于强制转换。
TryParse比parse好:如果可能引发异常,TryParse的效率远大于Parse。如果方法异常会带来明显的性能损耗时,使用TryParse。
数据库写入中,使用int? 来保证值类型可为空。因数据库字段 存在非必填的情况。而值类型不能为null。使用??运算符进行可空类型的赋值:int? i = 123; int j = i ?? 0;
const 效率高于 readonly。const是编译时的常量,readonly是运行时的常量。
开发过程中,应习惯于使用重载操作符的这一特性。例如:重载+操作符,比使用Add方法更加一目了然。
int x = 1; int y = 2; int total = int.Add(x, y); int total = x + y;
创建对象需要考虑是否自定义排序
public sealed class Program { static void Main(string[] args) { List<Salary> companySalary = new List<Salary> { new Salary{ Name="M",BaseSalary=3000,Bouns = 1000}, new Salary{ Name="R",BaseSalary=2000,Bouns = 4000}, new Salary{ Name="J",BaseSalary=1000,Bouns = 6000}, }; companySalary.Sort(); //使用默认比较器,就是集成IComparable接口的比较器 companySalary.Sort(new BonusComparer()); //使用自定义比较器 foreach (Salary item in companySalary) { Console.WriteLine(item.Name); } Console.ReadLine(); } } public class Salary : IComparable<Salary> { public string Name { get; set; } public int BaseSalary { get; set; } public int Bouns { get; set; } public int CompareTo(Salary other) { return BaseSalary.CompareTo(other.BaseSalary); } } public class BonusComparer : IComparer<Salary> { public int Compare(Salary x, Salary y) { return x.Bouns.CompareTo(y.Bouns); } }
可以设置默认比较器,也可以设置自定义比较器。继承接口以后,可以进行定义排序操作。
对于值类型,如果类型的值相等,就应该返回True。
对于引用类型,如果类型指向同一个对象,则返回True。但string是一个特殊引用类型,他的比较被重载为针对 类型的值 的比较。而不是针对引用本身。
浅拷贝:将对象中所有的字段复制到新的对象中,值类型不会影响到元对象。引用类型会。浅拷贝会将基元类型string,也进行深拷贝。
深拷贝:将对象中所有的字段赋值到新的对象中。无论是值类型还是引用类型,都会重新赋值。
使用dynamic来简化反射
dynamic dy = Activator.CreateInstance(Type.GetType("ConsoleApplication1.DynamicSample")); Console.WriteLine(dy.Add(1,2));
使用dynamic效率会比普通反射高很多。不过dynamic没有智能提示。推荐如果需要反射,则使用dynamic来进行。
集合和LINQ
元素数量可变的时候,不应该使用数组。
数组在创建的时候,会被分配一个固定长度的内存。一旦被分配就不能进行改变。唯一能改变的方法就是转换泛型。所以在元素数量可变的情况下,应使用泛型操作。
多数情况下使用foreach进行循环遍历
因为遍历模式可能多种,比如说根据索引、根据键值。如果它的遍历没有一个公共的接口,那么客户端相当于对具体类型进行了编码。这样就违反了开闭原则。所以多数情况下应考虑实现迭代器模式。
foreach不支持循环时对集合进行增删操作。当遇见需要对循环集合进行增删操作的时候,应使用for循环。
应使用更有效的对象和集合的初始化器。
使用泛型集合代替非泛型集合。泛型集合效率远高于非泛型集合。
选择正确的集合
如果集合的数目是固定并且不涉及转型,使用数组效率高,否则就使用List泛型。
队列Queue<T> 遵循先入先出的模式。它在集合的末尾添加元素,在起始删除元素。可以用来处理并发命令场景。
栈Stack<T>遵循后入先出的模式。它在集合末尾添加元素,在集合末尾删除元素。
字典Dictionary<TKey,TValue>存储的是键值对。值在基于键的散列码的基础上进行存储。
双向链表LinkedList<T>双向链表每个节点都向前指定Previous节点,向后指定Next节点。
三种有序排列
SortedList<T> 对应 List<T>
SortedDictionary<TKey,TValue> 对应 Dictionary<TKey,TValue>
SortedSet<T> 对应 HashSet<T>
四种集合类,如果集合应用于多线程应用中,可以使用保证线程安全
ConcurrentBag<T> 对应 List<T>
ConcurrentDictionary<TKey,TValue> 对应 Dictionary<TKey,TValue>
ConcurrentQueue<T> 对应 Queue<T>
ConcurrentStack<T> 对应 Stack<T>
结束占位符
以上是关于提高C#质量与性能的主要内容,如果未能解决你的问题,请参考以下文章
《Effective C#》提炼总结提高Unity中C#代码质量的22条准则
UnityEffective C#观后感之提高Unity中C#代码质量的21条准则
UnityEffective C#观后感之提高Unity中C#代码质量的21条准则