C# 中几个小“陷阱”

每天写代码,偶尔就会有让你抓狂的时候:代码改了千百遍,蓦然回首,Bug就在灯火阑珊处……这里就列举一些容易犯错的几个小地方,以后遇到了其他的,再慢慢添加。

1. 获取程序当前运行路径
情景复现:WPF客户端程序,开机自启动后无法进入主界面,卡在初始屏(Splash Screen)
处理问题:通过日志发现加载一个icon的时候,跳了一个Bug。初始代码如下:
- var icon = new Icon("Images\\xxx.ico");
很简单,貌似不会有问题,相对目录且正确。直接双击程序启动完全正常,Debug启动同样完全正常,否则早就发现这个Bug了。开机自启动时日志中的错误是:找不到“C:\Windows\System32\Images\xxx.ico”这个文件 ??? 这很让人摸不着头脑,程序中的相对目录怎么会跑到sysem32里面了?目录不对导致文件找不到,当然就进入到Exception里面了。
第一反应是相对目录可能不带靠谱,就改成了下面的代码:
- var icon = new Icon(Directory.GetCurrentDirectory() + "\\Images\\xxx.ico");
- //var icon = new Icon(Environment.CurrentDirectory + "\\Images\\xxx.ico");
呵呵,还是不起作用,换一种写法(被注释的第二句),报的错是一样的。两个方法返回的都是“C:\Windows\System32”这个路径,在程序开机自启动的时候。其实Environment.CurrentDirectory内部调用的也是Directory.GetCurrentDirectory()方法。
解决方案:StackOverflow上面关于这个问题有个讨论,WinForm中Application.StartupPath也会有相同的问题,下面的是获取当前目录的推荐写法:
- var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
2. IEnumerable之LINQ 表达式
软件设计有个很重要的原则,就是高内聚,低耦合,于是我们经常的会面向接口编程,并且尽可能开放底层接口。所以IEnumerable就会经常用到,因为Linq操作中很多方法的返回值都是IEnumerable<T>。这一个陷阱就是关于IEnumberable,先看下面的代码,看看返回值和你想的是不是一样:


2 {
3 private static void Main(string[] args)
4 {
5 //...
6 var students = GetStudents();
7 foreach (var student in students)
8 {
9 student.IsActived = true;
Console.WriteLine(student.IsActived);
}
foreach (var student in students)
{
Console.WriteLine(student.IsActived);
}
}
private static IEnumerable<string> GetNames()
{
//....
return new[] { "AAA", "BBB", "CCC" };
}
private static IEnumerable<Student> GetStudents()
{
//...
return GetNames().Select(s => new Student(s));
}
public class Student
{
public string Name { get; set; }
public bool IsActived { get; set; }
public Student(string name)
{
Name = name;
}
}
}
第一个foreach里面会输出3个true,这毫无疑问,第二个foreach里面任然会输出3个true?

如果你理解这样的结果,那么这个“陷阱”对你无效,你可以跳过这一条了……
继续看下面的代码:


studentList[].IsActived = true;
var studentsActived = studentList.Where(s => s.IsActived);
Console.WriteLine(studentsActived.Count());
studentList[].IsActived = false;
Console.WriteLine(studentsActived.Count());
这次会输出什么?

If(IsUnderstandingAgain) return; else……这里先不解释,我们用代码说话,继续看代码,修改一下GetStudents()方法:
- 1 private static IEnumerable<Student> GetStudents()
- 2 {
- 3 //...
- 4 return GetNames().Select(s =>
- 5 {
- 6 var stu = new Student(s);
- 7 Console.WriteLine(s + ": " + stu.GetHashCode());
- 8 return stu;
- 9 });
- }
在上面的代码中,GetStudent()方法被调用了2次,你觉得现在HashCode会输入几次?

