tags:

views:

7260

answers:

51

This question is similar to this one, but focused on C# and .NET.

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.

+15  A: 

I saw this one posted the other day, and I think it is pretty obscure, and painful for those that don't know

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

As that will return 0 and not 1 as most would expect

Mitchel Sellers
I hope that wouldn't actually bite people though - I really hope they wouldn't write it in the first place! (It's interesting anyway, of course.)
Jon Skeet
pre-increment actually works, it's post-increment that doesn't. I changed this.
tvanfosson
I don't think this is very obscure...
Chris Marasti-Georg
tvanfosson thanks for the fix there, I noticed it right after posting. I agree that you SHOULDN'T see it, but man I've seen much worse stuff actually.....
Mitchel Sellers
I agree with Chris, this isn't very obscure. It's very handy to be able to have the increment after the assignment.
Brian Knoblauch
At least, in C#, the results are defined, if unexpected. In C++, it could be 0 or 1, or any other result including program termination!
James Curran
This isn't a gotcha; x=x++ -> x = x, then increment x....x=++x -> increment x then x = x
Kevin
I'm teaching C# next year and that is exactly the sort of thing a student might do! And if you are not expecting it can be a trial to detect.
topcat
If i understand it Correctly, it is functionally equivalent toint temp = x;x++;x = temp;
RCIX
This Works: int x = 0; x++; return x;
yelinna
@Kevin: I don't think it's quite that simple. If x=x++ were equivalent to x=x followed by x++, then the result would be x = 1. Instead, I think what happens is first the expression to the right of the equals sign is evaluated (giving 0), then x is incremented (giving x = 1), and finally the assignment is performed (giving x = 0 once again).
Tim Goodman
@Tim Goodman I agree, what I said made no sense.
Kevin
It is not so obvious at all. In C/C++, this is actually undefined code - x could be 0 or 1, depending on the compiler!
BlueRaja - Danny Pflughoeft
This is no way limited to C#
Joe Philllips
x=++x; return x; //is what you're looking for
csauve
A: 

Ditto with mystring.Replace("x","y") While it looks like it should do the replacement on the string it's being invoked on it actually returns a new string with the replacements made without changing the string it's invoked on. You need to remember that strings are immutable.

tvanfosson
It's immutable; myString is not changed, it returns a new string where 'x' has been replaced by 'y'.
Robin Bennett
A good convention (unfortunately not used much in .net) is to use two patterns: "string GetReplaced()" for a call that returns a copy and "void Replace()" for methods that alter the original object. The distinct behaviours/usages are then rather difficult to mix up.
Jason Williams
Ditto? What do you mean? (I know you mean that .Replace returns the modified string, but does not change mystring. You really should edit your answer so that it stands alone).
aaaa bbbb
+107  A: 

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.

Jon Skeet
The iterator example is devious!
Jimmy
However, it IS how it's supposed to work. It's tricky to get your head around it, but with some playing around with it it's actually pretty useful.
Erik van Brakel
why not split this into 3 answer so we can vote each one up instead of all together?
chakrit
@chakrit: In retrospect, that would probably have been a good idea, but I think it's too late now. It might also have looked like I was just trying to get more rep...
Jon Skeet
Actually Type.GetType works if you provide the AssemblyQualifiedName.Type.GetType("System.ServiceModel.EndpointNotFoundException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
chilltemp
@Jon, I like your concerns about splitting this across 3 answers, but in all honesty you'd deserve the extra rep :)
DoctaJonez
Hey, wait!!! I can't get the # 1) of your brainteasers..."Derived.Foo(object)"can you explain why int is resolved as an object? O_O;;
kentaromiura
@kentaromiura: The overload resolution starts at the most derived type and works up the tree - but only looking at methods *originally declared* in the type it's looking at. Foo(int) overrides the base method, so isn't considered. Foo(object) is applicable, so overload resolution stops there. Odd, I know.
Jon Skeet
Relevant to your "Anonymous Methods" gotcha, I just came across this the other day: http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
Funka
+18  A: 
Jimmy
You've got extra spaces in there which make it all wrong - but if you take the spaces out, the last line will still be true as "my" + "string" is still a constant.
Jon Skeet
ack! you're right :) ok, I edited a bit.
Jimmy
a warning is generated on such uses.
chakrit
Yes, one of the biggest flaws with the C# language is the == operator in class Object. They should have forced us to use ReferenceEquals.
erikkallen
Anyone that has programmed in C would think of things this way straight off the bat. Course, you'd think that it should be `==(string)` isn't it virtual?
Earlz
@Earlz: operators in C# are never virtual.
Jimmy
@Jimmy, ah, where thats where the problem is then.
Earlz
+55  A: 

Here's another time one that gets me:

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

TimeSpan.Seconds is the seconds portion of the timespan (2 minutes and 0 seconds has a seconds value of 0).

TimeSpan.TotalSeconds is the entire timespan measured in seconds (2 minutes has a total seconds value of 120).

Jon B
I did not know that. Good thing I usually use Environment.TickCount
MusiGenesis
That one has burned me before.
kurious
Burned me last month. Ow.
Dave Markle
About a year or so ago, this one burned me for weeks
Dinah
Yeah, that one has got me too. I think it should be TimeSpan.SecondsPart or something to make it more clear what it represents.
Dan Diplo
You should add a comment explaining why it's wrong. I'm pretty sure it's because the timespan is probably stored like 2min30 seconds and that .Seconds returns 30 and .TotalSeconds returns 150.
GoodEnough
OK - added explanation
Jon B
On re-reading this, I have to wonder why `TimeSpan` even *has* a `Seconds` property at all. Who gives a rat's ass what the seconds portion of a timespan is, anyway? It's an arbitrary, unit-dependent value; I can't conceive of any practical use for it.
MusiGenesis
+101  A: 
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)

Eric Z Beard
Spent over 30 minutes wondering why I was getting Stackoverflow Exceptions because of this once...
Matthew Ruston
Done this one. Very nasty.
MusiGenesis
Yup. Been hit by that one.
chris
and SO appropriate for this site :)
gbjbaanb
I put underscores on the private member, helps a lot!
chakrit
This one takes the prize due to its evilocity and Stack Overflow-related irony.
MusiGenesis
If i'm not mistaken, resharper can warn you of this possible problem
faulty
I use automatic properties where I can, stops this kind of problem alot ;)
TWith2Sugars
I've had same problem when overriding a property and forgetting to put base.PropertyName instead of PropertyName.
Benjol
+1 for underscore prefix
Chris Hynes
I know it is not possible to programatically ascertain an endless loop, but this is such a basic one, I've always thought VS should issue a warning on this one!
johnc
I've actually seen this kind of thing crash the VS ide when the property is exposed during design time.
Peter LaComb Jr.
This is a GREAT reason to use prefixes for your private fields (there are others, but this is a good one): _myVar, m_myVar
jrista
@jrista: O please NO... not m_ ... aargh the horror...
fretje
@fretje: if you like crashes better than m_'s...
Turro
This one is done really fast, but can eat up some time. Nasty!
Michael Barth
just use _myVariableName for the private field.
James Westgate
I think "my" prefix works great:private int myAge;public int Age{ get { return myAge; }}
Stefan
+9  A: 

