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


当前回答

到目前为止我最糟糕的一个,我今天才想出来……如果你重写object。Equals(object obj),你会发现:

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

与以下行为不同:

((MyObject)obj) == this;

一个会调用你的覆盖函数,另一个不会。

其他回答

合同在流。阅读是我见过很多人被绊倒的东西:

// Read 8 bytes and turn them into a ulong
byte[] data = new byte[8];
stream.Read(data, 0, 8); // <-- WRONG!
ulong data = BitConverter.ToUInt64(data);

这是错误的原因是流。Read最多读取指定的字节数,但完全可以只读取1个字节,即使在流结束前还有7个字节可用。

它看起来与Stream如此相似,这并没有什么帮助。如果没有异常返回,则保证已写入所有字节。上面的代码几乎一直都能工作,这也没有什么帮助。当然,没有现成的、方便的方法来正确地读取N个字节也无济于事。

所以,为了堵住这个漏洞,提高人们的意识,这里有一个正确的方法:

    /// <summary>
    /// Attempts to fill the buffer with the specified number of bytes from the
    /// stream. If there are fewer bytes left in the stream than requested then
    /// all available bytes will be read into the buffer.
    /// </summary>
    /// <param name="stream">Stream to read from.</param>
    /// <param name="buffer">Buffer to write the bytes to.</param>
    /// <param name="offset">Offset at which to write the first byte read from
    ///                      the stream.</param>
    /// <param name="length">Number of bytes to read from the stream.</param>
    /// <returns>Number of bytes read from the stream into buffer. This may be
    ///          less than requested, but only if the stream ended before the
    ///          required number of bytes were read.</returns>
    public static int FillBuffer(this Stream stream,
                                 byte[] buffer, int offset, int length)
    {
        int totalRead = 0;
        while (length > 0)
        {
            var read = stream.Read(buffer, offset, length);
            if (read == 0)
                return totalRead;
            offset += read;
            length -= read;
            totalRead += read;
        }
        return totalRead;
    }

    /// <summary>
    /// Attempts to read the specified number of bytes from the stream. If
    /// there are fewer bytes left before the end of the stream, a shorter
    /// (possibly empty) array is returned.
    /// </summary>
    /// <param name="stream">Stream to read from.</param>
    /// <param name="length">Number of bytes to read from the stream.</param>
    public static byte[] Read(this Stream stream, int length)
    {
        byte[] buf = new byte[length];
        int read = stream.FillBuffer(buf, 0, length);
        if (read < length)
            Array.Resize(ref buf, read);
        return buf;
    }

对于C/ c++程序员来说,过渡到c#是很自然的事情。然而,我个人遇到的最大的问题(以及其他人在做同样的转换时遇到的问题)是没有完全理解c#中类和结构之间的区别。

在c++中,类和结构是相同的;它们只在默认可见性上有所不同,其中类默认为私有可见性,结构默认为公共可见性。在c++中,这个类定义

    class A
    {
    public:
        int i;
    };

在功能上等价于此结构定义。

    struct A
    {
        int i;
    };

然而,在c#中,类是引用类型,而结构是值类型。这在(1)决定何时使用一个而不是另一个,(2)测试对象的相等性,(3)性能(例如,装箱/拆箱)等方面产生了很大的差异。

网络上有各种各样的信息与两者之间的差异有关(例如,这里)。我强烈建议任何想要过渡到c#的人至少要了解这些差异及其含义。

Linq2SQL:接口成员的映射[…]不支持。

如果对实现接口的对象执行Linq2Sql查询,则会得到非常奇怪的行为。假设你有一个类MyClass,它实现了一个接口IHasDescription,这样:

public interface IHasDescription {
  string Description { get; set; }
}

public partial class MyClass : IHasDescription { }

(MyClass的另一半是一个Linq2Sql生成的类,包括属性Description。)

现在你写一些代码(这通常发生在泛型方法中):

public static T GetByDescription<T>(System.Data.Linq.Table<T> table, string desc) 
  where T : class, IHasDescription {
  return table.Where(t => t.Description == desc).FirstOrDefault();
}

编译正常-但你会得到一个运行时错误:

NotSupportedException: The mapping of interface member IHasDescription.Description is not supported.

现在该怎么办呢?好吧,这真的很明显:只需将==更改为.Equals(),这样:

return table.Where(t => t.Description.Equals(desc)).FirstOrDefault();

现在一切都好了!

在这里看到的。

private int myVar;
public int MyVar
{
    get { return MyVar; }
}

心想。你的应用在没有堆栈跟踪的情况下崩溃了。这种事经常发生。

(请注意getter中的大写MyVar而不是小写MyVar。)

在虚方法中使用默认参数

abstract class Base
{
    public virtual void foo(string s = "base") { Console.WriteLine("base " + s); }
}

class Derived : Base
{
    public override void foo(string s = "derived") { Console.WriteLine("derived " + s); }
}

...

Base b = new Derived();
b.foo();

输出: 派生的基础