What is the worst gotcha in C# or .NET? [closed]
I was recently working with a DateTime
object, and wrote something like this:
DateTime dt = DateTime.Now;
dt.AddDays(1);
return dt; // still today's date! WTF?
The intellisense documentation for AddDays()
says it adds a day to the date, which it doesn't - it actually returns a date with a day added to it, so you have to write it like:
DateTime dt = DateTime.Now;
dt = dt.AddDays(1);
return dt; // tomorrow's date
This one has bitten me a number of times before, so I thought it would be useful to catalog the worst C# gotchas.
Solution 1:
private int myVar;
public int MyVar
{
get { return MyVar; }
}
Blammo. Your app crashes with no stack trace. Happens all the time.
(Notice capital MyVar
instead of lowercase myVar
in the getter.)
Solution 2:
Type.GetType
The one which I've seen bite lots of people is Type.GetType(string)
. They wonder why it works for types in their own assembly, and some types like System.String
, but not System.Windows.Forms.Form
. The answer is that it only looks in the current assembly and in mscorlib
.
Anonymous methods
C# 2.0 introduced anonymous methods, leading to nasty situations like this:
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();
}
}
}
What will that print out? Well, it entirely depends on the scheduling. It will print 10 numbers, but it probably won't print 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 which is what you might expect. The problem is that it's the i
variable which has been captured, not its value at the point of the creation of the delegate. This can be solved easily with an extra local variable of the right scope:
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();
}
}
}
Deferred execution of iterator blocks
This "poor man's unit test" doesn't pass - why not?
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
}
}
}
The answer is that the code within the source of the CapitalLetters
code doesn't get executed until the iterator's MoveNext()
method is first called.
I've got some other oddities on my brainteasers page.