Garbage collection and Dispose(). Although you don't have to do anything to free up memory, you still have to free up resources via Dispose(). This is an immensely easy thing to forget when you are using WinForms, or tracking objects in any way.

Jeff Kotula
The using() block neatly solves this problem. Whenever you see a call to Dispose, you can immediately and safely refactor to use using().
Jeremy Frey
I think the concern was *implementing* IDisposable correctly.
Mark Brackett
This is a big gotcha in .NET compact framework too, where resources is severely limited.
chakrit
On the other hand, the using() habit can bite you unexpectedly, like when working with PInvoke. You don't want to dispose something that the API is still referencing.
MusiGenesis
Implementing IDisposable correctly is very hard to and understand even the best advice I have found on this (.NET Framework Guidelines) can be confusing to apply until you finally "get it".
Quibblesome
A: 

you cannot assign null to a DateTime variable in C# 2.0

devio
If you use DateTime? you can. DateTime? is the equivalent of Nullable<DateTime>
Shaun Bowe
This is so much part of the .NET core behaviour, you can hardly consider it a gotcha.
Konrad Rudolph
"Gotchas" are things that .Net allows you to do even though they're sure to hit you where the good lord split you.
MusiGenesis
A: 

Some code:

        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 - in this query check all integers in List; q2 - check integers until find "right" integer.

That should be somewhat obvious... q1 has to check the entire list to ensure there is only 1 match.
CodeMonkey1
+4  A: 

If you're coding for MOSS and you get a site reference this way:

SPSite oSiteCollection = SPContext.Current.Site;

and later in your code you say:

oSiteCollection.Dispose();

From MSDN:

