C#或.NET中最糟糕的陷阱是什么?

384

我最近在使用一个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#陷阱会很有用。


158
返回 DateTime.Now 增加一天后的日期。 - crashmstr
24
据我所知,内置值类型都是不可变的,至少在该类型包含的任何方法中,返回的都是一个新项,而不是修改现有项。至少,我想不到有一个不这样做的(方法):所有这些行为都非常一致。 - Joel Coehoorn
6
可变值类型:System.Collections.Generics.List.Enumerator。 (如果你尝试得足够努力,你会看到它表现得很奇怪。) - Jon Skeet
14
智能感知会为你提供所需的所有信息。它表明返回一个DateTime对象。如果它只是改变了你传入的对象,那么它就是一个void方法。 - John Kraft
22
不一定:例如,StringBuilder.Append(...)返回“this”,这在流畅接口中非常常见。 - Jon Skeet
显示剩余10条评论
61个回答

5
无论是LINQ-to-SQL还是LINQ-to-Entities
return result = from o in table
                where o.column == null
                select o;
//Returns all rows where column is null

int? myNullInt = null;
return result = from o in table
                where o.column == myNullInt
                select o;
//Never returns anything!

这里有一个关于LINQ-to-Entities的错误报告,请点击此处。然而,他们好像不经常查看这个论坛。或许应该为LINQ-to-SQL提交一个错误报告?


2
对于那些因遇到这个问题而来到此页面的人,可以在这里找到解决方法。 - BlueRaja - Danny Pflughoeft

5

我迄今最糟糕的发现是今天刚刚弄清楚的...如果你重写了object.Equals(object obj)方法,你可能会发现:

((MyObject)obj).Equals(this);

不会像这样表现:

((MyObject)obj) == this;

一个会调用您重写的函数,另一个则不会。

我曾经和一些以前从事Java的人一起工作,他们喜欢重写任何他们能够接触到的东西,并且他们经常因此而相互搞砸。我总是使用==,所以他们做的任何事情都不会对我产生影响。 - MusiGenesis
我认为你也可以重载==运算符... - RCIX
6
你可以重载==运算符,但重载.Equals()方法不能达到相同效果。因此你可以假设重载.Equals()和==两者,然后让它们分别执行不同的操作。 - GWLlosa
5
你可以重写Equals和重载==。这两者的区别微妙但非常重要。更多信息请参考此处:https://dev59.com/cHI-5IYBdhLWcg3wm5vN#1849288 - Samuel Neff
我似乎记得很久以前 == 是值比较运算符,而 .Equals() 是对象比较运算符。如果你重写了 Equals() 方法,你必须重写 GetHashCode() 方法...不确定(我可能记错了)。 - JPM
关于相等性的另一个需要注意的事项是,您不能为接口覆盖 ==。即使您的类具有自定义 Equals 方法,也无法告诉 == 使用它,并且只能比较引用。 - Eldritch Conundrum

5

看看这个:

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构造函数则创建一个新列表,并将所有引用从原始列表复制到新列表中。

在此处查看更多信息:http://blog.roboblob.com/2012/09/19/dot-net-gotcha-nr1-list-versus-collection-constructor/


哦,我的天啊...这太可怕了,来自BCL。 - nawfal

5

Oracle参数必须按顺序添加

这是在Oracle的参数化查询中使用ODP .Net实现时的一个重要陷阱。

当您向查询中添加参数时,默认行为是忽略参数名称,并按照添加顺序使用值。

解决方案是将OracleCommand对象的BindByName属性设置为true - 它默认为false... 这在质量上(如果不是数量上)有点像具有默认值为trueDropDatabaseOnQueryExecution属性。

他们称其为一个特性; 我称其为公共领域中的陷阱

更多细节请点击这里


对于Access,我的问题在这里:https://dev59.com/z1rUa4cB1Zd3GeqPk47F - nawfal

5

我经常需要提醒自己DateTime是一个值类型而不是引用类型。这对我来说似乎太奇怪了,尤其是考虑到它的各种构造函数。


2
我一直在不停地输入小写的日期时间...幸运的是,Intellisense 为我修复了它 :-) - chakrit
4
为什么这很重要呢?毕竟DateTime是不可变的,我无法想象你需要知道它是否是引用类型的情况。 - Stefan Steinegger
是的,需要提醒。我遇到的最频繁的烦恼与此相关,那就是我维护的旧应用程序中处处都有日期时间,它们可能未设置、未初始化、有效等等,当然我想看看它们是否为“null”,而在数据库中它们经常是这样,但在代码中不能这样。因此,它们必须是DateTime.MinValue或其他一些魔术数字或其他临时措施,并且如果它们没有被绝对地并确定地设置为至少合理的东西,我必须确保它们是这样的。 - MickeyfAgain_BeforeExitOfSO

4

递归属性的陷阱

这个问题不仅仅出现在C#中,我相信我在Stack Overflow上看到过它的提及(这里是问题,它让我想起了这个问题)。

这个问题可以通过两种方式发生,但最终结果是一样的:

当重写一个属性时忘记引用base.

 public override bool IsRecursive
 {
     get { return IsRecursive; }
     set { IsRecursive = value; }
 }

从自动属性转换为支持备份属性,但并非完全实现:

public bool IsRecursive
{
    get { return IsRecursive; }
    set { IsRecursive = value; }
}

2
不是一个“陷阱”,只是粗心编码。 - Shaul Behr
1
有问题需要问吗? :) - Benjol

3

1
似乎更根本的问题在于“Visible”在其getter和setter中具有不同的含义。 “Visible”属性应指示控件是否应“尝试”显示自己,而单独的“CanBeSeen”属性应是一个控件自身“visible”属性和其父级“CanBeSeen”的逻辑与。 - supercat

3

3

Linq-To-Sql和数据库/本地代码歧义

有时Linq无法确定某个方法是应该在数据库中执行还是在本地代码中执行。

请参见此处此处,了解问题陈述和解决方案。


请参阅:https://dev59.com/ek3Sa4cB1Zd3GeqPt0u3 - BlueRaja - Danny Pflughoeft

2

相关对象和外键不同步

Microsoft已经承认了这个错误

我有一个类Thing,它有一个对Category的外键。Category没有与Thing定义关系,以避免污染接口。

var thing = CreateThing(); // does stuff to create a thing
var category = GetCategoryByID(123); // loads the Category with ID 123
thing.Category = category;
Console.WriteLine("Category ID: {0}", thing.CategoryID); 

输出:

Category ID: 0

同样地:
var thing = CreateThing();
thing.CategoryID = 123;
Console.WriteLine("Category name: {0}", order.Category.Name);

抛出 NullReferenceException 异常。相关对象 Category 未加载 ID 为 123 的 Category 记录。

提交更改到数据库后,这些值会被同步。但是在访问数据库之前,FK 值和相关对象的函数几乎是独立的!

(有趣的是,只有在没有定义子关系时才似乎无法将 FK 值与相关对象同步,即 Category 没有 "Things" 属性。但是当您仅设置 FK 值时,“按需加载”永远不起作用。)

注意!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接