C#代码精简优化技巧总结

工具

俗话说,工欲善其事必先利其器,使用得心应手的工具必然会提高开发效率,做微软平台开发的肯定离不开VS,就VS本身来说,除了常用功能外一些常用的快捷键一定要能熟练运用,例如下面是我认为比较有用的几个快捷键:

  • 注释: Ctrl + K + C
  • 取消注释: Ctrl + K + U
  • 全屏: Shift + Alt + Ente
  • 设置标签: CTRL + K, CTRL + K
  • 下一个、上一个标签: CTRL + K, CTRL + P 、CTRL + K, CTRL + P
  • 列出成员: Ctrl + J
  • 显示参数信息: Ctrl + Shift + Space
  • 转到定义后返回: Ctrl + -

 

熟练使用快捷键对于代码编写的速率和跟踪代码的速率会有大大的提高。 有时候开发工具自身的功能受到了限制,这是就需要使用插件来丰富功能,这里推荐两款插件,VS中的ReSharperSqlServer中的SQL prompt5ReSharper是功能很强大的一个VS插件,但会拖慢VS的速度,就看怎么去权衡了。

 

代码质量 

代码质量好了,产生的bug就少,和测试的交互也就少了,也就不会因为前面产生的bug而影响后面的进度,效率自然就高了。代码质量可以分三个方面来看: 1 代码出错少,能够正常的运行;

  • 主动学习,提升自我的编程技能;
  • 勤思考,对干过的错要经常总结,一些规范性的原则要牢记,这些常常会出现一些低级错误;
  • 一个任务做完后需要进行充分的自测。

 

2 代码的运行效率高,在大数据、高并发的时候能够高效运行;

  • 高性能的开发得从点滴做起,不放过每一个细节,可能一个小的细节点就是一个性能的瓶颈;
  • 要有重构代码的习惯,好的代码是重构出来的,高性能的代码也是重构出来的;
  • 多学习一些原理性的知识,不光要知其然还是知其所以然,基础扎实了,一些性能的问题就知道怎么去优化了;

 

3 代码最后的运行结果要和客户的要求一致;

  • 做需求之前把自己的理解跟需求分析进行沟通看是否能达成一致,如果是直接和客户进行沟通可以先做出小Demo,然后给客户演示,根据反馈不断改进;
  • 在做的过程中如果遇到有疑问的地方一定要和需求或客户进行沟通,不要根据自己的想法想当然的去进行代码编写;
  • 必要的时候可以引导客户,我们的主要目的能以最有效的方式帮客户解决问题,不能盲目的按照客户的要求来,有时客户说需要一双雨鞋,可能一把伞就可以解决问题。同样对于需求分析写的文档,开发也需要有质疑的精神。

 

业务知识学习

做任何的系统都避免不了有业务背景,熟练的了解业务知识可以使我们更清楚的知道我们是在做什么。很多的开发人员可能只喜欢钻研技术,对业务往往没什么兴趣,代码写完了,可能还不知道做出的模块时做什么用的,这样写出来的代码的质量就可想而知了。

  • 学习业务可能很枯燥,但却是一劳永逸的事情,所以不管是否有兴趣,还是应该硬着头皮啃下来;
  • 小组内可以成立兴趣小组,探讨的方式来进行学习,互相分享各自的学习内容,关键是组内的氛围要搞起来;
  • 如果是直接跟客户沟通,需要用客户能听懂的语言,比如图文配合或是一些小Demo,否则当开发术语碰上领域术语就可能都是在对牛弹琴了。

 

 

 