If you create an SPSite object, you can use the Dispose method to close the object. However, if you have a reference to a shared resource, such as when the object is provided by the GetContextSite method or Site property (for example, SPContext.Current.Site), do not use the Dispose method to close the object, but instead allow Windows SharePoint Services or your portal application to manage the object. For more information about object disposal, see Best Practices: Using Disposable Windows SharePoint Services Objects.

This happens to every MOSS programmer and some point.

cciotti
+12  A: 

When you start a process (using System.Diagnostics) that writes to the console, but you never read the Console.Out stream, after a certain amount of output your app will appear to hang.

The same can still happen when you redirect both stdout and stderr and use two ReadToEnd calls in sequence. For safe handling of both stdout and stderr you have to create a read thread for each of them.
Sebastiaan Megens
+10  A: 

foreach loops variables scope!

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

prints five "amet", while the following example works fine

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());
Brian J Cardiff
This is essentially equivalent to Jon's example with anonymous methods.
Mehrdad Afshari
Save that it is even more confusing with foreach where the "s" variable is easier to mix with scoped variable. With common for-loops the index variable clearly is the same one for each iteration.
Mikko Rantanen
http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx and yes, wish the variable was scoped "properly".
romkyns
+4  A: 

I frequently have to remind myself that DateTime is a value type, not a ref type. Just seems too weird to me, especially considering the variety of constructors for it.

jcollum
I constantly type lowercase datetime all the time... luckily intellisense fix it for me :-)
chakrit
Why should this matter? DateTime is immutable anyway and I can't see a situation where you actually need to know if it is a reference type or not.
Stefan Steinegger
+5  A: 

There is a whole book on .NET Gotchas

My favourite is the one where you create a class in C#, inherit it to VB and then attempt to re-inherit back to C# and it doesnt work. ARGGH

MikeJ
I guess this is purely Visual Studio weirdness.
Anton Gogolev
That's pretty funny.. Anton is probably right.
Big Endian
+9  A: 

Value objects in arrays

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

mypoints[i].x = 10;

has no effect.

mypoints[i] returns a copy of a Point value object. C# happily lets you modify a field of the copy. Silently doing nothing.


Update: This appears to be fixed in C# 3.0:

Cannot modify the return value of 'System.Collections.Generic.List<Foo>.this[int]' because it is not a variable
Bjarke Ebert
I can see why that is confusing, considering that it does indeed work with arrays (contrary to your answer), but not with other dynamic collections, like List<Point>.
Lasse V. Karlsen
You're right. Thanks. I fixed my answer :).`arr[i].attr=` is special syntax for arrays that you cannot code in library containers ;(.Why is (<value expression>).attr = <expr> allowed at all? Can it ever make sense?
Bjarke Ebert
+22  A: 

If you count ASP.NET, I'd say the webforms lifecycle is a pretty big gotcha to me. I've spent countless hours debugging poorly written webforms code, just because a lot of developers just don't really understand when to use which event handler (me included, sadly).

Erik van Brakel
That's why I moved to MVC... viewstate headaches...
chakrit
There was a whole other question devoted specifically to ASP.NET gotchas (deservedly so). The basic concept of ASP.NET (making web apps seem like windows apps for the developer) is so horribly misguided that I'm not sure it even counts as a "gotcha".
MusiGenesis
MusiGenesis I wish I could up vote your comment a hundred times.
csauve
+69  A: 

Re-throwing exceptions

A gotcha that gets lots of new developers, is the re-throw exception semantics.

Lots of time I see code like the following

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

The problem is that it wipes the stack trace and makes diagnosing issues much harder, cause you can not track where the exception originated.

The correct code is either the throw statement with no args

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

Or wrapping the exception in another one, and using inner exception to get the original stack trace:

catch(Exception e) 
{
   // Do stuff 
   throw new MySpecialException(e); 
}
Sam Saffron
Very luckily, I got taught about this in my first week by someone and find it in more senior developers' code.Is: catch() { throw; } The same as the second code snippet? catch(Exception e) { throw; } only it doesn't create an Exception object and populate it?
StuperUser
Besides the error of using throw ex (or throw e) instead of just throw, I have to wonder what cases there are when it's worth catching an exception only to throw it again.
Kyralessa
@Kyralessa: there are many cases: for instance, if you want to rollback a transaction, before the caller gets the exception. You rollback and then rethrow.
Martinho Fernandes
I see this all the time where people catch and rethrow exceptions just because they are taught that they must catch all exceptions, not realising that it will be caught further up the call stack.It drives me nuts.
James Westgate
A: 

