C# Nested Namespaces Confusion!

Posted on June 23, 2021

C# allows the definition of nested namespaces:

namespace A.B
{
  class Class1 {}
}

Per the C# 6.0 draft specification, this is semantically equivalent to:

namespace A
{
  namespace B
  {
    class Class1 {}
  }
}

This has consequences that may be surprising if there are types with the same names in the parent and child namespaces.

In the following code, which Class1 does X.Class2 use?

namespace A
{
  class Class1
  {
    public void DoA() {}
  }
}

namespace A.B
{
  class Class1
  {
    public void DoB() {}
  }
}

namespace X
{
  using A.B;

  class Class2
  {
    public void DoSomething()
    {
      Class1 x = new ();

      // The using directive brings A.B.Class1 into scope, as expected.
      x.DoB();
    }
  }
}

So far, so good. But what if Class2 is in namespace A.C? Remember that this is equivalent to a C namespace nested in the A namespace.

namespace A
{
  class Class1
  {
    public void DoA() {}
  }
}

namespace A.B
{
  class Class1
  {
    public void DoB() {}
  }
}

namespace A.C
{
  using A.B;

  class Class2
  {
    public void DoSomething()
    {
      Class1 x = new ();

      // The using directive brings A.B.Class1 into scope, as expected.
      x.DoB();
    }
  }
}

This still works as one might inuitively expect. But something surprising happens if we move the using directive out of the namespace and into the compilation unit. Consider two files:

// File A.cs

namespace A
{
  class Class1
  {
    public void DoA() {}
  }
}

namespace A.B
{
  class Class1
  {
    public void DoB() {}
  }
}


// File A.C.cs

using A.B;

namespace A.C
{
  class Class2
  {
    public void DoSomething()
    {
      Class1 x = new ();

      // Surprise! Class1 here is A.Class1, despite our using directive.
      x.DoA();
    }
  }
}

Surprisingly, the Class1 we’re using in A.C.Class2 is actually A.Class1, even though we have a using A.B directive at the top of the file.

Why are the names resolved this way?

First, we encounter the using A.B directive. At this point, Class1 would be resolved as A.B.Class1.

Next, we enter the A.C namespace. Remember that this is equivalent to a C namespace nested in an A namespace. When we enter the A namespace, A.Class1 hides A.B.Class1, and that remains even after we enter the nested C namespace.

This is one of the rare cases where it really matters where you put your using directive.