Sunday, May 02, 2010

Code Generation For Auto-Implemented Properties

I recently ventured into one of the more obscure areas of .NET framework - code generation. The project involved rules engine manipulating properties of our internal domain objects. Long story short, I had to create a routine that converts our domain objects into .NET classes (derived from System.Workflow.Activity). These generated classes did not have much behavior - all methods were pushed to the base class - but they did carry so many properties that they in turn had to be grouped together into classes.

Writing code generation logic for a property turned out to be a lot of work: first, I had to add declaration for a private backing field, then a property declaration, including code expressions for both getter and setter. Here's sample code similar to what I ended up with:


var myType = new CodeTypeDeclaration("Person");
 

var field = new CodeMemberField()
{
    Name = "_LastName",
    Type = new CodeTypeReference("System.String"),
    Attributes = MemberAttributes.Private
};
myType.Members.Add(field);
 
var prop = new CodeMemberProperty()
{
    Name = "LastName",
    Type = new CodeTypeReference("System.String"),
    Attributes = MemberAttributes.Public
};
prop.GetStatements.Add(
    new CodeMethodReturnStatement(
        new CodeFieldReferenceExpression(
            new CodeThisReferenceExpression(), "_LastName")));
prop.SetStatements.Add(
    new CodeAssignStatement(
        new CodeFieldReferenceExpression(
            new CodeThisReferenceExpression(), "_LastName"), 
        new CodePropertySetValueReferenceExpression()));
myType.Members.Add(prop);

And here is the code that was generated by the above fragment:


private string _LastName;
public string LastName
{
    get { return _LastName; }
    set { _LastName = value; }
}

Of course, C# 3.0 has introduced a much shorted way of declaring the same property: "public string LastName { get; set; }". This syntax is called "auto-implemented properties" and it puts the burden on the compiler to create a backing field and implement getter and setter logic. Naturally, I wanted generated classes to look cleaner, so being an optimist that I am I decided to change code generation logic to create auto-implemented properties instead.

That proved to be a mistake: after a while I realized that classes in System.CodeDom namespace do not support auto-implemented properties generation. The best I could come up with was a hack using CodeSnippetTypeMember:


var snippet = new CodeSnippetTypeMember("public string LastName { get; set; }");
myType.Members.Add(snippet);

This solution is pretty far from ideal. It goes against the spirit of code generation because it allows me to target just one programming language, C#. Still, it is pragmatic. Hopefully, Microsoft can bring CodeDom up to date in a future release.

2 comments:

dalshe.com said...

Try following

var cfield = new CodeMemberField
{
Attributes = MemberAttributes.Public | MemberAttributes.Final,
Name = MyPropName,
Type = new CodeTypeReference(typeof(MyType)),
};
cfield.Name += " {get;set;}";

dalshe.com

Anonymous said...

dalshe: your snippet appears to work but doesn't compile due to the invalid semicolons on the field definitions