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

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

Rate This
  • Comments 11

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

Part Two: Machine-generated code:

You write

namespace Foo
{
  public sealed class Foo
  {
    public string Blah(int x) { … }
  }
}

You take this code and run a third-party “decorator” tool over it that makes your class into a more colourful class:

// Machine-generated code:
namespace Foo
{
  public sealed class ColorFoo
  {
    public ColorFoo(Foo.Foo foo, System.Drawing.Color color)
    {
      innerFoo = foo;
      innerColor = color;
    }
    private Foo.Foo innerFoo;
    private System.Drawing.Color innerColor;
    public System.Drawing.Color Color { get { return innerColor; } }
    public string Blah(int x) { return innerFoo.Blah(x); }
  }
}

And maybe some other stuff in there like an implicit conversion to or from Foo, and so on. The exact details of the decorator aren’t important here; what’s important is that this code was generated by a machine.

The authors of the code generator thought they were being clever and fully qualifying all the type names, but they were not clever enough. Those type names should actually be fully qualified with “global::” on the front of all of them. The code as it stands will not compile; it gives the incredibly unhelpful message “type name Foo does not exist in Foo.Foo”. 

Uh, what? It sure looks like a type named Foo exists in Foo… oh, wait. Inside namespace Foo, the name “Foo.Foo” means “the thing named Foo inside type Foo”, and we’re inside namespace Foo.

It’s ironic, like good advice that you just didn’t take: the code generator writers would have been better off not qualifying the names in this case.

Now the user is again in a cleft stick not of their own devising; the code is machine-generated and probably cannot easily be edited because its just going to be re-generated again later and the edits will be lost. 

Now, you might say, well, this is a bug in the code generator. There exist such code generators; I actually wrote this bug into a code generator once, though fortunately I realized my mistake before we shipped. It’s really easy to do, and lots of existing code generators do not fully qualify their type names all the way out to “global::”. (*)

But still, better to not cause trouble for code generators in the first place; you can avoid the problem by not naming the class and the namespace the same thing.

This scenario happens surprisingly often. Our own testers have rediscovered this one independently several times when they’ve been doing testing on code generators, and often believe it to be a bug in the compiler’s type lookup algorithm, not a flaw in the code generator.

Next time: how to design a bad hierarchy

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