The following will not catch the exception in .Net. Instead it results in a StackOverflow exception.

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);
}
Chris Lively
Once the stack overflows, no more code can execute, including the exception handler. I'm not sure this is really a gotcha, but I guess it could be confusing if you weren't used to the idea.
Chris
This is just bad code, not a gotcha.
Shaul
+3  A: 

MemoryStream.GetBuffer() vs MemoryStream.ToArray(). The former returns the whole buffer, the latter just the used portion. Yuck.

Jon B
Yeah, streams can bite you if you're unaware. I used to have a really bad habit of not remembering to close them.
MusiGenesis
@MusiGenesis, use using(Stream stream = new ...) { } .
tuinstoel
+5  A: 

My worst one so far I just figured out today... If you override object.Equals(object obj), you can wind up discovering that:

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

does not behave the same as:

((MyObject)obj) == this;

One will call your overriden function, the other will NOT.

GWLlosa
I used to work with some ex-Java guys who loved to override anything they could get their hands on, and they were constantly screwing each other up with this one. I always used ==, so nothing they did ever affected me.
MusiGenesis
I thought you could overload the == operator as well...
RCIX
You can override the == operator, but overriding the .Equals() won't do it for you. So you could hypothetically override both .Equals() and ==, and have them do different things :\
GWLlosa
You can **override** Equals and **overload** ==. The difference is subtle but very important. More info here. http://stackoverflow.com/questions/1766492/c-overloading-operator-versus-equals/1849288#1849288
Sam
+14  A: 

For C/C++ programmers, the transition to C# is a natural one. However, the biggest gotcha I've run into personally (and have seen with others making the same transition) is not fully understanding the difference between classes and structs in C#.

In C++, classes and structs are identical; they only differ in the default visibility, where classes default to private visibility and structs default to public visibility. In C++, this class definition

    class A
    {
    public:
        int i;
    };

is functionally equivalent to this struct definition.

    struct A
    {
        int i;
    };

In C#, however, classes are reference types while structs are value types. This makes a BIG difference in (1) deciding when to use one over the other, (2) testing object equality, (3) performance (e.g., boxing/unboxing), etc.

There is all kinds of information on the web related to the differences between the two (e.g., here). I would highly encourage anyone making the transition to C# to at least have a working knowledge of the differences and their implications.

Matt Davis
+4  A: 

Dictionary<,>: "The order in which the items are returned is undefined". This is horrible, because it can bite you sometimes, but work others, and if you've just blindly assumed that Dictionary is going to play nice ("why shouldn't it? I thought, List does"), you really have to have your nose in it before you finally start to question your assumption.

(Similar question here.)

Benjol
Uh, List<T> does play nice, right? Right? Coz I need to rewrite some stuff if it doesn't.
MusiGenesis
Ask Reflector if it does.
Kyralessa
Of course List<T> plays nice. And use SortedDictionary<T> if the order of items matters to you.
Allon Guralnek
List<T> maintains the order in which items are added, however when you call GetEnumerator (i.e. in a foreach loop), the enumerator returned is NOT guranteed to preserve order. You would have to loop through by index to gurantee the order when processing.
ck
@ck, do you have a link for that? Lot's of people are going to be surprised, I believe...
Benjol
@ck: Wrong. List<T>.GetEnumerator **does** preserve order.
SLaks
I think @ck is suggesting that there are no guarantees implied in the contract that the enumeration will happen in order.
Will
+10  A: 

DateTime.ToString("dd/MM/yyyy"); This will actually not always give you dd/MM/yyyy but instead it will take into account the regional settings and replace your date separator depending on where you are. So you might get dd-MM-yyyy or something alike.

The right way to do this is to use DateTime.ToString("dd'/'MM'/'yyyy");

Maltrap
Changed mm to MM - mm is minutes, and MM is months. Another gotcha, I guess...
Kobi
I could see how this would be a gotcha if you didn't know it (I didn't)...but I'm trying to figure out when you would want the behavior where you're specifically trying to print a date that doesn't match what your regional settings are.
Beska
@Beska: Because you are writing to a file, that needs to be in a specific format, with a specified date format.
GvS
I am of the opinion that the defaults being localized is worse than the other way around. At least of the developer ignored localization completely the code *works* on machines localized differently. This way, the code probably doesn't work.
Joshua
Actually I believe the correct way to do this would be `DateTime.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);`
BlueRaja - Danny Pflughoeft
+6  A: 

Today I fixed a bug that eluded for long time. The bug was in a generic class that was used in multi threaded scenario and a static int field was used to provide lock free synchronisation using Interlocked. The bug was caused because each instantiation of the generic class for a type has its own static. So each thread got its own static field and it wasn't used a lock as intended.

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

