C#或.NET中最糟糕的问题是什么?

时间:2008-10-27 19:30:09

标签: c# .net

我最近使用的是DateTime对象,并写了类似这样的内容:

DateTime dt = DateTime.Now;
dt.AddDays(1);
return dt; // still today's date! WTF?

AddDays()的intellisense文档说它在日期中添加了一天,但它没有 - 它实际上返回添加了一天的日期,所以你必须写得像:

DateTime dt = DateTime.Now;
dt = dt.AddDays(1);
return dt; // tomorrow's date

之前我曾多次咬过我,所以我认为编制最糟糕的C#陷阱会很有用。

61 个答案:

答案 0 :(得分:299)

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

Blammo。您的应用程序崩溃,没有堆栈跟踪。一直发生。

(注意资本MyVar而不是getter中的小写myVar。)

答案 1 :(得分:251)

<强> Type.GetType

我见过的人咬了很多人Type.GetType(string)。他们想知道为什么它适用于自己的程序集中的类型,以及某些类型,如System.String,但不是System.Windows.Forms.Form。答案是它只查看当前程序集和mscorlib


匿名方法

C#2.0引入了匿名方法,导致这样的恶劣情况:

using System;
using System.Threading;

class Test
{
    static void Main()
    {
        for (int i=0; i < 10; i++)
        {
            ThreadStart ts = delegate { Console.WriteLine(i); };
            new Thread(ts).Start();
        }
    }
}

打印出来的是什么?嗯,这完全取决于时间安排。它将打印10个数字,但它可能不会打印0,1,2,3,4,5,6,7,8,9,这是您可能期望的。问题是它是被捕获的i变量,而不是它在创建委托时的值。这可以通过适当范围的额外局部变量轻松解决:

using System;
using System.Threading;

class Test
{
    static void Main()
    {
        for (int i=0; i < 10; i++)
        {
            int copy = i;
            ThreadStart ts = delegate { Console.WriteLine(copy); };
            new Thread(ts).Start();
        }
    }
}

延迟执行迭代器块

这个“穷人的单元测试”没有通过 - 为什么不通过?

using System;
using System.Collections.Generic;
using System.Diagnostics;

class Test
{
    static IEnumerable<char> CapitalLetters(string input)
    {
        if (input == null)
        {
            throw new ArgumentNullException(input);
        }
        foreach (char c in input)
        {
            yield return char.ToUpper(c);
        }
    }

    static void Main()
    {
        // Test that null input is handled correctly
        try
        {
            CapitalLetters(null);
            Console.WriteLine("An exception should have been thrown!");
        }
        catch (ArgumentNullException)
        {
            // Expected
        }
    }
}

答案是CapitalLetters代码源代码中的代码在第一次调用迭代器的MoveNext()方法之前不会执行。

我的brainteasers page还有其他一些奇怪的东西。

答案 2 :(得分:192)

重新抛出异常

获得大量新开发人员的问题是重新抛出异常语义。

很多时候我看到如下代码

catch(Exception e) 
{
   // Do stuff 
   throw e; 
}

问题在于它擦除了堆栈跟踪并使诊断问题更加困难,导致您无法跟踪异常发生的位置。

正确的代码是没有args的throw语句:

catch(Exception)
{
    throw;
}

或者将异常包装在另一个异常中,并使用内部异常来获取原始堆栈跟踪:

catch(Exception e) 
{
   // Do stuff 
   throw new MySpecialException(e); 
}

答案 3 :(得分:192)

海森堡观察窗口

如果您正在进行按需加载的操作,这可能会让您感到非常痛苦,例如:

private MyClass _myObj;
public MyClass MyObj {
  get {
    if (_myObj == null)
      _myObj = CreateMyObj(); // some other code to create my object
    return _myObj;
  }
}

现在让我们假设你在其他地方使用了一些代码:

// blah
// blah
MyObj.DoStuff(); // Line 3
// blah

现在您要调试CreateMyObj()方法。所以你在上面的第3行放了一个断点,打算进入代码。只是为了好的衡量标准,你还在_myObj = CreateMyObj();上方的一行上设置断点,甚至在CreateMyObj()内部设置一个断点。

