Scott Rickman

Here we try

Case insensitive dictionary keys in C#

When constructing a Dictionary<string, TValue>, it can be useful to have the keys be case-insensitive. i.e. taking a string parameter into a method and using that as the key into the dictionary

// use the ctor that takes a comparitor
var dict = new Dictionary<string, List<string>>(StringComparison.OrdinalIgnoreCase)
{
    { "cat", ["Shorthair", "Longhair", "Persian"] },
}

Console.WriteLine(dict["Cat"]); //"Shorthair", "Longhair", "Persian"

C# placement of using statements

In C# the placement of using statements seems irrelevent, especially when the default templates in Visual Studio places using statements at the top of the file.

Consider these code samples

// example 1
using System;
namespace Demo.Models;
public class CarModel
{
    public int Three = (int)Math.PI;
}

// example 2
namespace Demo.Models;
using System;
public class CarModel
{
    public int Three = (int)Math.PI;
}

Both example 1 and example 2 will compile and are clearly contrived.

Now, let's say another developer in the team adds this code to the project

// example 3
namespace Demo;
public class Math
{
    public int PI = 4;
}

I mean, why not right?

When example 2 is compiled, the compiler will try to resolve the token Math by looking from the innermost scope (CarModel) until it finds a type called Math - in this case System.Math from the using statement.

// example 2
namespace Demo
{
    public class Math
    {
        public int PI = 4;
    }

    namespace Demo.Models;

    // this will be used by the compiler as it resolves earlier than Demo.Math
    using System;
    public class CarModel
    {
        public int Three = (int)Math.PI;
    }
}

When example 1 is compiled, the compiler will try to resolve the token Math by looking from the innermost scope (CarModel) until it finds a type called Math - in this case Demo.Math. The compiler sees the scope as though all the code was in the same file, something like this

// example 1
using System;
namespace Demo
{
    // this will be used by the compiler as it resolves earlier than System.Math
    public class Math
    {
        public int PI = 4;
    }

    namespace Demo.Models;
    public class CarModel
    {
        public int Three = (int)Math.PI;
    }
}

You might find that in example 1 that you don't get the desired value for CarModel.Three