This prints 5 10 5

Pratik
Interesting... good to know!
Arcturus
you can have a non-generic base class, that defines the statics, and inherit the generics from it. Although I never fell for this behavior in C# - I still remember the long debugging hours of some C++ templates... Eww! :)
Paulius Maruška
+1  A: 

The worst thing it happen to me was the webBrowser documentText issue:

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

the AllowNavigation solutions works in Windows forms...

but in compact framework the property doesn't exists...

...so far the only workaround I found was to rebuild the browser control:

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

But doing so, you need to handle the browser history at hands ... :P

kentaromiura
+13  A: 

I'm a bit late to this party, but I have two gotchas that have both bitten me recently:

DateTime resolution

The Ticks property measures time in 10-millionths of a second (100 nanosecond blocks), however the resolution is not 100 nanoseconds, it's about 15ms.

This code:

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

will give you an output of (for example):

0
0
0
0
0
0
0
156254
156254
156254

Similarly, if you look at DateTime.Now.Millisecond, you'll get values in rounded chunks of 15.625ms: 15, 31, 46, etc.


Path.Combine

A great way to combine file paths, but it doesn't always behave the way you'd expect.

If the second parameter starts with a \ character, it won't give you a complete path:

This code:

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

Gives you this output:

C:\MyFolder\MySubFolder\log\
\log\
C:\MyFolder\MySubFolder\log\
\log\
Damovisa
The quantization of times in ~15ms intervals isn't because of a lack of accuracy in the underlying timing mechanism (I neglected to elaborate on this earlier). It's because your app is running inside a multi-tasking OS. Windows checks in with your app every 15ms or so, and during the little time slice it gets, your app processes all of the messages that were queued up since your last slice. All of your calls within that slice return the exact same time because they're all made at effectively the exact same time.
MusiGenesis
@MusiGenesis: I know (now) how it works, but it seems misleading to me to have such a precise measure which isn't really that precise. It's like saying that I know my height in nanometres when really I'm just rounding it to the nearest ten million.
Damovisa
DateTime is quite capable of storing up to a single tick; it's DateTime.Now that isn't using that accuracy.
Ruben
The extra '\' is a gotcha to many unix/mac/linux folks. In Windows, if there's a leading '\', it's mean that we want to go the drive's root (i.e. C:\) try it in a `CD` command to see what I mean.... 1) Goto `C:\Windows\System32` 2) Type `CD \Users` 3) Woah! Now you're at `C:\Users` ... GOT IT? ... Path.Combine(@"C:\Windows\System32", @"\Users") should returns `\Users` which means precisely the `[current_drive_here]:\Users`
chakrit
Even without the 'sleep' this performs the same way. This has nothing to do with the app being scheduled every 15 ms. The native function called by DateTime.UtcNow, GetSystemTimeAsFileTime, appears to have a poor resolution.
Jimbo
+10  A: 
[Serializable]
class Hello
{
    readonly object accountsLock = new object();
}

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

Moral of the story : Field initialiser are not runned when deserializing an object

Nicolas Dorier
+3  A: 

ASP.NET:

If you are using Linq-To-SQL, you call SubmitChanges() on the data context and it throws an exception (e.g. duplicate key or other constraint violation), the offending object values remain in your memory while you are debugging, and will be resubmitted every time you subsequently call SubmitChanges().

Now here's the real kicker: the bad values will remain in memory even if you push the "stop" button in your IDE and restart! I don't understand why anyone thought this was a good idea - but that little ASP.NET icon that pops up in your system tray stays running, and it appears to save your object cache. If you want to flush your memory space, you have to right-click that icon and forcibly shut it down! GOTCHA!

Shaul
Surely you mean pause, not stop.
David B
David - no, I meant what I said. Even if you push "Stop", the little ASP.NET icon keeps on running in your taskbar. That's why it's a gotcha!
Shaul
+1 because of the first part, no independent knowledge of the kicker.
Maslow
+5  A: 

MS SQL Server can't handle dates before 1753. Significantly, that is out of synch with the .NET DateTime.MinDate constant, which is 1/1/1. So if you try to save a mindate, a malformed date (as recently happened to me in a data import) or simply the birth date of William the Conqueror, you're gonna be in trouble. There is no built-in workaround for this; if you're likely to need to work with dates before 1753, you need to write your own workaround.

