Do not name a class the same as its namespace, Part One


(This is part one of a four part series; part two is here.)

The Framework Design Guidelines say in section 3.4 “do not use the same name for a namespace and a type in that namespace”. (*) That is:

namespace MyContainers.List
{
    public class List { … }
}

Why is this badness? Oh, let me count the ways.

Part One: Collisions amongst referenced assemblies:

You can get yourself into situations where you think you are referring to one thing but in fact are referring to something else. Suppose you end up in this unfortunate situation: you are writing Blah.DLL and importing Foo.DLL and Bar.DLL, which, unfortunately, both have a type called Foo:

// Foo.DLL:
namespace Foo { public class Foo { } }

// Bar.DLL:
namespace Bar { public class Foo { } }

// Blah.DLL:
namespace Blah
{
  using Foo;
  using Bar;
  class C { Foo foo; }
}

The compiler gives an error. “Foo” is ambiguous between Foo.Foo and Bar.Foo. Bummer. I guess I’ll fix that by fully qualifying the name:

  class C { Foo.Foo foo; }

This now gives the ambiguity error “Foo in Foo.Foo is ambiguous between Foo.Foo and Bar.Foo”. We still don’t know what the first Foo refers to, and until we can figure that out, we don’t even bother to try to figure out what the second one refers to.

This reveals an interesting point about the design of the “type binding” algorithm in C#. That is, the algorithm which determines what type or namespace a name like “X.Y” is talking about. We do not “backtrack”. We do not say “well, suppose X means this. Then Y would have no meaning. Let’s backtrack; suppose X means this other thing, oh, yes, then Y has a meaning.” We figure out what X unambiguously means, and only then do we figure out what Y means. If X is ambiguous, we don’t check all the possibilities to see if any of them has a Y, we just give up.

Assuming you cannot change Foo.DLL, the correct thing to do here is to either remove the “using Foo” – and who knows what all that will break – or to use an extern alias when compiling:

// Blah.DLL:
extern alias FOODLL;
namespace Blah
{
  using Foo;
  using Bar;
  class C { FOODLL::Foo.Foo foo; }
}

Many developers are unfamiliar with the “extern alias” feature. And many of those developers who end up in this situation thereby end up in a cleft stick not of their own devising. Some of them send an angry and profane email to the person who did not cause the problem in the first place, namely, me.

The problem can be avoided in the first place by the authors of Foo.DLL following the guidelines and not naming their type and their namespace the same thing.

Next time: machine-generated code throws a wrench into its own works.

****************

(*) FXCOP flags violations of a far more stringent guideline – FXCOP will tell you if *any* type name conflicts with *any* namespace. But today I’m just talking about the straight-up type-in-a-namespace scenario.

(This is part one of a four part series; part two is here.)

