我最近正在使用一个DateTime对象,并写了这样的东西:
DateTime dt = DateTime.Now;
dt.AddDays(1);
return dt; // still today's date! WTF?
AddDays()的智能感知文档说它在日期后添加了一天,但它并没有这样做——它实际上返回了一个添加了一天的日期,所以你必须这样写:
DateTime dt = DateTime.Now;
dt = dt.AddDays(1);
return dt; // tomorrow's date
这个问题以前已经困扰过我很多次了,所以我认为将最糟糕的c#陷阱分类会很有用。
可枚举对象可以计算不止一次
当您有一个惰性枚举的可枚举对象,并且对其进行两次迭代并得到不同的结果时,它会咬您一口。(或者你得到相同的结果,但它执行两次不必要的)
例如,在编写某个测试时,我需要一些临时文件来测试逻辑:
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName());
foreach (var file in files)
File.WriteAllText(file, "HELLO WORLD!");
/* ... many lines of codes later ... */
foreach (var file in files)
File.Delete(file);
想象一下当file . delete (file)抛出FileNotFound时我的惊讶!!
这里发生的情况是,可枚举的文件被迭代了两次(第一次迭代的结果根本没有被记住),在每次新的迭代中,您将重新调用Path.GetTempFilename(),因此您将得到一组不同的临时文件名。
当然,解决方案是使用ToArray()或ToList()快速枚举值:
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName())
.ToArray();
当你在做一些多线程的事情时,这甚至更可怕,比如:
foreach (var file in files)
content = content + File.ReadAllText(file);
你会发现内容。在所有写入之后,长度仍然为0 !!然后,当....时,您开始严格检查是否没有竞态条件浪费了一个小时之后……你发现这只是一个微小的可枚举的东西,你忘记了....
可枚举对象可以计算不止一次
当您有一个惰性枚举的可枚举对象,并且对其进行两次迭代并得到不同的结果时,它会咬您一口。(或者你得到相同的结果,但它执行两次不必要的)
例如,在编写某个测试时,我需要一些临时文件来测试逻辑:
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName());
foreach (var file in files)
File.WriteAllText(file, "HELLO WORLD!");
/* ... many lines of codes later ... */
foreach (var file in files)
File.Delete(file);
想象一下当file . delete (file)抛出FileNotFound时我的惊讶!!
这里发生的情况是,可枚举的文件被迭代了两次(第一次迭代的结果根本没有被记住),在每次新的迭代中,您将重新调用Path.GetTempFilename(),因此您将得到一组不同的临时文件名。
当然,解决方案是使用ToArray()或ToList()快速枚举值:
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName())
.ToArray();
当你在做一些多线程的事情时,这甚至更可怕,比如:
foreach (var file in files)
content = content + File.ReadAllText(file);
你会发现内容。在所有写入之后,长度仍然为0 !!然后,当....时,您开始严格检查是否没有竞态条件浪费了一个小时之后……你发现这只是一个微小的可枚举的东西,你忘记了....
看看这个:
class Program
{
static void Main(string[] args)
{
var originalNumbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var list = new List<int>(originalNumbers);
var collection = new Collection<int>(originalNumbers);
originalNumbers.RemoveAt(0);
DisplayItems(list, "List items: ");
DisplayItems(collection, "Collection items: ");
Console.ReadLine();
}
private static void DisplayItems(IEnumerable<int> items, string title)
{
Console.WriteLine(title);
foreach (var item in items)
Console.Write(item);
Console.WriteLine();
}
}
输出是:
List items: 123456
Collection items: 23456
接受IList的集合构造函数会对原始List创建一个包装器,而List构造函数会创建一个新List并将所有引用从原始List复制到新List。
点击这里查看更多信息:
http://blog.roboblob.com/2012/09/19/dot-net-gotcha-nr1-list-versus-collection-constructor/