Shaul
Quite frankly I think MS SQL Server has this right and .Net is wrong. If you do the research then you know that dates prior to 1751 get funky due to calendar changes, days completely skipped, etc. Most RDBMs have some cut off point. This should give you a starting point: http://www.ancestry.com/learn/library/article.aspx?article=3358
Chris Lively
Also, the date is 1753.. Which was pretty much the first time that we have a continuous calendar without dates being skipped. SQL 2008 introduced the Date and datetime2 datetype which can accept dates from 1/1/01 to 12/31/9999. However, date comparisons using those types should be viewed with suspicion if you are really comparing pre-1753 dates.
Chris Lively
Oh, right, 1753, corrected, thanks.
Shaul
+2  A: 

LINQ to SQL and One-To-Many Relationships

This is a lovely one that has bitten me a couple times, and MS left it to one of their own developers to put it in her blog. I can't put it any better than she did, so take a look there.

Shaul
+2  A: 

Linq-To-Sql and the database/local code ambiguity

Sometimes Linq just can't work out whether a certain method is meant to be executed on the DB or in local code.

See here and here for the problem statement and the solution.

Shaul
+21  A: 

Leaking memory because you didn't un-hook events.

This even caught out some senior developers I know.

Imagine a WPF form with lots of things in it, and somewhere in there you subscribe to an event. If you don't unsubscribe then the entire form is kept around in memory after being closed and de-referenced.

I believe the issue I saw was creating a DispatchTimer in the WPF form and subscribing to the Tick event, if you don't do a -= on the timer your form leaks memory!

In this example your teardown code should have

timer.Tick -= TimerTickEventHandler;

This one is especially tricky since you created the instance of the DispatchTimer inside the WPF form, so you would think that it would be an internal reference handled by the Garbage Collection process... unfortunately the DispatchTimer uses a static internal list of subscriptions and services requests on the UI thread, so the reference is 'owned' by the static class.

