首页 > Python基础教程 >
-
C#教程之C#开发人员应该知道的13件事情(2)
将字段标记为“易变”是高级功能,即使专家也经常误解。C#编译器将确保访问字段具有获取和释放语义; 这不同于确保对该字段的所有访问都处于锁定状态。如果你不知道什么是获取和释放语义,以及它们如何影响CPU级优化,则应避免使用volatile字段。相反,应该使用较高级别的工具,如任务并行库或CancellationToken类型。
利用线程安全的内置方法
标准库类型通常提供方便线程安全访问对象的方法。例如,Dictionary.TryGetValue()。使用这些方法通常使你的代码更清洁,你不需要担心如TOCTTOU or TOCTOU场景等数据竞争的情况。
不要锁定“this”,字符串或其他常见的公共对象
当实现在多线程上下文中使用的类时,要非常小心使用锁。锁定此字符串或其他公共对象,会阻止封装锁定状态,并可能导致死锁。你需要防止其他代码锁定你的实现上正在使用的对象; 最安全的做法是使用一个私人的对象成员。
13. 避免常见错误
引用null
不适当的使用null,是编码缺陷的常见来源,可能会导致程序崩溃和其它意外行为。如果你尝试访问一个空引用,以为它是一个对象的有效引用一样 - 例如,通过访问一个属性或方法,运行时将抛出一NullReferenceException异常。
静态和动态分析工具可以帮助你在发布代码之前识别潜在的NullReferenceException异常。在C#中,空引用通常由尚未引用对象的变量引起。对于空值类型和引用类型来说,Null是一个有效值。例如,Nullable <Int>,空委托,取消订阅事件,会在“as”转换,以及在许多其他情况下失败。
每个空引用异常是都一个错误。不应该去捕获NullReferenceException,而应该尝试在使用它们之前测试对象是否为null。这也使得代码更容易被最小化try / catch块读取。
从数据库表中读取数据时,请确保,缺失值可以表示为DBNull对象,而不是空引用。不要指望它们表现的像潜在的空引用。
将十进制值替换为二进制数
浮点数和双精度表示二进制有理数,不是小数有理数,在存储十进制值时必须使用二进制的近似值。从十进制的角度来看,这些二进制近似具有不一致的舍入和精度 - 有时导致算术运算的意外结果。因为浮点运算通常在硬件中执行,硬件条件可能会不可预测地加剧这些差异。
当小数精度非常重要时,使用十进制,就像财务计算等情况。
修改结构
一个常见的错误情况是忘记结构体是值类型的,这就意味着它们被复制了并且通过值来进行传递。假设你有这样的代码:
struct P { public int x; public int y; } void M() { P p = whatever; … p.x = something; … N(p);
有一天,维护者决定将代码重构为:
void M() { P p = whatever; Helper(p); N(p); } void Helper(P p) { … p.x = something;
现在当在M()中调用N(p)时,p具有错误的值。调用助手(p)传递p的副本,而不是p的引用,因此Helper()中执行的变化将丢失。相反,Helper会返回修改的p的副本。
意外的算术
C#编译器保护你出现常量的算术溢出,但不一定是计算值。
忽略保存返回值
与结构体不同,类是引用类型,方法可以修改引用的对象。然而,不是所有的对象方法都实际修改了引用的对象,一些会返回一个新对象。当开发人员调用后者时,他们需要记住将返回值赋给变量,以便使用修改后的对象。在代码审查期间,这种类型的问题通常在会被发现。一些对象,如字符串,是不可变的,所以方法从不修改这些对象。即使如此,开发人员也会通常忘记。
例如,考虑string.Replace():
string label = “My name is Aloysius”; label.Replace(“Aloysius”, “secret”);
代码打印“我的名称是Aloysius”,因为Replace方法不修改字符串。
不要使迭代器/枚举器变得无效
不要在迭代时修改集合。
List<Int> myItems = new List<Int>{20,25,9,14,50}; foreach(int item in myItems) { if (item < 10) { myItems.Remove(item); // iterator is now invalid! // you’ll get an exception on the next iteration
如果你运行这个代码,一旦循环到集合中的下一个项目时。你会收到一个异常抛出。
正确的解决方案,是使用第二个列表来保存你要删除的项目,然后在删除时迭代该列表:
List<Int> myItems = new List<Int>{20,25,9,14,50}; List<Int> toRemove = new List<Int>(); foreach(int item in myItems) { if (item < 10) { toRemove.Add(item); } } foreach(int item in toRemove) {
或者如果你使用C#3.0或更高版本,你可以使用List <T> .RemoveAll。
就像这样:
myInts.RemoveAll(item => (item < 10));
属性名称错误
在实现属性时,请注意属性名称不同于类中使用的数据成员。在访问属性时,容易意外使用相同的名称,并导致出现无限递归的情况。
// The following code will trigger infinite recursion private string name; public string Name { get { return Name; // should reference “name” instead.
当重命名间接属性时要小心。例如,WPF中的数据绑定,会将属性名称指定为字符串。如果不小心更改该属性名称,你将会无意中创建了一个编译器无法防护的问题。
以上就是所有C#开发人员应该知道的13件事情。
了解了C#开发中应该知道的13件事情,有助于我们更好地使用C#进行开发,当然在开发时,也可以借助一些使用C#编写的开发工具。如ComponentOne Studio Enterprise,这是一款专注于企业应用的.NET全功能控件套包,支持WinForms、WPF、UWP、ASP.NET MVC等多个平台,帮助、在缩减成本的同时,提前交付丰富的桌面、Web和移动企业应用。
相关阅读:
是什么让C#成为最值得学习的编程语言
从Visual Studio看微软20年技术变迁
Visual Studio 2017正式版发布全纪录