Comments (25)

  1. Thomas Levesque says:

    I thought I knew every keyword in the C# language, but I guess I was wrong… I had never heard about "extern alias" (btw, it’s not listed on the keywords page on MSDN). Learn something new every day !

  2. Pavel Minaev says:

    It’s listed in a roundabout way:

    http://msdn.microsoft.com/en-us/library/x53a06bb.aspx

    "alias" is not a keyword as such (it’s a "context keyword"), so it’s not listed. But "extern" is listed, and if you follow the link, you’ll see a reference to "extern alias" right at the top.

  3. braddabug says:

    Why not just make the the C# compiler raise a compiler error if it detects a type inside a namespace with the same name as the parent namespace? Was there a conscious decision to allow it or a reason for it?

  4. braddabug…It is allowed because it is legal [although a dangerous practice as this shows]. There are other (more complex) scenarios where this also arises where a top level type in one assembly may match a class name in another assembly. This is impossible to detect at the time where the individual assemblies are compiled.

  5. Vaguely related: What about naming a property the same as a type? The compiler usually has no problems distinguishing between the two but avoiding it at any cost often leads to awkwardly-named properties.

  6. braddabug…It is allowed because it is legal [although a dangerous practice as this shows]. There are other (more complex) scenarios where this also arises where a top level type in one assembly may match a class name in another assembly. This is impossible to detect at the time where the individual assemblies are compiled.

  7. Johannes, Property Names that match Type Names are prevelant throughout the Framework. Just look at most "Control" classes, the Type for a property is most often an exact match for the Property Name.

  8. Michael McMullen says:

    Why is the extern alias necessary?  Couldn’t you just use global::Foo.Foo?

    In this particular case that would do, yes. But suppose you really, absolutely wanted to be sure that you were talking to the right thing? Then it might be necessary to go with the extern alias. — Eric

  9. Anthony P says:

    This is interesting and it is also not something I would do. That being said, I’m not having success replicating the error codes. I’ve created Foo.dll, Bar.dll, Blah.dll, namespaces and classnames all match, used using statements, etc. However, the message I get if trying to erroneously type

    Foo foo = new Foo();

    Is “‘Foo’ is a ‘namespace’ but is used like a ‘type'”

    However, using Foo.Foo fooFoo = new Foo.Foo() and Bar.Foo barFoo = new Bar.Foo() compiles and runs without fail.

    …Ah… as I’m typing this, I went back and compared one more time. The using statement inside the namespace generates the errors contained in your post. However, I had the using statements outside of the namespace declaration.

    using Foo;
    using Bar;
    namespace Blah
    {
       public class Blah
       {
           Foo.Foo fooFoo = new Foo.Foo();
           Bar.Foo barFoo = new Bar.Foo();
       }
    }

    So that sparks another question, is it a standard practice to include the usings within the namespace rather than at the top of a code file?

  10. Thomas: extern alias is mentioned on MSDN (http://msdn.microsoft.com/en-us/library/ms173212(VS.80).aspx). Notice that you actually have to compile the alias "reference" into the assembly. The most common scenario is when you are stuck having to use two different versions of an assembly. Rare, but possible if you do not properly branch source.

    Peace and Grace,

    Greg

    Twitter: @gbworld

  11. Chris says:

    Foo is rather ambiguous too….  Bar none – Foo foo fooey.

    I wish those terms would be replaced by something that you might truly model, even abstractions are based in reality.

  12. pradeeptp says:

    Where can I read The Framework Design Guidelines?

    Were you to type that into Bing, it would tell you that you can buy bound copies at Amazon.com, or read them online at http://msdn.microsoft.com/en-us/library/ms229042.aspx. — Eric 

  13. Tim Goodman says:

    Eric, I’m confused by the behavior Anthony P describes.  According to the old post you linked to,

    using Foo;
    using Bar;

    should be equivalent to

    using global::Foo;
    using global::Bar;

    regardless of whether the using directives are inside or outside the namespace Blah (*unless* Blah contains namespaces called Foo and Bar).

    So why does the error depend on where the usings are located?

    The C# specification does not specify what the error messages should say, just whether there should be errors or not. The compiler uses various heuristics to try to figure out the “best” error for a given situation. Some of those heuristics are sensitive to small changes. I’m not sure exactly what the heuristic is for this situation, but I was recently looking at the code that gives errors when a type binding fails. The heuristic there is very complicated; a binding could fail because the result was inaccessible, ambiguous, “bogus” (that is, the winner is a type imported from metadata that is so malformed that the C# compiler cannot actually use it), of the wrong generic arity, and so on. Or any combination of the above! It’s hard to say what the best error message is for a given situation; we do our best, but sometimes do end up producing confusing error messages. — Eric

  14. Benjol says:

    Been there, done that, and it’s worse when the VS Designer is involved:

    http://stackoverflow.com/questions/1259399/alias-and-namespace-conflict-in-visual-studio-designer

    That’s what I’ll be discussing next time. — Eric

  15. Tim Goodman says:

    But Eric, it seems that Anthony P is saying (and I admit I haven’t tested it) that by putting the using directives outside his namespace and by writing “Foo.Foo foo” instead of just “Foo foo” that he can get it to compile and run without any errors at all.  How is that possible? (or perhaps I’m misunderstanding what is being said)

    Oh, I see what you mean. Yes, that produces no error and the lookup becomes unambiguous then. The “using Foo” is effectively ignored. The lookup rules, briefly are “look in yourself, look in your base, look in your outer namespace, look in your outer namespace’s using directives”. 

    If the “using Foo” is moved to the global namespace then we look in the global namespace for Foo first and find namespace Foo; we never even consider the using directive. If the “using Foo” is inside the declared namespace then we check the using clause before we check the global namespace.

    This is a subtle difference. With the using directive in one place, Foo means the namespace, in the other, it means the type. This is yet another reason to avoid this pattern: because it changes the bindings of things in strange and subtle ways when you make changes that you think ought to be no-ops, like moving a using directive outwards or inwards. — Eric

  16. Sohel Rana says:

    Learned new thing "external alias". Thanks

  17. Tim Goodman says:

    Thanks for the explanation.  That’s pretty sneaky all right.

  18. mpg says:

    I find myself often writing code of the following form

     class DataProperties
     { }

     class BigFooObject
     {
       public DataProperties DataProperties { get; private set; }
     }

    which is a similar legal-but-morally-wrong sort of namespacing thing.  I keep doing it, and I keep wishing the compiler (well, fxcop really) would slap me every time I do.

    -mpg

    No, that’s fine. People do that all the time, and the language has been specifically designed to ensure that this is legal and unambiguous. See http://blogs.msdn.com/ericlippert/archive/2009/07/06/color-color.aspx for details.

    — Eric

  19. Bahador says:

    I wonder why exactly is the type binding algorithm the way it is. Why don’t simply backtrack? Does that cause problems besides performance penalty (which is a valid reason for itself, if the penalty is high enough)?

    I have a feeling that I’ve probably missed something in my Compiler class in college 😉

  20. Pavel Minaev says:

    > I wonder why exactly is the type binding algorithm the way it is. Why don’t simply backtrack?

    I dare say it would be mighty confusing if, in the same line of code, A.B would mean “class B in namespace A”, and A.C would mean “inner class C in outer class A”.

    Also, C# already goes to great lengths to make sure an identifier used in the same scope has only one meaning throughout it (see the rules for local variables for an example – where “{int x = 1; { int x = 2; }}” is not allowed). The approach described here is consistent with that. I wonder, in fact, if that was intentional.

    Indeed. Another factor is that C# is a “say what you mean” language. If you say “Foo.Bar.Blah”, we figure out what the best possible meaning for “Foo” is and stick to it. If that thing doesn’t have a Bar that has a Blah, we do not say “well, let’s make a wild guess as to what Foo really meant, perhaps it meant this other thing, oh, no, that other thing doesn’t have a Bar with a Blah. Perhaps the second Foo is right but Bar means this other Bar that does have a Blah, oh, wait, no, that Blah is not accessible. Well then, perhaps Foo meant this third thing…” If you do that then you have no reason whatsoever to believe that you are binding to the thing that the user intended to bind to. C# is not a “fix up the user’s mistakes and muddle on through as best we can” language, it is a “tell the user that they have a mistake and get them to fix it so that we know that the program is correct” language. — Eric

  21. Grico says:

    Just out of curiosity…does it get any worse if you have, for instance, Color.Color.Color?

  22. Grico says:

    Clarifying the previous post. I know that under C# its not allowed to declare a class member with the same name as the class itselft. I was wondering if there could be the case of having to consume a class written in a language where you could define class members with the same name as their enclosing type.

  23. Bahador says:

    @Pavel Minaev, @Eric

    That makes perfect sense.

  24. Wayne Bloss says:

    Hi Eric,

    How about this?

    namespace Project.Json

    {

       public static class JSON

       {

       ….

       }

    }

  25. Matthew Copeland says:

    Hopefully you are still monitoring this….

    I agree with an earlier post that the use of Foo wasted a teachable moment.

    So let me use a real world example and Eric you explain why it is a poorly designed hierarchy, and what you would do instead.

    Football.Play

    Play

    OffensivePlay

    DefensivePlay

    Forrmation

    Presnap Adjustment

    etc.