Как перехватить, разобрать и скомпилировать?
Это проблема, которую я долго пытался решить. Мне нужен способ заменить код в методе на анализируемый код из шаблона во время компиляции (на ум приходит PostSharp) или создать динамический прокси-сервер (Linfu или Castle). Так, учитывая исходный код, как это
[Template]
private string GetSomething()
{
var template = [%=Customer.Name%]
}
Мне нужно, чтобы это было скомпилировано в это
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();
}
Не важно какая технология используется. Я попытался использовать PostSharp ImplementMethodAspect, но ничего не получилось (из-за отсутствия опыта работы с ним). Я также посмотрел в рамки Linfu. Может кто-нибудь предложить какой-то другой подход или способ сделать это, я был бы очень признателен. Весь мой проект зависит от этого.
Предположения:
- Код может появиться в любом классе.
- Код шаблона всегда будет аннотирован атрибутом [Template]
- Шаблонный метод всегда будет возвращать строку.
Разбор кода из одной формы в другую уже сделан. Теперь мне просто нужен способ заменить его.
Пример "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));
}
2 ответа
Взгляните на T4 (Набор инструментов преобразования текстовых шаблонов).
<#@ 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();");
// ...
}
#>
Инструментом, который может анализировать C#, выбирать атрибуты и преобразовывать этот код любым удобным вам способом, является набор инструментов реинжиниринга программного обеспечения DMS и его интерфейсная часть C#.
DMS анализирует ваши файлы, создает полные синтаксические деревья синтаксиса и позволяет вам писать собственные преобразования, которые могут расширить точку в тексте (что делает большинство генераторов кода, например, T4) или, что более важно, заменить любую конструкцию (как локальную, так и / или распределенную по всему набор файлов, из которых состоит приложение) с любым другим кодом, который вы можете сгенерировать.
Похоже, у вас есть представление о метаязыке для создания фрагментов кода, например:
From operation In metaObject.operations _
Select (MethodTemplate.Create(operation).GetSourceCode)
Этот метаязык AFAIK не C#. С DMS вы можете определить синтаксический анализатор для метаязыка и обработать метаязык в деревья, когда генератор кода встречает конструкции. С помощью небольшого интерпретатора над такими деревьями вы можете конвертировать металический текст в действия генератора, создавая интересующий вас текст.
[Я технический директор DMS].