提高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条准则

C++/CLI + C++ Native 会提高性能吗? [关闭]

是否可以动态编译和执行 C# 代码片段?

跳槽高峰期,旺季来了,提高你的代码质量