.NET Daily Quiz Archive - Week 9

.NET Daily Quiz #041

Can you predict the output of this program? Do you know the rules determining the order in which these constructors are called?

using System;
public class MyApplication
{
    public static void Main(string[] args)
    {
        Console.WriteLine(Foo.i);
        Console.WriteLine(Foo.i);
        new Foo();
        new Foo();
        new Foo();
        Console.WriteLine(Foo.i.ToString());
        Console.WriteLine(Foo.i.ToString());
    }
}
class Foo
{
    public static int i = 0;
    static Foo()
    {
        i++;
    }
    public Foo()
    {
        i++;
    }
}

Answer

The output is

1
1
3
2

Here's what's happening. When

Console.WriteLine(Foo.i);
is called, the constructed type Foo is realized. This constructed type contains a static variable i=0. Now according to the C# spec (section 10.12):

The execution of a static constructor is triggered by the first of the following events to occur within an application domain (emphasis mine):

  • An instance of the class type is created.
  • Any of the static members of the class type are referenced.

Next we call

Console.WriteLine(Foo.i);
and according to the C# spec (same section) a new static constructor execution is called because we have a new closed type:

A static constructor is a member that implements the actions required to initialize a closed class type.

We now have two closed types, each with their own static members. From here the execution is trivial to predict - each closed type acts on its own static members.


.NET Daily Quiz #042

See comments in code for question…

using System;
public class Dynamics
{
    public static void Main(string[] args)
    {
        // Why doesn't this work...
        IGeneric c = new Generic();
        // ... but this does?
        string s = new Generic().Thing();
    }
}
interface IGeneric<out T>
{
    T Thing();
}
class Generic : IGeneric
{
    public T Thing()
    {
        return default(T);
    }
}

Answer

The dynamic type in C# is essentially compiler smoke and mirrors. When you compile this:

// Why doesn't this work...
IGeneric c = new Generic();

All the CLR sees is:

IGeneric c = new Generic();

Covariance and contravariance on object is not allowed (because there is no implicit conversion from object to anything else), so the above code fails. There is an implicit conversion from string to object, so swapping the type arguments works:

// The following works - hooray?
IGeneric c = new Generic();

The next piece of code has a lot more going on:

// ... but this does?
string s = new Generic().Thing();

When compiled, the CLR sees something like this:

IGeneric c = MagicalDynamicRuntimeTypeCast(new Generic().Thing());

The upshot is that dynamic is not a feature of the CLR — it’s a fancy trick by the C# compiler to generate the appropriate runtime code (which itself will instantiate the compiler). The CLR has no knowledge that a dynamic object is being used.

This means that as far as the CLR knows, the runtime type of dynamic is just object (or as Eric Lippert says, ‘"dynamic" as a type is nothing more than "object" with a funny hat on’). So any covariance/contravariance operations with a dynamic constructed typed behave exactly as if you replaced dynamic with object.


.NET Daily Quiz #043

This one might be a bit of a brain-bender. Think carefully.

Is the call to method M bound statically or dynamically (ie, is the call site for this method call determined at compile time, or is it dynamically dispatched)? Can you describe the line of reasoning that leads to your conclusion?

using System;
using System.Collections.Generic;
public class C1
{
    public static void Main(string[] args)
    {
        IEnumerable strings = null;
        IEnumerable dynamics = null;
        var result = C1.M(strings, dynamics);
        result.NoCompilerErrorHere();
    }
    public static T M(IEnumerable a1, IEnumerable a2)
    {
        return default(T);
    }
}

Hint: This program compiles. This information is relevant.

Answer

The correct answer is a static dispatch, although the reasoning is quite subtle (if you want to answer without diving into reflector or ildasm.exe). Most of my reference comes from Chris Burrows’ excellent blog. Burrows is the guy who designed much of and implemented almost all of dynamic in C#.

First you need to understand the rules behind two features: overload resolution with dynamic and assignment conversions.

Overload resolution — any method call with a dynamic argument is dispatched dynamically. This is simple.

Assignment conversions — assignment conversions are a new type of conversion introduced into C# to support dynamic. Just as implicit conversions are a subset of explicit conversions, assignment conversions are a superset of implicit conversions and a subset of explicit conversions. So the conversion hierarchy says that all implicit conversions are assignment conversions and all assignment conversions are explicit conversions. See the image to the right.

