RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Build Custom Code Generators in C# : Page 4

You don't have to rely on libraries and frameworks to avoid writing repetitive code; instead, learn to generate such code automatically, using a custom input format to describe the code you want to generate.

Completing and Testing the Generator
Now that the generator is complete, you can generate and include the finished classes in your code just like any other classes. Here's how to invoke the generator:

   namespace TestApplication
       class Program
           static void Main(string[] args)
               ObjectDefinition addressDef = 
               ObjectDefinition personDef = 
               ClassGenerator AddressGenerator = new ClassGenerator(
                  "Address", "Dolan.TestCode");
               ClassGenerator PersonGenerator = new ClassGenerator(
                  "Person", "Dolan.TestCode");
To expose the generated classes in a Web service, you need only return the class as you would have before implementing IXmlSerializable. One of the special things about this interface is that the XmlSerializer looks for it before deciding how to serialize your class. If you specify that your class implements IXmlSerializable, the serializer will call your ReadXml and WriteXml methods, which—depending on how much data you're sending over the wire—can be a lot faster than using any old default serializable class.

The sample Web service project (TestService) functions as an example of how to return objects. Notice that because the class implements IXmlSerializable, the XmlSerializer invoked by the ASMX architecture will automatically call the generated methods rather than initiating a reflection-based generation using the intrinsic serialization code.

A side note here: The Schema Provider as it stands in the .NET Framework version 2.0 is a little limited. It doesn't know how to handle objects that are included as part of a complex hierarchy and that are also defined to the schema provider by their own schema provider methods. Therefore, it will throw an exception because it percieves them to be declared twice (for example Person uses a Schema Include for Address, and Address defines itself via the Schema provider). The workaround here is to use the same namespace for all of your classes. That way, the schema provider will essentially allow you to "Overwrite" any previous definition with the next one. This has the desired effect, but unfortunately doesn't follow the spirit of what a schema provider should logically be doing.

Here's a recap of the entire process for writing a code generator:

  • Determine that your code will be doing a finite set of repeatable steps a potentially infinite number of times (really this can be applied to any code, but practically speaking, you have to write these steps individually).
  • Write a single instance of the output you wish to generate. This is not necessarily easy, and can be downright grueling because it's precisely what you don't want to do—in other words, avoiding such work is why you decided to write a generator in the first place.
  • Map out the major structures (classes, sections of code, and methods) in outline form, and assign an intermediate representation to each instance of variation.
  • Create an intermediate format to hold the abstractions you wish to represent. This is a little vague, I know, but the applications can vary from generating data-holding classes to generating entire services or application tiers, so you have to make the intermediate representation fit the model you wish to implement. (This is what ObjectDefinition, PropertyDefinition, DataType, and PropertyType are in my example.) It's a good idea to make this intermediate format able to be loaded and stored easily to disk. That will allow you to prepare your target structures without writing code to declare them.
  • Implement the descending generator which generates output based on the values of the intermediate representation. In this example, it's the ClassGenerator class.
  • Test your code. Though my examples work, they aren't quite complete. Before you can consider your generator to be "domain-complete"—covering all aspects of the target domain—you must have at least one test object that covers each variation that you are encapsulating.
Code generation can be intimidating at first. After all, it involves a complex set of steps to follow, and can at times the output can be frustrating to debug. Just take it one step at a time, and keep in mind all the benefits that not writing repetitive code offers in the end. If you break it down step-by-step and keep your cool, you'll eventually be able to pull code generation out of your toolbox of techniques at will. You may even find generating code produces cleaner and more consistent results than some of your own long-winded efforts. A whole new world of possibilities opens up for you now that what you once considered to be 'too much typing' or 'too much code to tackle' is well within your reach.

Dave Dolan works as a Systems Administrator for Concurrent Technologies Corporation where he regularly builds .NET based Web applications to integrate and automate enterprise systems. He is currently studying the associative memory model of computing, and developing solutions utilizing associative database technology. For more of his writing, visit http://davedolan.com.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date