一、 使用readonly而不是const

const是编译时常量,readonly是运行时常量。如果引用了一个库中的const常量,则在更新了程序集,但应用程序没有重新编译时,运行结果会出错

如程序集assembly.dll中有如下声明

  1. public static readonly int Start = ;
  2. public const int End = ;

在ConsoleApp.exe中调用。在正常编译的情况下是没有问题的,但如果更新了assembly.dll中的声明,却不重新编译ConsoleApp.exe,此时End常量的值依旧是10

  1. public static readonly int Start = ;
  2. public const int End = ;

二、使用is和as关键字,而不是使用强制类型转换

强制类型转换不当会引发 InvalidCastException异常。使用is和as可以避免这个问题

三、使用$ - 字符串内插来格式化字符串,而不是string.Format()函数.这个功能需要C#6.0

https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/tokens/interpolated

四、尽量使用Conditional特性,减少#if和#endif的使用

平常我们会使用#if和#endif来执行一些Debug环境下执行的代码。这些代码在编译成Release版本时不会执行,但仍然会有一些开销。这时我们就可以使用Conditional特性,使用了Conditional特性的函数只有在某些环境或者设置了某个值之后才能编译成类的一部分,这样就可以减少Release版本的开销。值得注意的,使用Conditional特性必须把以前的调试部分代码封装成函数才行。

需要了解ConditionalAttribute更详细的说明,可以查看MSDN文档 https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.conditionalattribute?redirectedfrom=MSDN&view=netframework-4.8

使用#if和#endif的情况

当定义了DEBUG符号时,控制台会输出Debug和Release。未定义DEBUG符号时,只会输出Release。

  1. #define DEBUG
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9.  
  10. namespace ConsoleApp5
  11. {
  12. class Program
  13. {
  14. static void Main(string[] args)
  15. {
  16. #if DEBUG
  17. Console.WriteLine("Debug");
  18. #endif
  19. Console.WriteLine("Release");
  20.  
  21. }
  22.  
  23. }
  24. }

使用Conditional特性的情况

输出情况是跟上面一致的。但当未定义DEBUG符号时,DebugMethod不会被编译进MSIL。可以节省开销

值得注意的是,应用了Conditional特性的函数,返回类型只能为void

  1. #define DEBUG
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9.  
  10. namespace ConsoleApp5
  11. {
  12. class Program
  13. {
  14. static void Main(string[] args)
  15. {
  16. DebugMethod();
  17.  
  18. Console.WriteLine("Release");
  19. }
  20.  
  21. [Conditional("DEBUG")]
  22. static void DebugMethod()
  23. {
  24. Console.WriteLine("Debug");
  25. }
  26.  
  27. }
  28. }

五、如何正确的对成员变量进行初始化(这个问题困扰了我好久,今天终于有解决方案了)

大多情况下,使用成员初始化器对成员进行初始化。

就是在声明时就进行初始化,而不是放到构造函数。初始化器可以看做是构造函数中初始化语句的另一种表示。初始化器生成的代码会在构造函数前执行。如果是派生类,初始化器会在父类的构造函数前执行,然后再执行子类的构造函数。

  1. public class Patient
  2. {
  3. List<Bitmap> images = new List<Bitmap>();
  4. string patientName;
  5. int patientId;
  6.  
  7. public Patient()
  8. {
  9.  
  10. }
  11. }

下面三种情况,不推荐使用初始化器语法

1、值类型初始值设为0,引用类型初始值设为null的情况

系统默认的初始化工作将在所有代码执行之前把一切都设置成0或null。系统生成0或者null的这一步操作是位于很底层的实现,会直接使用CPU指令将一整块内存设置为0。

所以以下代码是多余的

  1. int num = ;
  2. Student stu = null;

2、需要对这个变量执行不同的初始化方式的情况。初始化器语法的前提是,所有的构造函数都会将变量设置为同样的值。

3、在进行初始化时,可能会引发异常的情况。这种情况下,就需要在构造函数中进行初始化,以便进行相应处理。

六、区分值类型和引用类型

值类型应该是那些小型、轻量级的类型,分配在栈上

引用类型则用来构成类层次结构,分配在堆上。

有如下代码

  1. MyType[] arrayOfTypes = new MyType[];

如果MyType属于值类型,那么将一次分配大小为MyType的100倍空间

如果MyType属于引用类型,那么也仅会分配一次,数组中的每个元素都为空。在初始化数组中的元素时,将执行101次分配,101次分配要比1次分配更占用时间,分配大量的引用 类型将让堆上充满碎片。

还有一点值得注意的是,在集合中查找元素时,如果集合里是值类型,则查找出来的元素跟集合中的元素是不同的引用。如果是引用类型,则查找出来的元素跟集合中的该元素是相同的引用。在修改值的时候,会导致意外的发生。

有如下代码

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Test1 test1 = new Test1() { Name = "Test1"};
  6. Test2 test2 = new Test2() { Name = "Test2"};
  7.  
  8. List<Test1> list1 = new List<Test1>() { test1 };
  9. var temp1 = list1.Find(x => x.Name == "Test1");
  10. Console.WriteLine(object.ReferenceEquals(test1,temp1));
  11.  
  12. temp1.Name = "Modified Test1";
  13. Console.WriteLine(list1.ElementAt().Name);
  14.  
  15. List<Test2> list2 = new List<Test2>() { test2 };
  16. var temp2 = list2.Find(x => x.Name == "Test2");
  17. Console.WriteLine(object.ReferenceEquals(test2,temp2));
  18.  
  19. temp2.Name = "Modified Test2";
  20. Console.WriteLine(list2.ElementAt().Name);
  21.  
  22. }
  23. }
  24.  
  25. struct Test1
  26. {
  27. public string Name { get; set; }
  28. }
  29.  
  30. class Test2
  31. {
  32. public string Name { get; set; }
  33. }