代码在第3行遇到你的断点。你进入代码。您希望输入条件代码,因为_myObj显然为空,对吧?呃...那么......为什么它跳过这个条件并直接进入return _myObj ?!你将鼠标悬停在_myObj上......事实上,它确实有价值!这是怎么发生的?!

答案是你的IDE导致它获得一个值,因为你打开了一个“监视”窗口 - 特别是“Autos”监视窗口,它显示与当前或上一行相关的所有变量/属性的值执行。当你在第3行遇到断点时,观察窗口决定你有兴趣知道MyObj的价值 - 所以在幕后,忽略你的任何断点,它会去为您计算MyObj的值 - 包括设置_myObj值的CreateMyObj()的调用!

这就是我称之为海森堡观察窗的原因 - 你不能在不影响它的情况下观察它的价值...... :)

<强> GOTCHA!


编辑 - 我觉得@ChristianHayter的评论值得包含在主要答案中,因为它看起来像是这个问题的有效解决方法。所以,只要你有一个懒惰的属性......

  

使用[DebuggerBrowsable(DebuggerBrowsableState.Never)]或[DebuggerDisplay(“&lt;按需加载&gt;”)]装饰您的属性。 - 克里斯蒂安·海特

答案 4 :(得分:143)

这是另一个让我受益的时间:

static void PrintHowLong(DateTime a, DateTime b)
{
    TimeSpan span = a - b;
    Console.WriteLine(span.Seconds);        // WRONG!
    Console.WriteLine(span.TotalSeconds);   // RIGHT!
}

TimeSpan.Seconds是时间跨度的秒部分(2分0秒的秒值为0)。

TimeSpan.TotalSeconds是以秒为单位测量的整个时间跨度(2分钟的总秒数值为120)。

答案 5 :(得分:79)

因为你没有解除事件的连接而导致内存泄漏。

这甚至让我认识的一些高级开发人员感到高兴。

想象一下包含很多内容的WPF表单,在那里你订阅了一个事件。如果您没有取消订阅,则在关闭和取消引用后,整个表单将保留在内存中。

我相信我看到的问题是在WPF表单中创建一个DispatchTimer并订阅Tick事件,如果你没有 - =在计时器上你的表单会泄漏内存!

在此示例中,您的拆卸代码应该

timer.Tick -= TimerTickEventHandler;

这个特别棘手,因为你在WPF表单中创建了DispatchTimer的实例,所以你会认为它是垃圾收集过程处理的内部引用...不幸的是,DispatchTimer使用静态内部列表UI线程上的订阅和服务请求,因此引用由静态类“拥有”。

答案 6 :(得分:62)

也许不是真的有问题,因为这种行为在MSDN中写得很清楚,但是我的脖子已经打破了一次,因为我发现它反直觉:

Image image = System.Drawing.Image.FromFile("nice.pic");

此人将"nice.pic"文件锁定,直到图像被丢弃。在我遇到它的时候,我虽然在飞行中加载图标并且没有意识到(起初)我最终得到了数十个打开和锁定的文件会很好!图像跟踪它从...加载文件的位置

如何解决这个问题?我以为一个班轮就可以完成这项任务。我期待FromFile()的额外参数,但没有,所以我写了这个......

using (Stream fs = new FileStream("nice.pic", FileMode.Open, FileAccess.Read))
{
    image = System.Drawing.Image.FromStream(fs);
}

答案 7 :(得分:51)

如果算上ASP.NET,我会说webforms生命周期对我来说是一个非常大的问题。我花了无数个小时来调试写得不好的webforms代码,只是因为很多开发人员并不真正了解何时使用哪个事件处理程序(遗憾的是我包括在内)。

答案 8 :(得分:47)

重载==运算符和无类型容器(arraylists,数据集等):

string my = "my ";
Debug.Assert(my+"string" == "my string"); //true

var a = new ArrayList();
a.Add(my+"string");
a.Add("my string");

// uses ==(object) instead of ==(string)
Debug.Assert(a[1] == "my string"); // true, due to interning magic
Debug.Assert(a[0] == "my string"); // false