(**) And of course many code generators were written in the C# 1.0 days before the “global::” syntax was invented.

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

  • "Next time: how to design a bad hierarchy"

    I hope this will offer some insight on how to design a good hierarchy. ;)

  • This is so familiar. I've made the mistake of not fully qualifying things in a code generator that I had written for VS a few months ago. Luckily for me, the error quickly manifest itself, as we did have a type that actually hit this case.

    Ironically, I actually did try to qualify all types (complete with global::) from the get go, because of bad experiences with not fully qualified names in the past, but somehow managed it to slip in just one case - and that was enough!

    Moral of the story: I don't know, really, but maybe write some tool that would verify that all type references in a .cs file are fully qualified, and run the output of any code generator through that? A special-purpose StyleCop rule, to be enabled for generated code?

    Speaking of fully qualified names... in a similar situation a few years ago, I ran into a curious problem with C++ syntax (codegen there was outputting C++). Remember that C++ fully qualified types are of the form ::A::B - no "global" there. Now consider a fully qualified name of a method that is defined outside a class:

     class ::C1 { ... }; // okay

     class ::C2 { ::C1 M(); }; // okay

     ::C1 ::C2::M() { ... } // oops

    In case you didn't spot it, the problem with the third line is that it is parsed as a single type name ::C1::C2::M, which is supposed to be followed by a method name, but wasn't. The workaround was to use parentheses around the method name, solving the mystery of why you would even need this in the language grammar:

     ::C1 (::C2::M)() { ... } // okay

    It's an interesting perspective on language design, actually - code generators often have requirements that are very different from human coders.

  • What's a cleft stick? Where can I get one?

  • "like good advice that you just didn’t take" : http://www.youtube.com/watch?v=nT1TVSTkAXg

  • @Tom R: "In a cleft stick (UK)" is roughly equivalent to "Between a rock and a hard place (US)." Thanks, Eric, for the new expression. I hadn't heard that one before.

    Indeed, to be "in a cleft stick" is to be trapped in a position where one can neither go forward nor backwards without making it worse. The phrase is often extended to "in a cleft stick of his own devising" or "in a cleft stick of their own cutting" to imply that the unfortunate situation one find's oneself in is in some sense one's own fault. President Obama is perhaps "in a cleft stick of his own devising" with respect to health care overhaul legislation: he can go neither forwards nor backwards on it without displeasing some huge constituency, and moreover he got himself into that situation. I wished to indicate that the unfortunate situation users of such a type find themselves in is a cleft stick that was devised by the author of the code generator, not its user. -- Eric

  • Is "sealed" actually part of the example? Because I would argue that unless it makes a difference, it is visual noise that doesn't belong. I understand that sealing your classes by default is "best practices", but its conspicuous existence here makes me think "Oh, I can avoid this problem just by not sealing my classes".

    It is there to motivate the reason for my choice to make the code generator extend the class through aggregations rather than inheritance. There could be any number of reasons why it is not appropriate to extend through inheritance; the class being sealed is one of them. (This point might have been too subtle.) One can of course run into similar issues when extending a class through inheritance. -- Eric

  • Not to be over blunt, but putting Foo in a namespace Foo is just absurd.  Anyone who is temped to do it is a n00b.  Sorry, but that's just obviously not going to play well.  Of course, namespaces could require a non-single word hierarchy, but that ship has sailed too.

    The *real* problem that gets my goat is non-template driven code generators.  I LOVE code generation, especially for model code where you need to implement a hundred items all with very similar behavior.

    But if you don't give me the template and the ability to substitute in my own template, then I don't want to play.  In Java land, I did wild and wonderful things with EMF because it's all based on JET templates.  We added all kinds of really business-helpful behavior to our generated model objects and it was almost as hard as coding an asp page.  It just puts out compilable code instead of HTML.

    However, with things like the DataSet generated db access code, I'm completely stuck.  

    I'm using VS 2008 and I've heard that this supposed to be getting better, but seriously seriously:  

    If you don't give me an editable template, then you had better not be generating code on my behalf.  If you do generate and fail to provide the template I will give you the Squiggly Eyebrows of Doom.

    </soapbox>

  • It's getting better in a sense that, in the past, there was simply no single definite solution for template code generation (and any template text generation) on .NET. The only thing you could do with stock framework is to rig up ASP.NET, but it really isn't designed for that kind of thing, and the whole scheme gets ugly real quick.

    So, most generators ended up being driven entirely from hand-written code using CodeDOM.

    Now? There's T4 (http://www.hanselman.com/blog/T4TextTemplateTransformationToolkitCodeGenerationBestKeptVisualStudioSecret.aspx). And in VS2010, there are pre-compiled T4 templates, which are much easier to use. So now that there's a clear single choice for this feature on the platform, there's more incentive to use it - and that generally implies making source templates themselves available for customization.

    Of Microsoft own offerings, ASP.NET MVC already does so (http://blogs.msdn.com/webdevtools/archive/2009/01/29/t4-templates-a-quick-start-guide-for-asp-net-mvc-developers.aspx).

  • One of the things that I really like about C# is that it is designed to be friendly to code generation, with partial methods and members on one hand, and #line on the other hand. Partial classes and methods are so useful that I can't imagine how I used to ever live without them. I remember how painful it was with Java in the old days, where it was impossible to debug because error messages had meaningless line numbers and filenames.

  • do appriciate your advice, If you have time, will love to read your views on the following.

    http://stackoverflow.com/questions/2434788/how-do-you-resolve-the-common-naming-collision-between-type-and-object/2434860#2434860

  • Hold on a tick, ColorFoo is sealed but NOT immutable via readonly on its private fields?  A buggy code generator indeed!

Page 1 of 1 (11 items)