[ACCEPTED]-What is the worst gotcha in C# or .NET?-.net

Accepted answer
Score: 308
private int myVar;
public int MyVar
{
    get { return MyVar; }
}

Blammo. Your app crashes with no stack 2 trace. Happens all the time.

(Notice capital 1 MyVar instead of lowercase myVar in the getter.)

Score: 257

Type.GetType

The one which I've seen bite lots of people 20 is Type.GetType(string). They wonder why it works for types 19 in their own assembly, and some types like 18 System.String, but not System.Windows.Forms.Form. The answer is that it only looks 17 in the current assembly and in mscorlib.


Anonymous methods

C# 2.0 introduced 16 anonymous methods, leading to nasty situations 15 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 14 entirely depends on the scheduling. It will 13 print 10 numbers, but it probably won't 12 print 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 which 11 is what you might expect. The problem is 10 that it's the i variable which has been captured, not 9 its value at the point of the creation of 8 the delegate. This can be solved easily 7 with an extra local variable of the right 6 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 5 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 4 within the source of the CapitalLetters code doesn't get 3 executed until the iterator's MoveNext() method is 2 first called.

I've got some other oddities 1 on my brainteasers page.

Score: 200

The Heisenberg Watch Window

This can bite you badly if you're doing 33 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 32 say you have some code elsewhere using this:

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

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

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

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

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

GOTCHA!


Edit - I feel @ChristianHayter's comment 6 deserves inclusion in the main answer, because 5 it looks like an effective workaround for 4 this issue. So anytime you have a lazy-loaded 3 property...

Decorate your property with [DebuggerBrowsable(DebuggerBrowsableState.Never)] or 2 [DebuggerDisplay("<loaded on demand>")]. – Christian 1 Hayter

Score: 195

Re-throwing exceptions

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

Lots of 8 time I see code like the following

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

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

The 4 correct code is either the throw statement 3 with no args:

catch(Exception)
{
    throw;
}

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

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

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 5 the seconds portion of the timespan (2 minutes 4 and 0 seconds has a seconds value of 0).

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

Score: 81

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

This even caught out some senior 19 developers I know.

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

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

In this example your teardown 9 code should have

timer.Tick -= TimerTickEventHandler;

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

Score: 63

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

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

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

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

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

overloaded == operators and untyped containers 4 (arraylists, datasets, etc.):

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

Solutions?

  • always 3 use string.Equals(a, b) when you are comparing string types 2

  • using generics like List<string> to ensure that both 1 operands are strings.

Score: 51

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

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

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

Moral of the story : Field initialisers 1 are not run when deserializing an object

Score: 46

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

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


DateTime.ToString("r") is supposed 7 to convert to RFC1123, which uses GMT. GMT 6 is within a fraction of a second from UTC, and 5 yet the "r" format specifier does not convert to UTC, even 4 if the DateTime in question is specified 3 as Local.

This results in the following gotcha 2 (varies depending on how far your local 1 time is from UTC):

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

Whoops!

Score: 45

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

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

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

Score: 41

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

DateTime resolution

The 13 Ticks property measures time in 10-millionths 12 of a second (100 nanosecond blocks), however 11 the resolution is not 100 nanoseconds, it's 10 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 9 of (for example):

0
0
0
0
0
0
0
156254
156254
156254

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

This 6 particular behaviour varies from system to system, but there are other resolution-related gotchas in this date/time 5 API.


Path.Combine

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

If 3 the second parameter starts with a \ character, it 2 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 1 you this output:

C:\MyFolder\MySubFolder\log\
\log\
C:\MyFolder\MySubFolder\log\
\log\
Score: 39

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

Score: 36

No operator shortcuts in Linq-To-Sql

See here.

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

Score: 31

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

0

Score: 28

Value objects in mutable collections

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

mypoints[i].x = 10;

has no effect.

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


Update: This 1 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
Score: 26

Perhaps not the worst, but some parts of 3 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 2 of this could have been avoided by having 1 an Angle class instead...

Score: 22

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

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

    class A
    {
    public:
        int i;
    };