conversions

Why this was done is out of the scope of this quiz — see this article.

The main point here is the set of rules for dynamic conversions (emphasis mine):

  1. There is an implicit conversion to dynamic from every type (modulo the weirdos, like pointer types). Basically, if it has an implicit conversion to object, it has an implicit conversion to dynamic too.
  2. There are no implicit conversions from dynamic to any type aside from itself and object.
  3. However (this is the clever part), there are implicit conversions from any dynamic expression (not type!) to any type.
  4. There are implicit conversions between types (in both directions) when they differ only by object and dynamic (as in the exception in rule 2).

Now to answer the quiz question. Our first argument is IEnumerable. Now string is a candidate for type T. The second argument is of type IEnumerable, so dynamic is a candidate for T. The compiler does not throw an exception so the call must be unambiguous, which proves that there must be a conversion from one type to the other in the set {string,dynamic}, but not the other way.

From rule 1 above we know there must be an implicit conversion from type string to type dynamic. From rule 3 we know that the type dynamic is NOT implicitly convertible to type string. Because we’re dealing with types and not expressions then the call must be static — if we used expressions (eg, if we used naked dynamic and string objects) then this whole analysis would be deferred until run time — the static type analysis would never be performed because of the overload resolution rule given above.


.NET Daily Quiz #044

Continuing on with our exploration of dynamic. What is the result of this program, and why?

using System;
using System.Collections;
using System.Dynamic;
public class DynamicConverter : DynamicObject
{
    public override bool TryConvert(ConvertBinder binder, out object result)
    {
        Console.WriteLine("Converting to type {0}", binder.Type.Name);
        result = null;
        return true;
    }
    public static void Main(string[] args)
    {
        dynamic d = new DynamicConverter();
        IEnumerable i1 = d;
        IEnumerable i2 = (IEnumerable)d;
    }
}

Answer

The application prints the first conversion but crashes attempting the second. But why?

This one is slightly tricky, even if you know dynamic well. It’s important to note that DynamicObject is just a convenience object that hides a lot of the complexity of creating dynamic types (the more difficult but more flexible way is to implement IDynamicMetaObjectProvider, which is not trivial).

As a result, DynamicObject has a weird and interesting way of defining call sites for what appear to be dynamic methods. If you call a method that exists statically on a DynamicObject then the static call site will be used — basically, if the underlying language (whether it be C# or something else) gets first dibs on resolving any method calls, and this includes conversions.

So what’s happening in my example code? When we call IEnumerable i1 = d; the C# compiler says, “do I have an implicit conversion from variable d of type DynamicConverter to IEnumerable?” The answer is no, so the compiler dispatches the call dynamically. No problem. Next, we call IEnumerable i2 = (IEnumerable)d; and the compiler asks itself if it can perform this conversion. The answer is “yes!” because in C# there is always an explicit conversion from any class type to any interface type. Because C# can handle this conversion, it is bound statically and fails at run time.

This is an important point to keep in mind if you’re using DynamicObject. If you try to do something like define and call a method named ToString then you should be aware that C# will bind this method statically at compile time — you will not get the dynamic behaviour you might expect!


.NET Daily Quiz #045

Why is my application crashing? I know for a fact that dynamicCollection implements get_Count!

using System;
using System.Collections;
public class DynamicCount
{
    public static void Main(string[] args)
    {
        PrintCount(new int[10]);
    }
    public static void PrintCount(ICollection collection)
    {
        dynamic dynamicCollection = collection;
        Console.WriteLine(collection.Count);
        Console.WriteLine(dynamicCollection.Count);
    }
}

Result:

10
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.Array' does not contain a definition for 'Count'
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at DynamicCount.PrintCount(ICollection collection)
   at DynamicCount.Main(String[] args)

Answer

Count is implemented explicitly on instances of Array.

When a method is bound at runtime due to dynamic arguments, the runtime type of the dynamic argument is used. In this case the runtime type is System.Array, while the compile-time type is ICollection. The statically bound call to ICollection.Count succeeds because System.Array explicitly implements the ICollection interface. The dynamically bound call fails because… well for the same reason — ICollection is explicitly implemented, so Count is not available through the System.Array type.