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

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

Rate This
  • Comments 25

(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.)

  • Learned new thing "external alias". Thanks

  • Thanks for the explanation.  That's pretty sneaky all right.

  • 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

  • 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 ;)

  • > 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

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

  • 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.

  • @Pavel Minaev, @Eric

    That makes perfect sense.

  • Hi Eric,

    How about this?

    namespace Project.Json

    {

       public static class JSON

       {

       ....

       }

    }

  • 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.

Page 2 of 2 (25 items) 12