输出结果是9次。区域3里面的3次是由于调用GetStudents().ToList()方法,区域1和2则是由前面的两个foreach运行时输出的,而且每一次HashCode都不一样,说明每一个都是不同的实例。再联想一想Entity Framewor里面是不是有一个Lazy Loading,每一次使用集合中的某个对象,就会执行一次SQL,从数据库中查找该对象。 真相就在这里,Llinq只是表达式(这里用的都是lambda写法),可以这么理解:每个表达式它会自动生成一个匿名方法,只有在需要结果的时候这个匿名方法才会去执行,这也就是为什么它的返回值是IEnmerable<T>而不是一个具体的类。 所以在需要全部所需集合时,最好先执行ToList(),ToDictionary()这类方法,生成真正的结果。
C# 中几个小“陷阱”的更多相关文章
- T-SQL中的一些小陷阱
1,当心ISNULL函数对你的逻辑引起BUG 有人喜欢或者习惯于(并不代表我推荐,甚至这种写法没有任何好处)用ISNULL处理变量这种方式写查询 比如:select * from TestISNULL ...
- java常量池中基本数据类型包装类的小陷阱
想必大部分学过java的人都应该做过这种题目: public class Test { public static void main(String[] args) { //第一个字符串 String ...
- C++ string中的几个小陷阱,你掉进过吗?
C++开发的项目难免会用到STL的string,使用管理都比char数组(指针)方便的多,但在得心应手的使用过程中也要警惕几个小陷阱,避免我们项目出bug却迟迟找不到原因. 1. 结构体中的stri ...
- Unity 3D中C#的性能优化小陷阱
本篇内容主要来自Unity官方手册: 一般性能优化 一些地方为本人瞎编杜撰,请酌情参考.如有错误,欢迎指出. Unity里C#编程虽然既简单还很爽,但是性能小陷阱还不少.我总强迫自己让代码最优,因此很 ...
- ESXi与物理交换机静态链路聚合配置过程中的小陷阱
作者:陆斌文章来自微信公众号:平台人生 内容简介:ESXi与物理交换机之间配置静态链路聚合时,因为静态链路聚合的特点,在进行down网卡和从虚拟交换机移除网卡的操作时,可能会无法完成故障流量切换,影响 ...
- Python中定义函数时参数有默认值的小陷阱
在定义函数的时候,如果函数的参数有默认值,有两种类型的参数,一种是整数,字符串这种不可变类型,另一种是列表这种可变类型,对于第一种情况没有什么特殊的地方,但是对于可变类型,有一个微妙的小陷阱. 可变类 ...
- [LeetCode] Kth Smallest Element in a Sorted Matrix 有序矩阵中第K小的元素
Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth ...
- 数组中第K小的数字(Google面试题)
http://ac.jobdu.com/problem.php?pid=1534 题目1534:数组中第K小的数字 时间限制:2 秒 内存限制:128 兆 特殊判题:否 提交:1120 解决:208 ...
- 九度OJ 1534 数组中第K小的数字 -- 二分查找
题目地址:http://ac.jobdu.com/problem.php?pid=1534 题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C. 譬如A为[1,2],B为[ ...
随机推荐
- 线程池ThreadPoolExecutor、Executors参数详解与源代码分析
欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. ThreadPoolExecutor数据成员 Private final Atom ...
- C# LINQ需求实现演化
Linq是C#3.0引入的,在C#2.0实现从集合中过滤符合条件的记录实现方式. 假设有一个Book类,以及一个Book类的集合,现在需要从集合中查找出单价大于50的Book. 1.固定查询字段的实现 ...
- 多个Jar包的合并操作
原文:http://www.cnblogs.com/meteoric_cry/p/4283656.html 需求是将多个jar合并成一个jar的问题.这里列一下操作步骤: 1.将所有jar文件复制至某 ...
- maven -- 学习笔记(一)之maven环境搭建
首先先感谢博主的分享http://www.cnblogs.com/yjmyzz/p/3495762.html 基本概念: Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建 ...
- redis java对象操作
使用Jedis客户端 1. java 对象,需序列化 public class Person implements Serializable { private int id; private Str ...
- [New Portal]Windows Azure Virtual Machine (15) 在本地制作数据文件VHD并上传至Azure(2)
<Windows Azure Platform 系列文章目录> 在上一章内容里,我们已经将包含有OFFICE2013 ISO安装文件的VHD上传至Azure Blob Storage中了. ...
- 基于HTML5的WebGL结合Box2DJS物理引擎应用
上篇我们基于HT for Web呈现了A* Search Algorithm的3D寻路效果,这篇我们将采用HT for Web 3D来呈现Box2DJS物理引擎的碰撞效果,同上篇其实Box2DJS只是 ...
- Fundamentals of MVC
Chapter 1 Fundamentals of MVC Microsoft's Web Development Platforms Understanding the past can b ...
- vue之自定义指令directive
<template> <div> <input v-model="dir1" v-my-directive1="dir1"/> ...
- C语言学习011:带参数的main函数
直接上代码 #include <stdio.h> int main(int argc,char *argv[]){ printf("%i \n",argc); int ...