解决方案?

  • 在比较字符串类型时始终使用string.Equals(a, b)

  • 使用List<string>等泛型来确保两个操作数都是字符串。

答案 9 :(得分:46)

<强>则DateTime.ToString( “DD / MM / YYYY”);这实际上总是给你dd / MM / yyyy但是它会考虑区域设置并根据你的位置替换日期分隔符。所以你可能会得到dd-MM-yyyy或类似的东西。

正确的方法是使用 DateTime.ToString(“dd'/'MM'/'yyyy”);


DateTime.ToString(“r”)应该转换为使用GMT的RFC1123。格林尼治标准时间距离UTC只有几分之一秒,而“r”格式说明符does not convert to UTC,即使有问题的日期时间被指定为本地。

这导致以下问题(取决于您当地时间与UTC的距离):

DateTime.Parse("Tue, 06 Sep 2011 16:35:12 GMT").ToString("r")
>              "Tue, 06 Sep 2011 17:35:12 GMT"

糟糕!

答案 10 :(得分:45)

[Serializable]
class Hello
{
    readonly object accountsLock = new object();
}

//Do stuff to deserialize Hello with BinaryFormatter
//and now... accountsLock == null ;)

故事的道德:反序列化对象时不会运行字段初始化程序

答案 11 :(得分:44)

前几天我看到这张贴了,我觉得它很晦涩,对那些不知道的人来说很痛苦

int x = 0;
x = x++;
return x;

因为它将返回0而不是大多数人所期望的

答案 12 :(得分:40)

我参加这个派对有点晚了,但是我最近有两个陷入困境的陷阱:

日期时间分辨率

Ticks属性测量时间为百万分之一秒(100纳秒块),但分辨率不是100纳秒,大约是15毫秒。

此代码:

long now = DateTime.Now.Ticks;
for (int i = 0; i < 10; i++)
{
    System.Threading.Thread.Sleep(1);
    Console.WriteLine(DateTime.Now.Ticks - now);
}

会给你一个输出(例如):

0
0
0
0
0
0
0
156254
156254
156254

同样,如果你看看DateTime.Now.Millisecond,你将获得15.625ms的圆形块值:15,31,46等。

此特定行为varies from system to system,但此日期/时间API中的there are other resolution-related gotchas


Path.Combine

组合文件路径的好方法,但它并不总是按照您期望的方式运行。

如果第二个参数以\字符开头,则不会为您提供完整的路径:

此代码:

string prefix1 = "C:\\MyFolder\\MySubFolder";
string prefix2 = "C:\\MyFolder\\MySubFolder\\";
string suffix1 = "log\\";
string suffix2 = "\\log\\";

Console.WriteLine(Path.Combine(prefix1, suffix1));
Console.WriteLine(Path.Combine(prefix1, suffix2));
Console.WriteLine(Path.Combine(prefix2, suffix1));
Console.WriteLine(Path.Combine(prefix2, suffix2));

给你这个输出:

C:\MyFolder\MySubFolder\log\
\log\
C:\MyFolder\MySubFolder\log\
\log\

答案 13 :(得分:37)

当您启动一个写入控制台的进程(使用System.Diagnostics),但您从未阅读过Console.Out流时,在一定量的输出后,您的应用程序将显示为挂起。

答案 14 :(得分:34)

Linq-To-Sql中没有操作员快捷方式

请参阅here

简而言之,在Linq-To-Sql查询的条件子句中,您不能使用||&&等条件快捷方式来避免空引用异常;即使第一个条件不需要评估第二个条件,Linq-To-Sql也会对OR或AND运算符的两边进行求值!

答案 15 :(得分:29)

使用虚拟方法的默认参数

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();
  

输出:
  派生基地

答案 16 :(得分:27)

可变集合中的值对象

struct Point { ... }
List<Point> mypoints = ...;

mypoints[i].x = 10;

无效。

mypoints[i]返回Point值对象的副本。 C#happily允许您修改副本的字段。默默无闻。


<强>更新 这似乎是在C#3.0中修复的:

