.NET Daily Quiz Archive - Week 8

.NET Daily Quiz #036

Why is my T-SQL behaving this way? Is this standard SQL behaviour? Can you think of a good solution, assuming you need your string equalities to be strictly correct?

select 1 where 'e' = 'e   '    -- true
select 1 where '  e' = '  e  ' -- true
select 1 where 'e' = '  e'     -- false
select 1 where 'e' = '  e  '   -- false

Answer

The SQL 92 spec requires a comparison of two strings of unequal length to append trailing spaces to the shorter string until they are the same length, and then make the comparison between the now-equal-length strings. What this means is that if the longer of the two strings has trailing spaces but everything before the trailing spaces is a match then the strings will compare equal.

I found this excellent blog post by Anthony Bloesch which goes over the various ways you can work around this, assuming that the trailing spaces in your strings are meaningful and you need a proper comparison. Also note that any of these approaches will kill your indexing performance.


.NET Daily Quiz #037

Try to do these without compiling. Which of these type definitions is not valid, and why?

using System.Collections;
using System.Collections.Generic;
class T1 where T : System.Enum
{}
class T2 where T : IEnumerable, IEnumerable
{}
class T3 where T : List, IEnumerable, IEnumerable
{}
class T4<L,M,N,O,P> where L : M where N : O where P : N where M : P where O : L
{}
class T5<Q,R,S> where Q : R where R : S where Q : S
{}
class T6<T,S> where S : struct where T : struct
{}
class T7<T,S> where S : T where T : struct
{}
class T8 where T : string
{}
class T9<Q, R, S> where Q : R where R : S where S : IEnumerable
{}
class T10<Q,R,S> where Q : struct, R where R : S where S : T10<Q,R,S>
{}

Answer

Invalid type definitions are (with comments):

// System.Enum is not a valid type constraint - neither are System.Array, System.Delegate or System.ValueType
class T1 where T : System.Enum
{ }
// Circular dependency - L depends on M which depends on P which depends on N which depends on O which depends on L
// (try drawing a dependency graph to make this clearer)
// Type constraints must not contain circular dependencies, either direct or indirect.
class T4<L, M, N, O, P> where L : M where N : O where P : N where M : P where O : L
{ }
// There is a duplicate type constraint - each type parameter must appear only once in the parameter
// constraint list - here Q appears twice
class T5<Q, R, S> where Q : R where R : S where Q : S
{ }
// If S is a T then T must not be a struct - T must be extensible.
class T7<T, S> where S : T where T : struct
{ }
// System.String is a sealed class. Type constraints must not be sealed (this would be logically equivalent
// to just declaring T8 because no other type could be substituted).
class T8 where T : string
{ }
// Q must be a struct and an R but R has a class constraint because R must be an S and S must be a T10<Q,R,S>
// (again, draw a depedency graph and follow the class constraint up).
class T10<Q, R, S> where Q : struct, R where R : S where S : T10<Q, R, S>
{ }

See the C# spec, section 10.1.5 — some of the rules around type parameter constraints are not necessarily obvious.


.NET Daily Quiz #038

In the following program, which DoThings overload is chosen by the compiler?

using System;
class Foo
{
    static void DoThings(params string[] args)
    {
        Console.WriteLine("params method");
    }
    static void DoThings(T args)
    {
        Console.WriteLine("generic method");
    }
    static void Main(string[] args)
    {
        DoThings("why hello there");
        Console.ReadLine();
    }
}

What happens if I change DoThings to this:

using System;
class Foo
{
    static void DoThings(params string[] args)
    {
        Console.WriteLine("params method");
    }
    static void DoThings(T args)
    {
        Console.WriteLine("generic method");
    }
    static void DoThings(string arg1, object arg2 = null)
    {
        Console.WriteLine("two args method");
    }
    static void Main(string[] args)
    {
        DoThings("why hello there");
        Console.ReadLine();
    }
}

Can you explain this behaviour?

Answer

It’s a trick question. This program exploits a bug in the C# compiler. In the first program will select DoThings(params string[] args) while the second program will fail to compile due to an ambiguous invocation. The bug is that both programs are ambiguous and the first program should fail to compile. For a really interesting and detailed analysis of this overload resolution problem take a look at this excellent StackOverflow answer by Eric Lippert.


.NET Daily Quiz #039

I want consumers of my class to be able to do this:

class Program
    {
        static void Main(string[] args)
        {
            Foo foo = new Foo
                          {
                              "insane",
                              "initializer",
                              "syntax!"
                          };
        }
    }

Show me how to write Foo without extending a class that currently supports this kind of initializer syntax.

Answer

We need to implement IEnumerable (why? I don't know. The spec never explains this point and there's no mechanical reason for it I can see) and a method named Add which accepts a single argument (return type not important, apparently). Totally awesomely, the Add method does not need to come from an interface.

Any overload of Add will allow you to add that type to your collection constructor syntax. Normal overload resolution rules apply to Add.

E.g.

using System;
using System.Collections;
public class InsaneConstructorTuesday
{
    public class Foo : IEnumerable
    {
        public void Add(string item) { }
        public void Add(int item) { }
        public void Add(Foo item) { }
        public IEnumerator GetEnumerator() { return null; }
    }
    public static void Main()
    {
        Foo foo = new Foo {
            "insane",
            10000000,
            "syntax!",
            new Foo()
        };
    }
}


.NET Daily Quiz #040

My application crashes at runtime. Why? What important design consideration (C#-specific) am I violating?

class Program
{
    static void Main(string[] args)
    {
        B b = new B();
    }
}
public class A
{
    public A()
    {
        Something();
    }
    public virtual void Something()
    {
    }
}
public class B : A
{
    private string _foo;
    public B()
    {
        _foo = "sdf";
    }
    public override void Something()
    {
        var b = _foo.ToLower();
    }
}

Answer

_foo is null and _foo.ToLower() throws an error as constructors execute from the base type up so _foo hasn't been initialized. If you use ReSharper is a similar tool no doubt you have seen the warnings about calling virtual methods from constructors, and this is why. For more detail check out this StackOverflow question, or read Eric Lipperts excellent blog articles on the topic of constructor and initializer call order, part I and part II.