1 .空操作符(??

在程序中经常会遇到对字符串或是对象判断null的操作,如果为null则给空值或是一个指定的值。通常我们会这样来处理

string name = value;
if (name == null)
{
    name = string.Empty;
}

可以使用三元操作符(?:)对上面对吗进行优化

string name = value == null ? string.Empty : value;

这样使代码简洁了不少,但这还不是最简洁的,我们还可以使用??操作符来进行进一步优化,??操作符意思是如果为null取操作符左边的值,否则取右边的值。

string name = value ?? string.Empty;

我们甚至可以写一个扩展方法来过滤掉null和空格,使返回的结果可以更好的使用??操作符

public static class StringUtility
{
    public static string TrimToNull(string source)
    {
        return string.IsNullOrWhiteSpace(source) ? null : source.Trim();
    }
}

使用代码如下:

string name = string.TrimToNull(value) ?? "None Specified";


 

2. 使用As转换类型

C#中进行类型转换有很多种方式比如可以进行强制类型转换,通常在转换前会使用Is进行类型的判断,所以您可能经常写过或见过类似下面的代码

if (employee is SalariedEmployee)
{
    var salEmp = (SalariedEmployee)employee;
    pay = salEmp.WeeklySalary;
    // ...
}

上面的代码不会报异常,但在整个过程中做了两次转换操作,这样会降低性能。我们可以使用as操作符来进行类型的转换,同样也不会报异常,如果类型不兼容则返回null,而是用as进行转换整个过程只转换一次。代码如下:

var salEmployee = employee as SalariedEmployee;
if (salEmployee != null)
{
    pay = salEmployee.WeeklySalary;
    // ...
}

3.string.IsNullOrEmpty() and string.IsNullOrWhiteSpace()

Net2.0String类型有一个静态方法IsNullOrEmpty,到了Net4.0String类又增加了一个新的静态方法IsNullOrWhiteSpace。这两个方法看名称也可以知道IsNullOrEmpty是判断空引用和空字符串,而IsNullOrWhiteSpace是判断空引用和字符串中的每一个字符是否是空格。

在有这两个方法之前,我们要进行这样的判断,需要些如下代码

public string GetFileName(string fullPathFileName)
{
    if (fullPathFileName == null || fullPathFileName.Length == 0)
    {
        throw new ArgumentNullException(fullPathFileName);
    } 
    //...
} 

使用IsNullOrEmpty

public string GetFileName(string fullPathFileName)
{
    if (string.IsNullOrEmpty(fullPathFileName))
    {
       
      throw new ArgumentNullException(fullPathFileName);
    } 
    //...
} 

下面又了新的需求,需要将三个名字连接在一起,并且希望中间名字不为空字符串和不出现多余的空格,我们会写出下面的代码

public string GetFullName(string firstName, string middleName, string lastName)
{
    if (middleName == null || middleName.Trim().Length == 0)
    {
        return string.Format("{0} {1}", firstName, lastName);
    }
    return string.Format("{0} {1} {2}", firstName, middleName, lastName);
} 

上面的代码中使用了Trim来去掉空格然后判断其长度是否为0,代码也非常的清晰简洁,但是会产生额外的String对象以至于影响性能,这时就应该使用Net4.0中的IsNullOrWhiteSpace方法

public string GetFullName(string firstName, string middleName, string lastName)
{
    if (string.IsNullOrWhiteSpace(middleName))
    {
        return string.Format("{0} {1}", firstName, lastName);
    }
    return string.Format("{0} {1} {2}", firstName, middleName, lastName);
} 

上面的代码非常简洁,而且也不用担心会产生额外的String对象没有及时的进行垃圾回收而影响性能。

4.自动属性

public class Point
{
    private int _x, _y;
 
    public int X
    {
        get { return _x; }
        set { _x = value; }
    }
    public int Y
    {
        get { return _y; }
        set { _y = value; }
    }
}

使用自动属性代码就会简洁了很多

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

在自动属性中,我们可以给getset访问器设置访问级别,使属性变成只读属性或是只写属性

public class Asymetrical
{
    public string ThisIsReadOnly { get; private set; }
    public double ThisIsWriteOnly { private get; set; }
}

 

5.StopWatch

在程序开发中有时会需要统计一个方法或是一个存储过程执行了多长时间,比如在做一些方法的性能测试时就需要用到这用的时间统计功能,很自然想到的方法是在处理的方法前后各记录一个时间,然后计算时间差,如下

DateTime start = DateTime.Now;
SomeCodeToTime();
DateTime end = DateTime.Now;
Console.WriteLine("Method took {0} ms", (end - start).TotalMilliseconds);

尽管使用DateTime的时间差可以达到目的,但DateTime统计出来的时间差并不是很精确,想要精确我们可以使用Win32 API调用PInvoke,但是做法非常麻烦而且容易出错。

这时我们就需要使用StopWatch类了,使用这个类必须引用命名空间 System.Diagnostics

var timer = Stopwatch.StartNew();
SomeCodeToTime();
timer.Stop();
Console.WriteLine("Method took {0} ms", timer.ElapsedMilliseconds);

 

6.string.Equals()

string.Equals方法有很多的重载供我们使用,但是其中有些常常会被我们忽视掉。通常我们比较字符串会使用下面的方法

public Order CreateOrder(string orderType, string product, int quantity, double price)
{
    if (orderType.Equals("equity"))
    {
    }
    // ...
}   

如果orderTypenull会抛出NullReferenceException异常,所以为了不抛出异常,在判断之前先要进行null的判断,如下:

if (orderType != null && orderType.Equals("equity"))

相当于每次都要做两次判断,很麻烦而且有时还有可能遗忘,如果使用string.Equals就可以解决这个问题,代码如下:

if (string.Equals(orderType, "equity"))

上面的代码当orderTypenull时不会抛出异常而是直接返回false

判断字符串相等的时候有时会需要区分大小写,很多人的习惯做法是先转换成大小或是小些在进行比较(建议转换成大写,因为编译器做了优化可以提高性能),但是当转换成大写或是小写时又会创建的的字符串,使性能降低。这时应该使用StringComparison.InvariantCultureIgnoreCase,代码如下

if (orderType.Equals("equity", StringComparison.InvariantCultureIgnoreCase))

如果要考虑到null的情况,还是应该使用string.Equal

if (string.Equals(orderType, "equity", StringComparison.InvariantCultureIgnoreCase))

7.using语句

我们都知道using最常用的地方就是在类中引用命名空间。除此之外还可以用作设置别名和应用在一些实现了IDisposable 借口的对象实例上,可以使这些对象在using的作用范围内自动释放资源。下面的代码示例是没有使用using的情况:

public IEnumerable<Order> GetOrders()
{
    var orders = new List<Order>();
    var con = new SqlConnection("some connection string");
    var cmd = new SqlCommand("select * from orders", con);
    var rs = cmd.ExecuteReader();
    while (rs.Read())
    {
        // ...
    }
    rs.Dispose();
    cmd.Dispose();
    con.Dispose();
    return orders;
} 

上面的代码不怎么好看,而且也没有对异常的处理,如果在代码执行过程中出现了异常将会导致有些资源不能及时释放,尽管最终还是会被垃圾回收,但还是会影响性能呢。
 

public IEnumerable<Order> GetOrders()
{
    var orders = new List<Order>();
 
    using (var con = new SqlConnection("some connection string"))
    using (var cmd = new SqlCommand("select * from orders", con))
    using (var rs = cmd.ExecuteReader())
    {
        while (rs.Read())
        {
            // ...
        }
    }
    return orders;
} 

8.对象和集合初始化器

C#3.0及以上版本中增加了对象和集合初始化器的新特性,会使代码看起来更加简洁,还有可能带来更高的性能。初始化器其实就是一个语法糖。看下面的例子,给出的是一个结构

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
} 