Timothy Walters
This has caught me a couple of times.
ChrisF
The trick is to always release all event subscriptions you create. If you begin to rely on Forms doing it for you, you can be sure you'll get into the habit and one day will forget to release an event somewhere where it needs to be done.
Jason Williams
There is an MS-connect suggestion for weak reference events [here](https://connect.microsoft.com/VisualStudio/feedback/details/94154/add-support-for-weak-reference-event-handlers) which would solve this problem, though in my opinion we should just entirely replace the incredibly poor event model with a weakly-coupled one, like that used by CAB.
BlueRaja - Danny Pflughoeft
+1 from me, thanks! Well, no thanks for the code review work I had to do!
Bob Denny
+10  A: 

The Nasty Linq Caching Gotcha

See my question that led to this discovery, and the blogger who discovered the problem.

In short, the DataContext keeps a cache of all Linq-to-Sql objects that you have ever loaded. If anyone else makes any changes to a record that you have previously loaded, you will not be able to get the lastest data, even if you explicitly reload the record!

This is because of a property called ObjectTrackingEnabled on the DataContext, which by default is true. If you set that property to false, the record will be loaded anew every time... BUT... you can't persist any changes to that record with SubmitChanges().

GOTCHA!

Shaul
Iv just spent a day and a half (and loads of hair!) chasing down this BUG...
pzycoman
+9  A: 

Maybe not really a gotcha because the behavior is written clearly in MSDN, but has broken my neck once because I found it rather counter-intuitive:

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

This guy leaves the "nice.pic" file locked until the image is disposed. At the time I faced it I though it would be nice to load icons on the fly and didn't realize (at first) that I ended up with dozens of open and locked files! Image keeps track of where it had loaded the file from...

How to solve this? I thought a one liner would do the job. I expected an extra parameter for FromFile(), but had none, so I wrote this...

using (Stream fs = new FileStream("nice.pic", FileMode.Open, FileAccess.Read))
{
    image = System.Drawing.Image.FromStream(fs);
}
jdehaan
I agree that this behavior makes no sense. I can't find any explanation for it other than "this behavior is by design".
MusiGenesis
Oh and what's great about this workaround is if you try to call Image.ToStream (I forget the exact name off hand) later it won't work.
Joshua
+6  A: 

No operator shortcuts in Linq-To-Sql

See here.

In short, inside the conditional clause of a Linq-To-Sql query, you cannot use conditional shortcuts like || and && to avoid null reference exceptions; Linq-To-Sql evaluates both sides of the OR or AND operator even if the first condition obviates the need to evaluate the second condition!

Shaul
+4  A: 

Enumerables can be evaluated more than once

It'll bite you when you have a lazily-enumerated enumerable and you iterate over it twice and get different results. (or you get the same results but it executes twice unnecessarily)

For example, while writing a certain test, I needed a few temp files to test the logic:

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

Imagine my surprise when File.Delete(file) throws FileNotFound!!

What's happening here is that the files enumerable got iterated twice (the results from the first iteration are simply not remembered) and on each new iteration you'd be re-calling Path.GetTempFilename() so you'll get a different set of temp filenames.

The solution is, of course, to eager-enumerate the value by using ToArray() or ToList():

var files = Enumerable.Range(0, 5)
    .Select(i => Path.GetTempFileName())
    .ToArray();

This is even scarier when you're doing something multi-threaded, like:

foreach (var file in files)
    content = content + File.ReadAllText(file);

and you find out content.Length is still 0 after all the writes!! You then begin to rigorously checks that you don't have a race condition when.... after one wasted hour... you figured out it's just that tiny little Enumerable gotcha thing you forgot....

chakrit
+2  A: 

Related object and foreign key out of sync

Microsoft have admitted to this bug.

I have a class Thing, which has a FK to Category. Category does not have a defined relationship to Thing, so as not to pollute the interface.

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

Output:

Category ID: 0

Similarly:

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

throws a NullReferenceException. Related object Category does not load the Category record with ID 123.

After you submit changes to the DB, though, these values do get synched. But before you visit the DB, the FK value and related object function practically independently!

(Interestingly, the failure to synch the FK value with the related object only seems to happen when there is no child relationship defined, i.e. Category has no "Things" property. But the "load on demand" when you just set the FK value NEVER works.)

GOTCHA!

Shaul
+17  A: 

The Heisenberg Watch Window

This can bite you badly if you're doing load-on-demand stuff, like this:

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

Now let's say you have some code elsewhere using this:

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

Now you want to debug your CreateMyObj() method. So you put a breakpoint on Line 3 above, with intention to step into the code. Just for good measure, you also put a breakpoint on the line above that says _myObj = CreateMyObj();, and even a breakpoint inside CreateMyObject() itself.

The code hits your breakpoint on Line 3. You step into the code. You expect to enter the conditional code, because _myObj is obviously null, right? Uh... so... why did it skip the condition and go straight to return _myObj?! You hover your mouse over _myObj... and indeed, it does have a value! How did THAT happen?!

The answer is that your IDE caused it to get a value, because you have a "watch" window open - especially the "Autos" watch window, which displays the values of all variables/properties relevant to the current or previous line of execution. When you hit your breakpoint on Line 3, the watch window decided that you would be interested to know the value of MyObj - so behind the scenes, ignoring any of your breakpoints, it went and calculated the value of MyObj for you - including the call to CreateMyObj() that sets the value of _myObj!

That's why I call this the Heisenberg Watch Window - you cannot observe the value without affecting it... :)

GOTCHA!

Shaul
yup, that's what I spent yesterday doing :/
Joel in Gö
brilliant find! you're not a programmer, you're a real debugger.
this. __curious_geek
Geek - thanks! :)
Shaul
This was driving me crazy last week, but for a static constructor as opposed to a lazy loader. No matter how hard I tried, I could not get it to hit my breakpoints, even though the code was clearly being run. Ended up having to use the low-tech `Debug.WriteLine` approach.
Aaronaught
I've run into this even hovering over the variable, not just the watch window.
Richard Morgan
+1  A: 

LinqToSQL and the empty set aggregate

See this question.

If you have a LinqToSql query on which you are running an aggregate - if your resultset is empty, Linq can't work out what the data type is, even though it's been declared.

e.g. Suppose you have a table Claim with a field Amount, which in LinqToSql is of type decimal.

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

Obviously no claims have an ID less than zero, so you'd expect to see sum = null, right? Wrong! You get an InvalidOperationException, because the SQL query underlying the Linq query doesn't have a data type. You have to tell Linq explicitly that it's a decimal! Thus:

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

This is really dumb and IMO a design bug on Microsoft's part.

GOTCHA!

Shaul
+2  A: 

For both LINQ-to-SQL and 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!

There's a bug-report for LINQ-to-Entites here, though they don't seem to check that forum often. Perhaps someone should file one for LINQ-to-SQL as well?

BlueRaja - Danny Pflughoeft
+2  A: 
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!"

Yes, this behavior is documented, but that certainly doesn't make it right.

