What do you think about this way?

Feb 18, 2009 at 10:19 PM
No one ever commented about using WriteLine()'s in lieu of the template features, so I'll make a separate discussion for it.  What do people thinking about doing something like this, instead of using the templating aspects of T4.  Note the use of W() and WL() to shorten typing.
<#+
public abstract class TemplateEx : Template
{
protected void WL()
{
WriteLine("");
}

protected void WL(string fmt, params object[] args)
{
if (args.Length == 0)
WriteLine("{0}", fmt);
else
WriteLine(fmt, args);
}

protected void W()
{
Write("");
}

protected void W(string fmt, params object[] args)
{
if (args.Length == 0)
Write("{0}", fmt);
else
Write(fmt, args);
}

protected void RenderLines(List<string> lines)
{
foreach (string line in lines)
WL(line);
}

protected void RenderUsings(List<string> usings)
{
WL("#region using");

foreach (string u in usings)
WL("using {0};", u);

WL("#endregion");
}

protected void BeginNamespace(string ns)
{
WL("");
WL("namespace {0}", ns);
WL("{");
PushIndent(" ");
}

protected void EndNamespace()
{
PopIndent();
WL("}");
}

protected void EndClass()
{
PopIndent();
WL("}");
}

protected void BeginRegion(string fmt, params object[] args)
{
WL("#region " + fmt, args);
}

protected void EndRegion()
{
WL("#endregion");
}

protected void BeginSection(string fmt, params object[] args)
{
WL("// " + fmt, args);
WL();
}

protected void EndSection()
{
}

protected void RenderProperty(string propType, string propName)
{
BeginRegion(propName);
WL("public {0} {1}", propType, propName);
WL("{");
WL(" get;");
WL(" protected set;");
WL("}");
EndRegion();
}

protected void RenderSingleton(string className)
{
BeginSection("Singleton");
BeginRegion("Instance");

WL("public static {0} Instance", className);
WL("{");
WL(" get");
WL(" {");
WL(" if (_instance != null)");
WL(" _instance = new {0}();", className);
WL();
WL(" return _instance;");
WL(" }");
WL("}");
WL("private static {0} _instance = null;", className);

EndRegion();
EndSection();
}
}
#>

<#+
public class CommandTemplate : TemplateEx
{
// Note: CommandSet and Command come from some XML bindings that are created separately.
// I've omitted the code for these classes
public CommandSet CommandSet = null;
public Command Command = null;

protected override void RenderCore()
{
RenderUsings();
BeginNamespace();
BeginClass();
RenderConstructor();
WL();
RenderSingleton(Command.Name + "Command");
EndClass();
EndNamespace();
}

private void RenderUsings()
{
List<string> u = new List<string>();
u.Add("System");
u.Add("System.Collections.Generic");
RenderUsings(u);
}

private void BeginNamespace()
{
BeginNamespace("MyNamespace");
}

private void BeginClass()
{
WL("public class {0}Command", Command.Name);
WL("{");
PushIndent(" ");
}

private void RenderConstructor()
{
BeginSection("Constructors");

BeginRegion("{0}Command()", Command.Name);
WL("public {0}Command()", Command.Name);
WL("{");
WL("}");
EndRegion();

EndSection();
}
}
#>
</string></string></string></string>
Feb 19, 2009 at 7:03 PM
The transformation engine is converting everything to C# or VB code in order to compile and run the template. I see no benefit, on creating -non human readable- code on the templates. I believe that the -human readable- templates is easier to edit, later on the application cycle...


George J.
Feb 19, 2009 at 7:57 PM
Yes, the approach I outlined is less human-readable, and that is unfortunate.

However, the templating approach has a few draw backs for me:
1.  You end up hard-coding your indentation.  It would be nice to keep that parameterized.
2. The template ends up getting littered with <# #> and <#= #> blocks, which limits the readability of the template.
3. How do you create reusable snippets with the templating approach?  The problem is that once you drop into a helper method using the <#+ #> block, you can no longer use the templating technique and you end up having to WriteLine() everything you want to generate.  And that's to say nothing of all the computer science stuff we've all learned ... decomposition, separation of concerns, don't-repeat-yourself, etc.

If there was some way to solve these issues, then I agree, in theory, that the templating approach is better.  But without a solution to these issues, I think the approach I've outlined is at least worth considering.  For me personally, I actually find it more readable, not less.
Coordinator
Feb 20, 2009 at 10:33 AM
Edited Feb 20, 2009 at 10:35 AM
It all depends on complexity of content you are generating. If you need to generate largely static content, using text blocks and code blocks is the easiest, most readable approach. On the other hand, if you are generating very dynamic content, Write and WriteLine may be easier to use and easier to read. I typically end up using a mix of text/code blocks and Write/WriteLine methods.

I wouldn't use acronyms like W and WL in my code. Write and WriteLine are not much longer and a lot more readable. Here is what .NET framework design guidelines say about this: Do not use any acronyms that are not widely accepted, and then only when necessary.

To answer #3 - yes, you can use text blocks and expression blocks inside of class feature blocks to create reusable methods. More here.
Feb 22, 2009 at 3:46 AM
Hmmm ... as for question #3 ... can you do that when creating your own Template-derived templates and implementing RenderCore()?  I'm sure I tried that and it blew chunks on me.  I will try it again and post an example.  Maybe I was using the wrong block type.  I'm still a little bit new to this and am still trying to figure out when to use <# #>, <#= #>, and <#+ #>.

Thanks for the feedback.  Like I said, I'm new to T4 and am still in the process of trying to discern best practices.  I'm experimenting.  I have an idea for W()/WL() w.r.t. identation, and I will post another improved example.  I appreciate your feedback on not using acronyms, but without Intellisense, it really can be a lot of typing, and this technique has helped me.