修改同类代码文件时生成T4文件

Ari*_*iac 7 c# t4

我有一个C#regex-parser程序,里面有三个文件,每个文件都包含一个静态类:

1)一个填充字符串字典的静态类

static class MyStringDicts
{
    internal static readonly Dictionary<string, string> USstates =
        new Dictionary<string, string>()
        {
            { "ALABAMA", "AL" },
            { "ALASKA", "AK" },
            { "AMERICAN SAMOA", "AS" },
            { "ARIZONA", "AZ" },
            { "ARKANSAS", "AR" }
             // and so on
        }
    // and some other dictionaries
}
Run Code Online (Sandbox Code Playgroud)

2)将这些值编译为正则表达式的类

public static class Patterns
{       
    Public static readonly string StateUS =
        @"\b(?<STATE>" + CharTree.GenerateRegex(Enumerable.Union(
            AddrVals.USstates.Keys,
            AddrVals.USstates.Values))
        + @")\b";

    //and some more like these
}
Run Code Online (Sandbox Code Playgroud)

3)一些基于这些字符串运行正则表达式的代码:

public static class Parser
{   
    // heavily simplified example
    public static GroupCollection SearchStringForStates(string str)
    {
        return Regex.Match(str, 
            "^" + Patterns.StateUS, 
            RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase).Groups;
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望能够像T4模板一样生成2),因为所有这些连接在每次执行时都是相同的:

@"\b(?<STATE><#=CharTree.GenerateRegex(Enumerable.Union(
    AddrVals.USstates.Keys,
    AddrVals.USstates.Values)#>)\b";
Run Code Online (Sandbox Code Playgroud)

这工作,但如果我创造的一个新成员MyStringDicts,从它的字典,或者添加/删除一些值,在T4模板将无法识别它们,直到从编译过程中排除Patterns.cs并重新编译.作为Parser依赖于Patterns,这真的不是一个选择-我需要的T4转变考虑到相同的生成帐户更改到其他文件.

我不想做的事情:

  • 拆分MyStringDicts成自己的项目.我想将文件保存在一个项目中,因为它们是一个逻辑单元.
  • 只需MyStringDicts进入Patterns.cs的顶部即可.我也需要MyStringDicts成员用于其他目的(例如,对于字典查找或其他T4模板).

我采用的建议在这里关于使用T4Toolbox的VolatileAssembly等,但似乎只针对反方向工作,上课的时候文件需要编辑T4模板后,重新编译.

想要的是什么?

编辑清楚

AVe*_*Vee 5

我刚刚创建了一个小的测试模板,该模板使用EnvDte(Visual Studio自动化)和T4Toolbox来运行第一个文件。它通过项目拾取文件,因此无需在运行模板之前进行编译。实际上,它甚至可以接收未保存的更改。

这基本上与FullSnabel所使用的方法相同,但是不需要罗斯林。

<#@ template debug="false" hostspecific="True" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ dte processor="T4Toolbox.DteProcessor" #>
<#@ TransformationContext processor="T4Toolbox.TransformationContextProcessor" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ import namespace="T4Toolbox" #>
<#@ import namespace="EnvDTE" #> 
<#@ import namespace="EnvDTE80" #>
<#
    ProjectItem projectItem = TransformationContext.FindProjectItem("Dictionaries.cs");
    FileCodeModel codeModel = projectItem.FileCodeModel;

    foreach (CodeElement element in codeModel.CodeElements)
    {
        CodeNamespace ns = element as CodeNamespace;
        if(ns != null)
        {
            foreach(CodeElement ele in ns.Children)
            {
                CodeClass cl = ele as CodeClass;

                if(cl != null && cl.Name == "Dictionaries")
                {
                    foreach(CodeElement member in cl.Members)
                    {
                        // Generate stuff...
                        this.WriteLine(member.Name);
                    }
                }
            }
        }
    }
#>
Run Code Online (Sandbox Code Playgroud)

如果您要坚持原来的方法,这应该可以工作。

您似乎正在做的是将数据存储在类文件中。您可以考虑将列表存储在代码外部(在xml或ini文件中),然后根据该数据生成两个文件。这样一来,您就可以避免所有问题,也可以简化列表的管理。如果您不太关心列表的更改,也可以将字典放在T4模板本身中。

另一种选择可能是在代码中完全处理它。您可以创建具有'Pattern'属性(或GetPattern()函数)的Dictionary的子类。然后,解析器将使用AddrVals.USstates.Pattern,并且不再需要pattern类。这样,您将不需要任何代码生成。

围绕实际字典的包装也许会更好,因为它允许您隐藏实际集合以确保在运行时不会更改它。请参阅.NET中是否有只读的通用词典?举个例子。