is functionally equivalent 10 to this struct definition.

    struct A
    {
        int i;
    };

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

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

Score: 19

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

Score: 19

Arrays implement IList

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

Compiles, but doesn't work:

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

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

Score: 18

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

Score: 18

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 2 "amet", while the following example works 1 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());
Score: 18

The Nasty Linq Caching Gotcha

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

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

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

GOTCHA!

Score: 18

The contract on Stream.Read is something that I've seen trip up a lot 13 of people:

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

The reason this is wrong is that 12 Stream.Read will read at most the specified number of bytes, but 11 is entirely free to read just 1 byte, even if another 10 7 bytes are available before end of stream.

It 9 doesn't help that this looks so similar 8 to Stream.Write, which is guaranteed to have written all 7 the bytes if it returns with no exception. It 6 also doesn't help that the above code works almost all the time. And 5 of course it doesn't help that there is 4 no ready-made, convenient method for reading 3 exactly N bytes correctly.

So, to plug the 2 hole, and increase awareness of this, here 1 is an example of a correct way to do this:

    /// <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;
    }
Score: 16

Today I fixed a bug that eluded for long 9 time. The bug was in a generic class that 8 was used in multi threaded scenario and 7 a static int field was used to provide lock 6 free synchronisation using Interlocked. The 5 bug was caused because each instantiation 4 of the generic class for a type has its 3 own static. So each thread got its own static 2 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 1 prints 5 10 5

Score: 16

Events

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

Score: 14

Just found a weird one that had me stuck 3 in debug for a while:

You can increment null 2 for a nullable int without throwing an excecption 1 and the value stays null.

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

Enumerables can be evaluated more than once

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

For 17 example, while writing a certain test, I 16 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 15 my surprise when File.Delete(file) throws FileNotFound!!

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

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

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

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

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

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

Score: 10
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 1 certainly doesn't make it right.

Score: 10

This is a super-gotcha that I wasted 2 days 9 troubleshooting. It didn't throw any exceptions 8 it just crashed the web-server with some 7 weird error messages. I could not reproduce the problem in DEV. Moreover 6 the experiments with the project build settings 5 somehow made it go away in the PROD, then 4 it came back. Finally I got it.

Tell me if 3 you see a problem in the following piece 2 of code:

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

So if you value your sanity:

!!! Never ever ever put any logic to Trace methods !!!

The 1 code must have looked like this:

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);
    }
}
Score: 9

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

Score: 8

There is a whole book on .NET Gotchas

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

Score: 8

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

(Similar question here.)

Score: 7

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

Score: 7

The base keyword doesn't work as expected when 7 evaluated in a debugging environment: the 6 method call still uses virtual dispatch.

This 5 wasted a lot of my time when I stumbled 4 across it and I thought I'd encountered 3 some kind of rift in the CLR's space-time, but 2 I then realized it's a known (and even somewhat 1 intentional) bug:

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

Score: 7

Static constructors are executed under lock. As 3 a result, calling threading code from static 2 constructor might result in deadlock. Here 1 is an example that demonstrates it:

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() { }
}
Score: 6

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

SPSite oSiteCollection = SPContext.Current.Site;

and later in your 2 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 1 MOSS programmer and some point.

Score: 6

ASP.NET:

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

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

Score: 6
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.";
    }
}

Error?
not all code paths return a value ...
are you kidding me? I bet all code 9 paths do return a value because every Seasons member 8 is mentioned here. It should have been checking 7 all enum members and if a member was absent 6 in switch cases then such error would be 5 meaningful, but now I should add a Default case 4 which is redundant and never gets reached 3 by code.

EDIT :
after more research on this Gotcha 2 I came to Eric Lippert's nice written and useful post but it is still kind of weird. Do 1 you agree?

Score: 5

Check this one out:

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

And output is:

List items: 123456
Collection items: 23456

Collection 5 constructor that accepts IList creates a 4 wrapper around original List, while List 3 constructor creates a new List and copies 2 all references from original to the new 1 List.

See more here: http://blog.roboblob.com/2012/09/19/dot-net-gotcha-nr1-list-versus-collection-constructor/