运行结果如下

可以看到,查找出来的值类型集合中的元素,跟添加时的元素并不是同一个引用,而且修改值,也不会导致集合中元素的更改。

引用类型则相反。

C#高效编程的更多相关文章

  1. C# 高效编程笔记2

    C# 高效编程笔记2 1.理解GetHashCode()的陷阱 (1)作用:作为基于散列集合定义键的散列值,如:HashSet<T>,Dictionary<K,V>容器等 (2 ...

  2. C# 高效编程笔记1

    C# 高效编程笔记1 1.使用属性而不是可访问的数据成员 (1).NET Framework中的数据绑定类仅支持属性,而不支持共有数据成员 (2)属性相比数据成员更容易修改 2.用运行时常量(read ...

  3. Python高效编程的19个技巧

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求.python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的不行不行的,终于解脱了.高级语言,如果做 ...

  4. 架构师速成-如何高效编程 for java

    引子 赵云大喝一声,挺枪骤马杀入重围,左冲右突,如入无人之境.那枪浑身上下,若舞梨花:遍体纷纷,如飘瑞雪. 赵云是所有历史人物中我最喜欢的一个,如果放到现代,他走了it的道路,一定可以成为一个编程高手 ...

  5. C语言高效编程的几招(绝对实用,绝对经典)

    编写高效简洁的C语言代码,是许多软件工程师追求的目标.废话不说,走起! 第一招:以空间换时间 计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题 eg.字符串的 ...

  6. [收藏转贴]struct探索·extern "C"含义探索 ·C++与C的混合编程·C 语言高效编程的几招

    一.C/C++语言 struct深层探索 1.自然对界 struct是一种复合数据类型,其构成元素既可以是基本数据类型(如 int.long.float等)的变量,也可以是一些复合数据类型(如 arr ...

  7. [.NET] 《C# 高效编程》(一) - C# 语言习惯

    C# 语言习惯 目录 一.使用属性而不是可访问的数据成员 二.使用运行时常量(readonly)而不是编译时常量(const) 三.推荐使用 is 或 as 操作符而不是强制类型转换 四.使用 Con ...

  8. [.NET] 《Effective C#》快速笔记 - C# 高效编程要点补充

    <Effective C#>快速笔记 - C# 高效编程要点补充 目录 四十五.尽量减少装箱拆箱 四十六.为应用程序创建专门的异常类 四十七.使用强异常安全保证 四十八.尽量使用安全的代码 ...

  9. 【Matlab编程】Matlab高效编程技巧

    1.默认状态下,matlab显示精度是short型,而默认的计算精度是double型,并且显示精度与计算精度没有关系. 2. 一只失明的猫的问题:注意方法! 3.给数组预分配空间是基本的高效编程准则之 ...

  10. 18个Python高效编程技巧,Mark!

    初识Python语言,觉得python满足了我上学时候对编程语言的所有要求.python语言的高效编程技巧让我们这些大学曾经苦逼学了四年c或者c++的人,兴奋的不行不行的,终于解脱了.高级语言,如果做 ...

随机推荐

  1. linux 定时任务 cron,利用cron进程保活

    cron定时任务 crond服务操作命令 /sbin/service crond start //启动服务 /sbin/service crond stop //关闭服务 /sbin/service ...

  2. Element + Vue I18n动态import加载国际化语言包翻译文件

    需求 项目为多页应用,包含产品a.b.c.d.e,每个产品都有自己的翻译文件.一次加载所有翻译文件是极度不合理的.于是考虑动态加载. 实现 参考官方文档:延迟加载翻译 项目结构 │ ├── dist ...

  3. MySql删除重复数据并保留一条

    DELETE FROM tbl_1 WHERE id NOT IN( SELECT id FROM ( SELECT min(id) AS id FROM tbl_1 GROUP BY `duplic ...

  4. Java NIO Buffer详解

    一.ByteBuffer类型化的put与get方法 /** * ByteBuffer类型化的put与get方法 */ public class NioTest5 { public static voi ...

  5. Python3基础 tuple 使用通配符*进行拆包 简单示例

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  6. Linux下查看根目录各文件内存占用情况

    一.服务器运行一点时间后各种的项目文件,日志文件,数据库备份登,会越来越多,在linux下可以使用 du 和 df 命令查看. 1.df -h 命令查看整体磁盘使用情况 2. 使用 du -ah -- ...

  7. 超简易简易PHP爬虫

    利用CURL和DOMDocument.通过xpath筛选数据,实现的简易PHP爬虫 <?php header('Content-type: text/plain; charset=utf-8') ...

  8. 中国大互联网公司在github上的开源项目

    公司名 账号数 账号名 总项目数 非fork项目数 百度 13 baidu.ApolloAuto. brpc. mipengine.Clouda-team.mesalock-linux. ecomfe ...

  9. matlab学习笔记12单元数组和元胞数组 cell,celldisp,iscell,isa,deal,cellfun,num2cell,size

    一起来学matlab-matlab学习笔记12 12_1 单元数组和元胞数组 cell array --cell,celldisp,iscell,isa,deal,cellfun,num2cell,s ...

  10. 经典面试题之——如何自由转换两个没有继承关系的字段及类型相同的实体模型,AutoMapper?

    相信很多童鞋们都被问到过这个问题,不管是在面试的时候被问过,还是笔试题里考过,甚至有些童鞋们找我要学习资料的时候我也考过这个问题,包括博主我自己,也曾被问过,而且博主现在有时作为公司的面试官,也喜欢问 ...