Рефлексия, кодогенерация и обёртка - C#
Формулировка задачи:
Доброго времени суток!
Сначала прикладная задача: имеется статический класс, для которого необходимо делать не статическую обёртку. Руками сделать эту обёртку не приятно, но можно. Но проект живой, развивающийся и статический класс тоже, т.е. в нём время от времени появляются новые члены. А это значит что обёртку нужно постоянно актуализировать, что совсем уж ни в какие ворота.
Я долго думал что с этим делать, рассматривал различные варианты и остановился на следующем: Custom Tool + кодогенерация. Мне кажется это будет идеальным решением. В конечном счёте обёртка всегда актуальная и для этого ничего не надо делать. Если я не прав и есть варианты по лучше, самое время мне сообщить об этом.
Едем дальше. Нагуглил я статей про Custom Tool и примерно представляю как это работает, может быть по ходу ещё возникнут какие-то вопросы, но давайте пока считать этот вопрос решённым. Сейчас же у меня есть вопросы по реализации собственно кодогенератора.
И так, опуская детали, Custom Tool принимает на вход файл, на основе которого мы будем генерить свой файл, обёртку для класса в моём случае. И тут я вижу два пути, простой и правильный.
Простой. Добавляем в кодогенератор референс на мою сборку с оборачиваемым классом, игнорируем входной файл и просто генерим то что нам нужно через рефлексию.
Но хочется сделать по правильному, а по правильному, это не завязываться на конкретную реализацию и сделать универсальный кодогенератор, который можно будет использовать и в будущем для других проектов. А это значит никаких референсов на проект и работать со входным файлом. Отсюда у меня вытекают 2 вопроса:
1. Можно ли как-то заюзать рефлексию имея лишь текстовый файл с классом?
2. Может быть есть какой-то удобный способ парсить cs файл?
Мои навыки гугления не помогли, а парсить файл руками совсем не хочется.
У простого способа есть ещё один недостаток, Custom Tool срабатывает когда изменяется файл к которому он привязан. А рефлексия подозреваю не увидит изменений в классе, пока он не будет скомпилирован. Мелочь, а не приятно.
Решение задачи: «Рефлексия, кодогенерация и обёртка»
textual
Листинг программы
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> <#@ assembly name="EnvDTE" #> <#@ import namespace="EnvDTE" #> namespace Wrappers { <# string projectName = "t4generatewrapper"; string[] classNames = {"t4generatewrapper.ClassToWrap"}; IServiceProvider serviceProvider = (IServiceProvider) this.Host; EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetService(typeof(EnvDTE.DTE)); Project currentProject = null; foreach (Project p in dte.Solution.Projects) { if(p.Name == projectName)currentProject = p; } foreach (string classFullName in classNames) { var classCode = currentProject.CodeModel.CodeTypeFromFullName(classFullName); #> public class <#= classFullName.Split('.').Last() #>Wrapper { <# foreach (CodeFunction cf in classCode.Members.OfType<CodeFunction>()) { var pars = string.Join(", ", from CodeParameter p in cf.Parameters select p.Type.AsFullName + " " + p.Name); var args = string.Join(", ", from CodeParameter p in cf.Parameters select p.Name); if(cf.Type.AsFullName == "System.Void") {#> public void <#= cf.Name #> (<#= pars #>) { <#= classFullName + "." + cf.Name #>(<#= args #>); } <# } else{ #> public <#= cf.Type.AsFullName #> <#= cf.Name #>(<#= pars #>) { return <#= classFullName + "." + cf.Name #>(<#= args #>); } <#} } #> } <# } #> }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д