普通初始化如下

var startingPoint = new Point();
startingPoint.X = 5;
startingPoint.Y = 13;

使用初始化器初始化

var startingPoint = new Point() { X = 5, Y = 13 };

代码的确精简了不少,从三行减到了一行,可以让我们少敲很多字。

下面再来看看集合的初始化,假设我们在一个集合List中添加5个整数

var list = new List<int>();
list.Add(1);
list.Add(7);
list.Add(13);
list.Add(42);

使用集合初始化器,代码如下

var list = new List<int> { 1, 7, 13, 42 };

下面来看一个通常情况下对象和集合一起使用的例子

var list = new List<Point>();
var point = new Point();
point.X = 5;
point.Y = 13;
list.Add(point);
point = new Point();
point.X = 42;
point.Y = 111;
list.Add(point);
point = new Point();
point.X = 7;
point.Y = 9;
list.Add(point); 

下面为使用了初始化器的代码,可以对比一下区别

var list = new List<Point>
{
    new Point { X = 5, Y = 13 },
    new Point { X = 42, Y = 111 },
    new Point { X = 7, Y = 9 }
}; 
9.System.IO.Path

Net中的System.IO.Path方法有很多的静态方法来处理文件和路径。很多时候我们尝试手动的将路径和文件名结合在一起而导致产生的文件路径不可用,因为我们往往忽视了路径后面可能有一个结尾符号‘\’。现在使用Path.Combine()方法可以避免这种错误

string fullPath = Path.Combine(workingDirectory, fileName);

假设现在有一个带文件名的完整的路径名,我们需要取其中的路径、文件名或是文件的扩展名。Path类的很多静态方法可以满足我们的需要,如下

string fullPath = "c:\\Downloads\\output\\t0.html";
// gets "c:\"
string pathPart = Path.GetPathRoot(fullPath);
// gets "t0.html"
string filePart = Path.GetFileName(fullPath);
// gets ".html"
string extPart = Path.GetExtension(fullPath);
// gets "c:\downloads\output"
string dirPart = Path.GetDirectoryName(fullPath);

所以当我们遇到这种需要对文件路径进行操作时,我们可以去使用Path类的静态方法。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值