Cannot modify the return value of 'System.Collections.Generic.List<Foo>.this[int]' because it is not a variable

答案 17 :(得分:25)

也许不是最糟糕的,但是.net框架use degrees的某些部分,而其他部分使用radians (并且Intellisense出现的文档从不告诉您哪个,您必须访问MSDN找出

所有这一切都可以通过Angle班而不是......

来避免

答案 18 :(得分:22)

对于C / C ++程序员来说,过渡到C#是很自然的。然而,我遇到的最大问题(并且已经与其他人一起进行过相同的转换)并没有完全理解C#中类和结构之间的区别。

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

    class A
    {
    public:
        int i;
    };

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

    struct A
    {
        int i;
    };

但是,在C#中,类是引用类型,而结构是值类型。这会产生 BIG 差异(1)决定何时使用其中一个,(2)测试对象相等,(3)性能(例如装箱/拆箱)等。

网络上有各种与两者之间的差异相关的信息(例如here)。我强烈建议任何过渡到C#的人至少要了解差异及其影响。

答案 19 :(得分:19)

垃圾收集和Dispose()。虽然您不必做任何事情来释放内存,但您仍然需要通过Dispose()释放资源。当您使用WinForms或以任何方式跟踪对象时,这是一个非常容易忘记的事情。

答案 20 :(得分:18)

令人讨厌的Linq Caching Gotcha

导致此次发现的my question和发现此问题的the blogger

简而言之,DataContext保留了您加载的所有Linq-to-Sql对象的缓存。如果其他人对您之前加载的记录进行了任何更改,即使您明确重新加载记录,也无法获取最新数据

这是因为DataContext上有一个名为ObjectTrackingEnabled的属性,默认情况下为true。如果您将该属性设置为false,则每次都会重新加载记录... 但是 ...您无法使用SubmitChanges()保留对该记录的任何更改。

<强> GOTCHA!

答案 21 :(得分:18)

阵列实施IList

但是不要实现它。当您调用Add时,它会告诉您它不起作用。那么为什么一个类在它不能支持时实现一个接口呢?

编译,但不起作用:

IList<int> myList = new int[] { 1, 2, 4 };
myList.Add(5);

我们经常遇到这个问题,因为序列化程序(WCF)将所有IList转换为数组,并且我们遇到运行时错误。

答案 22 :(得分:18)

MS SQL Server无法处理1753年之前的日期。重要的是,这与.NET DateTime.MinDate常量(1/1/1)不同步。因此,如果你试图保存一个思想,一个畸形的日期(最近发生在我的数据导入中)或者仅仅是征服者威廉的出生日期,你就会遇到麻烦。没有内置的解决方法;如果您可能需要在1753年之前使用日期,则需要编写自己的解决方法。

答案 23 :(得分:18)

foreach循环变量范围!

var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
    l.Add(() => s);
}

foreach (var a in l)
    Console.WriteLine(a());

打印五个“amet”,而以下示例正常工作

var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
    var t = s;
    l.Add(() => t);
}

foreach (var a in l)
    Console.WriteLine(a());

答案 24 :(得分:17)

Stream.Read 上的合同是我见过很多人的事情:

// 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);

这是错误的原因是Stream.Read将读取最多指定的字节数,但完全免费只能读取1个字节,甚至如果在流结束之前有另外7个字节可用。

这看起来与Stream.Write非常相似没有帮助, 保证在没有异常的情况下返回时写入所有字节。上述代码几乎一直在工作也无济于事。当然,没有现成的,方便的方法可以正确读取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;
    }

答案 25 :(得分:14)

今天我修复了一个长期躲避的错误。该错误位于多线程场景中使用的泛型类中,而静态int字段用于使用Interlocked提供无锁同步。该错误是由于类型的泛型类的每个实例化都有自己的静态引起的。所以每个线程都有自己的静态字段,并没有按预期使用锁。

class SomeGeneric<T>
{
    public static int i = 0;
}