Score: 5

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

Score: 5

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 4 a bug-report for LINQ-to-Entites here, though 3 they don't seem to check that forum often. Perhaps 2 someone should file one for LINQ-to-SQL 1 as well?

Score: 5

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

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

does not behave 2 the same as:

((MyObject)obj) == this;

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

Score: 5

Oracle parameters have to added in order

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

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

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

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

See here for more details.

Score: 4

The recursive property gotcha

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

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

Forgetting to reference base. when overriding 2 a property:

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

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

public bool IsRecursive
{
    get { return IsRecursive; }
    set { IsRecursive = value; }
}
Score: 3

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

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

the AllowNavigation 6 solutions works in Windows forms...

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

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

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

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

Score: 3

VisibleChanged is not usually called when Visible changes.

0

Score: 3

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

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

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

Score: 2

LINQ to SQL and One-To-Many Relationships

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

Score: 2

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 13 does not have a defined relationship to 12 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 11 a NullReferenceException. Related object Category does not load the Category 10 record with ID 123.

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

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

GOTCHA!

Score: 2

I always thought value types were always on stack and 5 reference types on heap.

Well it is not so. When i saw this question recently 4 on SO (and arguably answered incorrectly) i 3 came to know its not the case.

As Jon Skeet answered 2 (giving a reference to Eric Lippert's Blog post ) its a Myth.

Considerably 1 Important Links:

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

Score: 1

The following will not catch the exception 15 in .Net. Instead it results in a StackOverflow 14 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);
}

For the commenters (and downvotes):
It would be extremely rare for 13 a stack overflow to be this obvious. However, if 12 one occurs you aren't going to catch the 11 exception and will likely spend several 10 hours trying to hunt down exactly where 9 the problem is. It can be compounded if 8 the SO occurs in little used logic paths, especially 7 on a web app where you might not know the 6 exact conditions that kicked off the issue.

This 5 is the exact same situation as the accepted 4 answer to this question (https://stackoverflow.com/a/241194/2424). The property 3 getter on that answer is essentially doing 2 the exact same thing as the above code and 1 crashing with no stack trace.

Score: 1

LinqToSQL and the empty set aggregate

See this question.

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

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

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

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

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

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

GOTCHA!

Score: 1

Sometimes the line numbers in the stack 9 trace do not match the line numbers in the 8 source code. This might happen due to inlining 7 of simple(single-line) functions for optimization. This 6 is a serious source of confusion for people 5 debugging using logs.

Edit: Example: Sometimes 4 you see a null reference exception in the 3 stack trace where it points to a line of 2 code with absolutely no chance of null reference 1 exception, like a simple integer assignment.

Score: 1
mystring.Replace("x","y")

While it looks like it should do the replacement 5 on the string it's being invoked on it actually 4 returns a new string with the replacements 3 made without changing the string it's invoked 2 on. You need to remember that strings are 1 immutable.

Score: 1

Not the worst, but one that hasn't been 7 brought up yet. Factory methods passed 6 as arguments to System.Collections.Concurrent 5 methods can be called multiple times even 4 if only one return value is ever used. Considering 3 how strongly .NET tries to protect you from 2 spurious wake-up in threading primitives 1 this can come as a surprise.

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

(Possible) Output:

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
Score: 1

Passing a capacity to List<int> instead of using the collection initializer.

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

0

Score: 0

Linq2SQL: The mapping of interface member [...] is not supported.

If you do a Linq2Sql query on an object 10 that implements an interface, you get a 9 very odd behavior. Let's say you have a 8 class MyClass that implements an interface IHasDescription, thus:

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

public partial class MyClass : IHasDescription { }

(The 7 other half of MyClass is a Linq2Sql generated class, including 6 the property Description.)

Now you write some code (usually 5 this happens in a generic method):

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

Compiles 4 fine - but you get a runtime error:

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

Now whaddaya 3 do about that? Well, it's obvious really: just 2 change your == to .Equals(), thus:

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

And everything works 1 fine now!

See here.

Score: 0

LinqToSql batches get slower with the square of the batch size

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

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

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

More Related questions