Como interceptar, analisar e compilar?
Pergunta
Este é um problema que tenho lutado para resolver por um tempo. Preciso de uma maneira de substituir o código no método por um código analisado do modelo no horário de compilação (PostSharp vem à mente) ou para criar um proxy dinâmico (Linfu ou Castle). Então, dado um código -fonte como este
[Template]
private string GetSomething()
{
var template = [%=Customer.Name%]
}
Eu preciso que seja compilado nisso
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();
}
Não é importante o que a tecnologia é usada. Eu tentei com o implementMethodaspecto do PostSharp, mas não cheguei a lugar algum (devido à falta de experiência com ele). Eu também olhei para a estrutura de Linfu. Alguém pode sugerir alguma outra abordagem ou maneira de fazer isso, eu realmente apreciaria. Todo o meu projeto depende disso.
Suposições:
- O código pode aparecer em qualquer classe.
- O código do modelo sempre será anotado com atributo [modelo
- O método do modelo sempre retornará a string.
A análise do código de um formulário para outro já está concluída. Agora eu só preciso de uma maneira de substituí -lo.
Exemplo de "Beefer":
[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));
}
Solução
Dê uma olhada em T4 (kit de ferramentas de transformação do modelo de texto).
<#@ 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();");
// ...
}
#>
Outras dicas
Uma ferramenta que pode analisar C#, escolher atributos e transformar esse código da maneira que desejar, é o DMS Software Reengeneering Toolkit e os seus C# front -end.
O DMS analisa seus arquivos, cria árvores de sintaxe de abstacto completas e permite escrever transformações personalizadas que possam expandir um ponto no texto (o que a maioria dos geradores de código como T4 faz) ou, mais importante, substitua qualquer construto (local e/ou distribuído através do definir de arquivos que compõem o aplicativo) com qualquer outro código que você possa gerar.
Você parece ter uma idéia sobre a metalinguagem para produzir fragmentos de código, por exemplo,
From operation In metaObject.operations _
Select (MethodTemplate.Create(operation).GetSourceCode)
Esse metalanguage afaik não é C#. Com o DMS, você pode definir um analisador para a metalanguage e processar a meta Langauge em árvores, à medida que o gerador de código encontra as construções. Com um pequeno intérprete sobre essas árvores, você pode converter o texto do MetalNguage em ações geradoras que produzem o texto de interesse.
Eu sou o CTO atrás do DMS].