class Test
{
    public static void main(string[] args)
    {
        SomeGeneric<int>.i = 5;
        SomeGeneric<string>.i = 10;
        Console.WriteLine(SomeGeneric<int>.i);
        Console.WriteLine(SomeGeneric<string>.i);
        Console.WriteLine(SomeGeneric<int>.i);
    }
}

这打印 五 10 5

答案 26 :(得分:14)

<强>事件

我从未理解为什么事件是语言功能。它们使用起来很复杂:你需要在调用之前检查null,你需要取消注册(你自己),你无法找到谁注册了(例如:我注册了吗?)。为什么事件不是图书馆中的一个类?基本上是专门的List<delegate>

答案 27 :(得分:13)

刚刚找到一个让我陷入调试一段时间的奇怪的事情:

您可以为可空int添加null而不抛出异常,并且该值保持为null。

int? i = null;
i++; // I would have expected an exception but runs fine and stays as null

答案 28 :(得分:13)

可以多次评估枚举数

当你有一个懒惰枚举的枚举并且你迭代两次并得到不同的结果时,它会咬你。 (或者你获得相同的结果但不必要地执行两次)

例如,在编写某个测试时,我需要一些临时文件来测试逻辑:

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 !!

时,我会感到惊讶

这里发生的事情是files枚举被迭代两次(第一次迭代的结果只是记住)并且在每次新的迭代中你我要重新调用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);

并且在所有写入之后你发现content.Length仍然是0!然后,你开始严格检查你没有竞争条件......在一个浪费时间之后......你发现它只是那个小小的可疑的东西你忘记了......

答案 29 :(得分:9)

TextInfo textInfo = Thread.CurrentThread.CurrentCulture.TextInfo;

textInfo.ToTitleCase("hello world!"); //Returns "Hello World!"
textInfo.ToTitleCase("hElLo WoRld!"); //Returns "Hello World!"
textInfo.ToTitleCase("Hello World!"); //Returns "Hello World!"
textInfo.ToTitleCase("HELLO WORLD!"); //Returns "HELLO WORLD!"

是的,记录了这种行为,但这肯定不是正确的。

答案 30 :(得分:8)

Dictionary&lt;,&gt;:“返回项目的顺序未定义”。这太可怕了,因为它有时可能会咬你,但是工作别人,如果你只是盲目地认为字典会发挥得很好(“为什么不应该呢?我想,List会这样做”),你真的需要在你最终开始质疑你的假设之前,请先了解它。

(类似问题here。)

答案 31 :(得分:8)

这是一个超级陷阱,我浪费了2天的故障排除。它没有抛出任何例外,它只是使用weird error messages崩溃了网络服务器。我无法在DEV中重现该问题。此外,项目构建设置的实验以某种方式使它在PROD中消失,然后又回来了。最后我明白了。

如果您在以下代码中看到问题,请告诉我:

private void DumpError(Exception exception, Stack<String> context)
{
    if (context.Any())
    {
        Trace.WriteLine(context.Pop());
        Trace.Indent();
        this.DumpError(exception, context);
        Trace.Unindent();
    }
    else
    {
        Trace.WriteLine(exception.Message);
    }
}

所以,如果你重视自己的理智:

<强> !!!从来没有对Trace方法提出任何逻辑!!!

代码必须如下所示:

private void DumpError(Exception exception, Stack<String> context)
{
    if (context.Any())
    {
        var popped = context.Pop();
        Trace.WriteLine(popped);
        Trace.Indent();
        this.DumpError(exception, context);
        Trace.Unindent();
    }
    else
    {
        Trace.WriteLine(exception.Message);
    }
}

答案 32 :(得分:8)

.NET Gotchas

上有一本书

我最喜欢的是你在C#中创建一个类,将它继承到VB然后尝试重新继承回C#并且它不起作用。 ARGGH

答案 33 :(得分:7)

静态构造函数在锁定下执行。因此,从静态构造函数调用线程代码可能会导致死锁。 这是一个演示它的例子:

using System.Threading;
class Blah
{
    static void Main() { /* Won’t run because the static constructor deadlocks. */ }

    static Blah()
    {
        Thread thread = new Thread(ThreadBody);
        thread.Start();
        thread.Join();
    }

