views:

198

answers:

4

Consider the following short code snippet.

namespace B
{
    public class Foo
    {
        public string Text
        {
            get { return GetType().FullName; }
        }
    }
}

namespace A.B
{
    public class Foo
    {
        public string Text
        {
            get { return GetType().FullName; }
        }
    }
}

Get familiar with example #1 first.

using B;

namespace A.C
{
    public static class Program
    {
        public static void Main()
        {
            Console.WriteLine(new Foo().Text);
        }
    }
}

Now consider example #2.

namespace A.C
{
    using B; // Notice the placement here.

    public static class Program
    {
        public static void Main()
        {
            Console.WriteLine(new Foo().Text);
        }
    }
}

There is nothing terribly peculiar about example #1. However, things get interesting with example #2. It is imperative that you pay close attention to all identifiers used in the examples. As a fun exercise try to guess what happens without plugging this into the compiler. I will not reveal the answer here because 1) it is easy enough to try yourself and 2) I do not want to ruin the fun.

Will the program:

  • not compile
  • display B.Foo
  • display A.B.Foo

The question...Where in the C# specification is this behavior described?

I did take a look at section 3.7 in the C# 4.0 specification and especially bullet #2, but I do not think that explains the behavior. If anything it almost makes me think the compiler is behaving contradictory to the specification.

+5  A: 

I didnt read the C# specifications, but I can tell you what's happening simply by deduction. When you put using B inside of the A.C namespace you are no longer in global scope, you are in the scope of the surrounding namespace. First the app will try to resolve in A.C, then in A.

The easiest fix is simply to change the inside using statement to:

using global::B;

But, you can further see this occuring by adding

namespace A.C.B
{
    public class Foo
    {
        public string Text
        {
            get { return GetType().FullName; }
        }
    }
}

Note that you now resolve to A.C.B

P.Brian.Mackey
+1, but I think you've got the order in which resolution occurs backwards. It should try to resolve in `A.C` first, then `A`.
Randolpho
Thank you for the correction. It has been made.
P.Brian.Mackey
Good tip on the `global::B` trick by the way.
Brian Gideon
+7  A: 

The first example prints "B.Foo", the second example prints "A.B.Foo". This is because in the second example the using B; directive is enclosed inside the A.C namespace.

Why does it use A.B rather than B?

Because namespace lookups follow the same rules as type name qualification lookups. Section 3.8 of the C# spec.

Basically, when the using directive is processed by the compiler , the symbol B is looked for in the A.C namespace. Not finding it, it's looked for in the A namespace. Because it's found there as a sub-namespace of A, it selects that namespace and doesn't go to the global namespace to find the B namespace.

Edit:
As @P.Brian.Mackey suggests, you can get to the B namespace with using global::B;.

Randolpho
Yep, I think you are right. The relevant piece is in 3.8 bullet #1 sub-bullet #2. It definitely, but not so clearly, describes this backtracking behavior. Nice catch.
Brian Gideon
@Brain: That's the one, along with the three bullets after.
Randolpho
A: 

I believe the pertinent parts of the spec are:

3.4.1 Namespace members

Namespaces and types that have no enclosing namespace are members of the global namespace. This corresponds directly to the names declared in the global declaration space.

Namespaces and types declared within a namespace are members of that namespace. This corresponds directly to the names declared in the declaration space of the namespace.

Namespaces have no access restrictions. It is not possible to declare private, protected, or internal namespaces, and namespace names are always publicly accessible.

As well as section 9.4.2 which discusses how using directives affect the scoped resoultion of identifiers.

LBushkin
+4  A: 

The other answers are correct, but one additional note. It helps to remember that

namespace A.C 
{
    using B;

actually is just a short way of writing

namespace A
{
    namespace C
    {
        using B;

Which might make it a bit more clear what is going on here. When resolving B we check A for B before we check the container of A.

If you're interested in ways that namespace lookups can go horribly wrong, see my series of articles about that:

http://blogs.msdn.com/b/ericlippert/archive/tags/namespaces/

Eric Lippert
Nice blog, thanks.
Brian Gideon