justnbusiness

Monday, July 09, 2007

CodeSnippetExpression is evil

By the way, to anyone thinking about doing some CodeDom development be sure to avoid the CodeSnippetExpression class like the plague. It turns out there is (at least) two legitimate usages of the class that I have encountered but otherwise it is evil.

I can say this because I just spent a couple weeks re-doing all of my templates so that the CodeSnippetExpression class was completely removed from my templates. This was only the beginning of the headache of making my CodeDom code language agnostic but it was the most painful. For the record I'll add my two legitimate usages of snippets and some examples of how I got burned by just saying to myself "Meh, I'll only be generating C# anyway...".

Legitimate use #1 - Calling the default base class constructor

//Example
CodeConstructor c = new CodeConstructor();
c.Name = "Example";
c.Attributes = MemberAttributes.Public;
c.BaseConstructorArgs.Add(new CodeSnippetExpression(""));

//Output
Example() : base() { }

For some reason you have to add a parameter to the BaseConstructorArgs collection to get this functionality and if you want to call the parameterless constructor you have to add an expression that generates nothing. I suppose you *might* be able to use the CodePrimitiveExpression(null) but I haven't tried that. I can confirm that the empty CodeSnippetExpression("") does work however.

Legitimate use #2 - Fake while loops

//Example
CodeIterationStatement i = new CodeIterationStatement(
new CodeSnippetStatement(""),
new CodePrimitiveExpression(true),
new CodeSnippetStatement(""));

//Output
for(;true;) { }

Now I realize that this looks stupid but I assure you, it's legitimate. It turns out there is no "while" loop in the CodeDom all you get is the CodeIterationStatement which in turn generates a for loop. However if you think about it what is a for loop? Essentially you're saying "for some initial value, while some condition is true, do this". A for loop with only the condition evaluation is essentially a while loop and the only way to do that with the CodeDom is to print out empty snippets.

So it's ironic that the only legitimate usages of the CodeSnippetExpression objects are simply empty strings. This makes me think that they're utterly evil and should instead be replaced with a CodeEmptyExpression() object or something similar. I welcome any comments about how else they could be properly used.

Here are a few simple examples about how I decided to use them then found it to be very painful to undo them:

Bad idea: "using"
Yes I know the "using" feature in C# is lovely and wonderful and all of that but in reality it is simply syntactic sugar. It is not part of the underlying .net framework.

Proper Alternative:
IDisposable d = ...;
try
{
//do something to d.
}
finally
{
d.Dispose();
}

This is essentially what the "using" statement is doing under the hood. Unfortunately you have to implement this using the CodeDom this robustly. There are no language agnostic shortcuts!

Bad Idea: "anonymous delegates"
Anonymous delegates, oh anonymous delegates, how I love thee... unfortunately you are simply an illusion! Once again more C# syntactic sugar. What's going on under the hood is that a private member is getting generated and a delegate pointing to that method is being generated for you. You're going to have to do it all yourself! Additionally, simply passing method names as parameters for delegates isn't good enough either, more damn syntactic sugar! You're going to have to do it all the hard way.

Proper Alternative:
void Test()
{
EventHandler e = new EventHandler(Test);
e.Invoke(this, EventArgs.Empty);
}
void MyDelegateMethod(object sender, EventArgs e)
{
//do something.
}

This goes for asynchronous methods as well. In stead of simply passing in Method names for parameters you'll need to use the CodeObjectCreate class to create an instance of your delegate (EventHandler or whatever) and pass in the Method name to that. Make sure you use all the proper CodeDom classes to get this language agnostic.

Those were really the biggest gotchas that I found out. You should start out assuming you're going to be generating for multiple languages and do it right the first time. Be sure to find the right class for the right job too!

0 Comments:

Post a Comment

Links to this post:

Create a Link

<< Home