BlueRaja - Danny Pflughoeft
I disagree - when a word is in all caps, it can have special meaning that you don't want to mess up with Title Case, e.g. "president of the USA" -> "President Of The USA", not "President Of The Usa".
Shaul
@Shaul: In which case, they should specify this as a [parameter](https://connect.microsoft.com/VisualStudio/feedback/details/546435/textinfo-totitlecase-simple-fix-which-doesnt-break-backwards-compatibility) to avoid confusion, because I've never met anyone who expected this behaviour ahead of time - which makes this a *gotcha*!
BlueRaja - Danny Pflughoeft
OK, that's fair enough.
Shaul
+6  A: 

Perhaps not the worst, but some parts of the .net framework use degrees while others use radians (and the documentation that appears with Intellisense never tells you which, you have to visit MSDN to find out)

All of this could have been avoided by having an Angle class instead...

BlueRaja - Danny Pflughoeft
+1  A: 

VisibleChanged is not usually called when Visible changes.

BlueRaja - Danny Pflughoeft
+3  A: 

The DesignMode property in all UserControls does not actually tell you if you are in design mode.

BlueRaja - Danny Pflughoeft
+3  A: 

The base keyword doesn't work as expected when evaluated in a debugging environment: the method call is still executed polymorphically. This caused me a lot of grief when I stumbled across it and I thought I'd encountered some kind of rift in the CLR's space-time, but I then realized it's a known (and even somewhat intentional) bug:

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

Will Vousden
A: 

LinqToSql batches get slower with the square of the batch size

Here's the question (and answer) where I explored this problem.

In a nutshell, if you try to build up too many objects in memory before calling DataContext.SubmitChanges(), you start experiencing sluggishness at a geometric rate. I have not confirmed 100% that this is the case, but it appears to me that the call to DataContext.GetChangeSet() causes the data context to perform an equivalence evaluation (.Equals()) on every single combination of 2 items in the change set, probably to make sure it's not double-inserting or causing other concurrency issues. Problem is that if you have very large batches, the number of comparisons increases proportionately with the square of n, i.e. (n^2+n)/2. 1,000 items in memory means over 500,000 comparisons... and that can take a heckuva long time.

To avoid this, you have to ensure that for any batches where you anticipate large numbers of items, you do the whole thing within transaction boundaries, saving each individual item as it is created, rather than in one big save at the end.

Shaul
+4  A: 

Using default parameters with virtual methods

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

Output:
derived base

BlueRaja - Danny Pflughoeft
Damn, dude, you really have it in for C#, don't you?
MusiGenesis
@Musi: No, it's just the language I work with the most :)
BlueRaja - Danny Pflughoeft
+1 Holy hell, that is a *weird* old gotcha!
Shaul
+3  A: 

Arrays implement IList

But don't implement it. When you call Add, it tells you that it doesn't work. So why does a class implement an interface when it can't support it?

Compiles, but doesn't work:

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

We have this issue a lot, because the serializer (WCF) turns all the ILists into arrays and we get runtime errors.

Stefan Steinegger
+4  A: 

Events

I never understood why events are a language feature. They are complicated to use: you need to check for null before calling, you need to unregister (yourself), you can't find out who is registered (eg: did I register?). Why isn't an event just a class in the library? Basically a specialized List<delegate>?

Stefan Steinegger
Also, multithreading is painful. All these issues but the null-thing are fixed in CAB (whose features should really just be built into the language) - events are declared globally, and any method can declare itself to be a "subscriber" of any event. My only issue with CAB is that the global event names are strings rather than enums *(which could be fixed by more intelligent enums, like Java has, which inherently work as strings!)*. CAB is difficult to set up, but there is a simple open-source clone available [here](http://www.codeproject.com/KB/dotnet/EventBroker.aspx).
BlueRaja - Danny Pflughoeft
+2  A: 

The recursive property gotcha

Not specific to C#, I think, and I'm sure I've seen it mentioned elsewhere on SO (this is the question that reminded me of it)

It can happen two ways, but the end result is the same:

Forgetting to reference base. when overriding a property:

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

Changing from auto- to backed- properties, but not quite going all the way:

public bool IsRecursive
{
    get { return IsRecursive; }
    set { IsRecursive = value; }
}
Benjol
Not really a gotcha, just careless coding.
Shaul
Is there a question for that? :)
Benjol
A: 

Oracle parameters have to added in order

This is a major gotcha in the ODP .Net implementation of parameterized queries for Oracle.

When you add parameters to a query, the default behavior is that the parameter names are ignored, and the values are used in the order in which they were added.

The solution is to set the BindByName property of the OracleCommand object to true - it's false by default... which is qualitatively (if not quite quantitatively) something like having a property called DropDatabaseOnQueryExecution with a default value of true.

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

See here for more details.

Shaul