    static void ThreadBody() { }
}

答案 34 :(得分:7)

在调试环境中评估时,base关键字无法按预期工作:方法调用仍使用虚拟调度。

当我偶然发现它时,我浪费了很多时间,我以为我在CLR的时空中遇到了某种裂痕,但我意识到这是一个已知的(甚至有点故意的)错误:

http://blogs.msdn.com/jmstall/archive/2006/06/29/funceval-does-virtual-dispatch.aspx

答案 35 :(得分:7)

如果您处于设计模式,则所有UserControl中的DesignMode属性都会not actually tell you

答案 36 :(得分:7)

MemoryStream.GetBuffer() vs MemoryStream.ToArray()。前者返回整个缓冲区,后者只是使用过的部分。呸。

答案 37 :(得分:6)

<强> ASP.NET:

如果您使用的是Linq-To-SQL,则在数据上下文中调用SubmitChanges()并抛出异常(例如重复键或其他约束违规),当您使用时,有问题的对象值会保留在您的内存中调试,每次随后拨打SubmitChanges()时都会重新提交。

现在这里是真正的踢球者:即使您在IDE中按下“停止”按钮并重新启动,错误的值仍将保留在内存中 我不明白为什么有人认为这是一个好主意 - 但是系统托盘中弹出的那个小的ASP.NET图标仍然在运行,它似乎可以保存你的对象缓存。如果要刷新内存空间,则必须右键单击该图标并强制关闭它!的 GOTCHA!

答案 38 :(得分:6)

如果您正在为MOSS编码并且您以这种方式获得站点引用:

SPSite oSiteCollection = SPContext.Current.Site;

以后在你的代码中说:

oSiteCollection.Dispose();

来自MSDN

如果创建SPSite对象,则可以使用Dispose方法关闭对象。但是,如果您具有对共享资源的引用,例如,当GetContextSite方法或Site属性(例如,SPContext.Current.Site)提供对象时,请不要使用Dispose方法关闭对象,而是允许Windows SharePoint Services或门户应用程序管理该对象。有关对象处理的详细信息,请参阅最佳实践:使用一次性Windows SharePoint Services对象。

每个MOSS程序员都会遇到这种情况。

答案 39 :(得分:5)

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

