Search code examples
c#compiler-constructionpostsharpcastle-dynamicproxylinfu-dynamicproxy

How to intercept, parse and compile?


This is a problem I've been struggling to solve for a while. I need a way to either replace code in the method with a parsed code from the template at compile time (PostSharp comes to mind) or to create a dynamic proxy (Linfu or Castle). So given a source code like this

[Template]

private string GetSomething()

{

var template = [%=Customer.Name%]

}

I need it to be compiled into this

private string GetSomething()

{

MemoryStream mStream = new MemoryStream();

            StreamWriter writer = new StreamWriter(mStream,System.Text.Encoding.UTF8);

writer.Write(@"" );

writer.Write(Customer.Name);

StreamReader sr = new StreamReader(mStream); 

writer.Flush();

mStream.Position = 0; 

return sr.ReadToEnd();

}

It is not important what technology is used. I tried with PostSharp's ImplementMethodAspect but got nowhere (due to lack of experience with it). I also looked into Linfu framework. Can somebody suggest some other approach or way to do this, I would really appreciate. My whole project depends on this.

Assumptions:

  1. Code can appear in any class.
  2. Template code will always be annotated with attribute [Template]
  3. Template method will always return string.

Parsing of the code from one form to another is already done. Now I just need a way to replace it.

"Beefer" example:

  [Test]
        public void can_parse_csharp_code_template3()
        {
            var template = @"<template> [%= GetUsing() %]
    namespace [%= GetModelNamespaceName(.metaPackage) %]
    {
    [%= .visibility.ToString().ToLower() %] class [%= .Name %] : INotifyPropertyChanged [%= If(.IsPersistent, "", PersistenObject"", """") %]
        {
            #region Constructors
            [%= ConstructorTemplate.Create(metaObject).GetParameterlessConstructorCode %]
            #endregion

            #region Attributes

            [%= From attribute In metaObject.attributes _
                Select (AttributeTemplate.Create(attribute).GetSourceCode) %]
            #endregion

            #region Relationships
            [%= From relationship As Relationship In metaObject.relationships _
                Select (RelationshipTemplateFactory.CreateFor(relationship).GetSourceCode()) %]
            #endregion

            #region Methods
            [%= From operation In metaObject.operations _
                Select (MethodTemplate.Create(operation).GetSourceCode) %]
            #endregion

            #region ""INotifyPropertyChanged""
            [%= GetOnPropertyChanged() %]
            #endregion
            }
        }</template>";

            Console.WriteLine(TemplateParser.GetParsedResult(template));

        }

Solution

  • Have a look at T4 (Text Template Transformation Toolkit).

    <#@ template language="C#v3.5" #>
    <#@ assembly name="System.Core" #>
    <#@ output extension=".cs" encoding="utf-8" #>
    
    private string GetSomething()
    {
        <# Generate("Customer.Name"); #>
    }
    
    <#+
    private void Generate(string s)
    {
        WriteLine(@"MemoryStream mStream = new MemoryStream();");
        // ...
    }
    #>