.NET Daily Quiz archive - week 5

.NET Daily quiz #021

I wrote an application with two assemblies:

// Assembly 1
using System;
namespace Assembly1
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly2.Class1.PrintNumber();
            Console.ReadLine();
        }
    }
}
// Assembly 2
using System;
namespace Assembly2
{
    public class Class1
    {
        public static void PrintNumber(int optional = 10)
        {
            Console.WriteLine(optional);
        }
    }
}

When I compile this application I get the expected output — 10. Later I recompile and deploy Assembly 2 with the following change:

public static void PrintNumber(int optional = 20)
{
      // etc

The output I receive when I run the application is not what I expected. Why not?

Answer:

(This answer is a direct copy & paste from my colleague Ben Fox, whose answer had more detail than I prepared)

Optional parameters are a compiler service — the C# compiler puts metadata attributes on the optional parameters to indicate how they should be treated. E.g.

// Library assembly
static void Test(int i, int j = 0, string s = "this")
{
    Console.WriteLine(s, i, j);
}

Compiles to something like:

static void Test(int i, [Optional, DefaultParameterValue(0)] int j, [Optional, DefaultParameterValue("this")] string s)
{
    Console.WriteLine(s, i, j);
}

The compiler which compiles a call to this method automatically substitutes in the appropriate values in the calling assembly. E.g.

// Calling assembly
static void Main()
{
    Assembly2.Thing.Test(10);
    Assembly2.Thing.Test(11, j: 10);
    Assembly2.Thing.Test(12, s: "that");
    Assembly2.Thing.Test(13, s: "the other", j: 23);
}

Compiles to something like:

private static void Main()
{
    Assembly2.Thing.Test(10, 0, "this");
    Assembly2.Thing.Test(11, 10, "this");
    Assembly2.Thing.Test(12, 0, "that");
    Assembly2.Thing.Test(13, 23, "the other");
}

Thus the behaviour described in the quiz: because the old default values are also compiled into the calling assembly you must recompile both assemblies in order to reflect the new default values.

My meagre addition
The upshot is that optional parameters, when omitted by client code, are essentially passed through as a const argument from the client assembly. Optional parameters are syntactic sugar and importantly that sugar is on the client code, not the receiving end! Avoid changing optional parameters, especially when it’s a breaking change. Consider when you should prefer optional arguments to overloads and vice versa — more on this in an upcoming quiz.


.NET Daily Quiz #022

I write the following program:

using System;
using System.Collections.Generic;
class Program
{
    static void Main(string[] args)
    {
        var strings = new List();
        DoStuff(strings);
        var objects = new List();
        DoStuff(objects);
        DoStuff(new[] {"hello", "there"});
        Console.ReadLine();
    }
    static void DoStuff(object o)
    {
        Console.WriteLine("I found an object!!");
    }
    static void DoStuff(IEnumerable objects)
    {
        Console.WriteLine("I found a collection of objects!!");
    }
}

I’m using the C# 4 compiler. What is the result when I compile targeting .NET 3.5? What about .NET 4? If they’re different, why?

Answer:

Output (.NET 3.5):
I found an object!!
I found a collection of objects!!
I found a collection of objects!!

Output (.NET 4):
I found a collection of objects!!
I found a collection of objects!!
I found a collection of objects!!

The reason is that in .NET 3.5 generic parameters were not covariant. This is just a fancy way of saying that IEnumerable<T> (and all other generic types) had no implicit conversions to IEnumerable<G> where G is a super-type of T. Keeping in mind that overload resolution is determined at compile time, this means that while List<object> has an implicit conversion to IEnumerable<object> (because a List<object> is an IEnumerable<object>) there is no covariant conversion from List<string> to IEnumerable<object> (or even IEnumerable<string> to IEnumerable<object>).

Importantly you can see that there is a conversion from string[] to IEnumerable<object> — arrays have been covariant since .NET 1.0. The compiler sees a conversion from string[] to IEnumerable<object> because there is a covariant conversion from string[] to object[] and an object[] is an IEnumerable<object>.

Since .NET 4 all generic type parameters in interfaces (and delegates) have respected covariance (and contravariance, to be introduced in a future quiz). The compiler recognizes the covariant conversion from IEnumerable<string> to IEnumerable<object>.

There are a ton of good resources for covariance and contravariance on the web. For a .NET perspective you could start at this article.


.NET Daily Quiz #023

Most people know that finalization is significantly more costly than disposing of an object. Do you know why? Under what conditions should you prefer a finalizer to IDisposable?

Answer:

Before garbage collecting an object the garbage collector will look for a finalizer. If the object contains a finalizer then instead of collecting the object the GC will place the object on the finalizer queue.

At some time in the future (finalizer thread is non-deterministic) the finalizer thread will execute, going through each item on the queue and calling Finalize. The object is popped from the finalizer queue and marked as finalized. The GC is now free to collect the object on its next sweep.

Finalization keeps dead objects around for much longer than necessary in most cases. This is why it is common practice to call GC.SuppressFinalize() in your Dispose method - it ensures the GC will never places your object on the finalization queue.

Finalization is complex and if done incorrectly can lead to deadlocks and other really weird stuff (like zombie objects that bring themselves back to life during finalization). Writing finalizers should be avoided in most cases.

For some really candid discussion on finalizers from Eric Lippert, check out these two StackOverflow answers.
http://stackoverflow.com/questions/5223325/garbage-collection-and-finalizers-finer-points
http://stackoverflow.com/questions/6652044/c-sharp-language-garbage-collection-suppressfinalize


.NET Daily Quiz #024

I have the following application:

using System.IO;
using System.Threading;
using System;
class Program
{
    static object syncLock = new object();
    static void Main(string[] args)
    {
        var t1 = new Thread(Thread1);
        t1.Start();
        // ... do some long-running stuff
        // ... at some point in the future:
        t1.Abort();
        t1 = new Thread(Thread1);
        t1.Start();
    }
    static void Thread1()
    {
        while (true)
        {
            lock (syncLock)
            {
                // do some stuff
            }
        }
    }
}

This program runs fine in production, but sometimes it deadlocks when running in debug mode (this has never happened in production). Why?

Hint: this one is subtle — it exploits a bug in the C# language itself.

Answer:

The lock(x)... statement translates precisely to the following (C# spec 8.12):

System.Threading.Monitor.Enter(x);
try {
      ...
}
finally {
      System.Threading.Monitor.Exit(x);
}

except that x is evaluated only once.

A consequence of this generated code is that in debug mode the compiler may insert a "no-op" instruction between Enter(x) and the try block as a natural place to put a breakpoint. If a thread context switch occcurs during this no-op and the thread is aborted then the finally block will not be called, Exit(x) will never execute and the lock is held indefinitely, leading to a deadlock.

This no-op instruction is considered a bug. According to a co-worker this has been resolved in C# 4 - I'm not 100% sure if this is the case (haven't done my research).


.NET Daily Quiz #025

Is this syntax legal?

int myInt = MyMethod(out myInt);

What about this?

var myVar = MyMethod(out myVar);

Why?

Answer:

The first example is perfectly legal (albeit strange and probably not recommended). The compiler knows the type of myInt and because it's a value type (Int32) it has a default and can be used before it is explicitly initialized.

The second example is not legal. Imagine we had multiple overloads of MyMethod - without more information it's not possible for the compiler to know the expected output of MyMethod (in the general case) and therefore it can't know the input type for the out parameter.