答案 40 :(得分: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-Entites here有一个错误报告,虽然它们似乎不经常查看该论坛。也许有人应该为LINQ-to-SQL提交一个文件?

答案 41 :(得分: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周围创建一个包装器,而List构造函数创建一个新的List并将所有引用从原始列表复制到新列表。

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

答案 42 :(得分:5)

到目前为止,我最糟糕的一个,我今天才知道......如果你覆盖object.Equals(object obj),你可以发现:

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

与以下行为不同:

((MyObject)obj) == this;

一个人会调用你的覆盖功能,另一个人不会。

答案 43 :(得分:5)

必须按顺序添加Oracle参数

这是Oracle的参数化查询的ODP .Net实现中的一个主要问题。

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

解决方案是将BindByName对象的OracleCommand属性设置为true - 默认为false ...这是定性的(如果不是非常定量)比如拥有一个名为DropDatabaseOnQueryExecution的属性,其默认值为true

They call it a feature; I call it a pit in the public domain

有关详细信息,请参阅here

答案 44 :(得分:5)

enum Seasons
{
    Spring = 1, Summer = 2, Automn = 3, Winter = 4
}

public string HowYouFeelAbout(Seasons season)
{
    switch (season)
    {
        case Seasons.Spring:
            return "Nice.";
        case Seasons.Summer:
            return "Hot.";
        case Seasons.Automn:
            return "Cool.";
        case Seasons.Winter:
            return "Chilly.";
    }
}

错误?
并非所有代码路径都返回值...
你在跟我开玩笑吗?我敢打赌所有代码路径都返回一个值,因为这里提到了每个Seasons成员。它本应该检查所有枚举成员,如果一个成员在交换机案例中不存在,那么这样的错误就会有意义,但是现在我应该添加一个Default案例,这个案例是多余的,并且永远不会被代码触及。

编辑:
经过对此问题的更多研究后,我来到Eric Lippert's nice written and useful post,但它仍然有点奇怪。你同意吗?

答案 45 :(得分:4)

递归属性问题

我认为并非特定于C#,而且我确信我已经在其他地方看到了它(this提醒我的问题)

它可以有两种方式,但最终结果是相同的:

在覆盖属性时忘记引用base.

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

从自动属性更改为支持属性,但并不完全一直:

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

答案 46 :(得分:3)

发生在我身上的最糟糕的事情是webBrowser documentText问题:

http://geekswithblogs.net/paulwhitblog/archive/2005/12/12/62961.aspx#107062

AllowNavigation解决方案适用于Windows窗体...

但在紧凑的框架中,该属性不存在......

...到目前为止,我找到的唯一解决方法是重建浏览器控件:

http://social.msdn.microsoft.com/Forums/it-IT/netfxcompact/thread/5637037f-96fa-48e7-8ddb-6d4b1e9d7db9

但是这样做,你需要手头处理浏览器历史......:P

答案 47 :(得分:3)

Linq-To-Sql和数据库/本地代码含糊不清

有时Linq无法确定某个方法是在DB上还是在本地代码中执行。

有关问题陈述和解决方案,请参阅herehere

答案 48 :(得分:3)

当可见变化时,

VisibleChangednot usually called

答案 49 :(得分:2)

LINQ to SQL和一对多关系

这是一个让我咬了几次的可爱的人,MS把它留给了自己的一个开发者,把它放在her blog中。我不能比她做得更好,所以看看那里。

答案 50 :(得分:2)

相关对象和外键不同步

Microsoft have admitted to this bug

我有一个班级Thing,其FK为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的类别记录。

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

(有趣的是,当没有定义子关系时,似乎没有将FK值与相关对象同步,即类别没有“物品”属性。但是当你设置了它时,“按需加载” FK值永远不会起作用。)

<强> GOTCHA!

答案 51 :(得分:1)

不是最糟糕的,但尚未提出的。即使只使用一个返回值,也可以多次调用作为参数传递给System.Collections.Concurrent方法的工厂方法。考虑到.NET试图保护您免受线程原语中的虚假唤醒的影响,这可能会让您大吃一惊。

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ValueFactoryBehavingBadlyExample
{
    class Program
    {
        static ConcurrentDictionary<int, int> m_Dict = new ConcurrentDictionary<int, int>();
        static ManualResetEventSlim m_MRES = new ManualResetEventSlim(false);
        static void Main(string[] args)
        {
            for (int i = 0; i < 8; ++i)
            {
                Task.Factory.StartNew(ThreadGate, TaskCreationOptions.LongRunning);
            }
            Thread.Sleep(1000);
            m_MRES.Set();
            Thread.Sleep(1000);
            Console.WriteLine("Dictionary Size: " + m_Dict.Count);
            Console.Read();
        }

        static void ThreadGate()
        {
            m_MRES.Wait();
            int value = m_Dict.GetOrAdd(0, ValueFactory);
        }

        static int ValueFactory(int key)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Value Factory Called");
            return key;
        }
    }
}

(可能)输出:

Value Factory Called
Value Factory Called
Value Factory Called
Value Factory Called
Dictionary Size: 0
Value Factory Called
Value Factory Called
Value Factory Called
Value Factory Called

答案 52 :(得分:1)

将容量传递给List<int>而不是使用收集初始化程序。

var thisOnePasses = new List<int> {2}; // collection initializer
var thisOneFails = new List<int> (2);  // oops, use capacity by mistake #gotcha#

thisOnePasses.Count.Should().Be(1);
thisOnePasses.First().Should().Be(2);

thisOneFails.Count.Should().Be(1);     // it's zero
thisOneFails.First().Should().Be(2);   // Sequence contains no elements...

答案 53 :(得分:1)

有时堆栈跟踪中的行号与源代码中的行号不匹配。这可能是由于用于优化的简单(单行)函数的内联而发生的。对于使用日志进行调试的人来说,这是一个严重的混淆源。

编辑:示例:有时您会在堆栈跟踪中看到一个空引用异常,它指向一行代码,绝对没有空引用异常,就像一个简单的整数赋值。

答案 54 :(得分:1)

我一直认为value类型始终位于stack上的referenceheap类型上。

好吧不那么。当我最近在SO上看到this question时(可以说错误地回答)我开始知道情况并非如此。

Jon Skeet回答(提及Eric Lippert's Blog post)时,其神话

相当重要的链接:

The truth about Value Types

References are not aAddress

The Stack is an Implementation Detail Part 1

The Stack is an Implementation Detail Part 2

答案 55 :(得分:1)

LinqToSQL和空集聚合

请参阅this question

如果您有运行聚合的LinqToSql查询 - 如果您的结果集为空,则Linq无法确定数据类型是什么,即使它已被声明。

e.g。假设您有一个包含字段Claim的表Amount,其在LinqToSql中的类型为decimal

var sum = Claims.Where(c => c.ID < 0).Sum(c => c.Amount);

显然没有声明的ID小于零,所以你期望看到sum = null,对吧? 错误!您得到InvalidOperationException,因为Linq查询底层的SQL查询没有数据类型。你必须明确地告诉Linq它是小数!因此:

var sum = Claims.Where(c => c.ID < 0).Sum(c => (decimal?)c.Amount);

这真是愚蠢,IMO是微软的设计缺陷。

<强> GOTCHA!

答案 56 :(得分:1)

以下内容不会在.Net中捕获异常。相反,它会导致StackOverflow异常。

private void button1_Click( object sender, EventArgs e ) {
    try {
        CallMe(234);
    } catch (Exception ex) {
        label1.Text = ex.Message.ToString();
    }
}
private void CallMe( Int32 x ) {
    CallMe(x);
}

对于评论者(和赞成票):
堆栈溢出是非常罕见的。但是,如果出现这种情况,您将无法捕获异常,并可能花费几个小时试图找到问题的确切位置。如果SO出现在很少使用的逻辑路径中,可能会更复杂,尤其是在您可能不知道启动问题的确切条件的Web应用程序上。

这与此问题的接受答案(https://stackoverflow.com/a/241194/2424)的情况完全相同。该答案的属性getter基本上与上面的代码完全相同,并且没有堆栈跟踪崩溃。

答案 57 :(得分:0)

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.

现在whaddaya那样做了吗?嗯,显而易见:只需将==更改为.Equals(),即可:

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

现在一切正常!

请参阅here

答案 58 :(得分:0)

使用批量大小的平方LinqToSql批次变慢

Here's the question(和answer)我探讨了这个问题。

简而言之,如果在调用DataContext.SubmitChanges()之前尝试在内存中构建太多对象,则会以几何速率开始出现迟缓。我没有确认100%是这种情况,但在我看来,对DataContext.GetChangeSet()的调用导致数据上下文对2个项目的每个组合执行等价评估(.Equals())更改集,可能是为了确保它不是双重插入或导致其他并发问题。问题是,如果您的批次非常大,则比较次数会与 n 的平方成比例增加,即(n ^ 2 + n)/ 2 。内存中的1000个项目意味着超过500,000个比较......这可能需要很长时间才能获得heckuva。

为了避免这种情况,您必须确保对于您预期大量项目的任何批次,您在事务边界内执行整个操作,在创建时保存每个项目,而不是在最后保存一个大项目

答案 59 :(得分:0)

mystring.Replace("x","y")

虽然它看起来应该对字符串进行替换,但是它实际上会返回一个新的字符串,其中包含替换而不更改它所调用的字符串。你需要记住字符串是不可变的。

答案 60 :(得分:-1)

一些代码:

        List<int> a = new List<int>();
        for (int i = 0; i < 10; i++)
        {
            a.Add(i);
        }

        var q1 = (from aa in a
                  where aa == 2
                  select aa).Single();

        var q2 = (from aa in a
                  where aa == 2
                  select aa).First();

q1 - 在此查询中检查List中的所有整数; q2 - 检查整数,直到找到“正确”的整数。