7

3つのファイルを含むC#regex-parserプログラムがあり、それぞれに静的クラスが含まれています。

1)文字列辞書で満たされた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
}

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
}

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;
    }
}

この連結はすべて実行時に同一であるため、T4テンプレートと同様に2)を生成できるようにしたいと思います。

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

これは機能しますが、の新しいメンバーを作成するMyStringDictsか、辞書からいくつかの値を追加/削除すると、Patterns.csをコンパイルから除外して再コンパイルするまで、T4テンプレートはそれらを認識しません。Parserに依存するように、Patternsこれは実際にはオプションではありません。同じビルド内の他のファイルへの変更を考慮に入れるには、T4変換が必要です。

やりたくないこと:

  • MyStringDicts独自のプロジェクトに分割します。ファイルは論理的な単位であるため、1つのプロジェクトに保存したいと思います。
  • MyStringDictsPatterns.csの一番上に移動するだけです。MyStringDictsメンバーは、他の目的にも必要です(たとえば、辞書検索や他のT4テンプレートなど)。

ここでは、T4Toolboxなどの使用に関するアドバイスを採用しましたVolatileAssemblyが、T4テンプレートの編集後にクラスファイルを再コンパイルする必要がある場合は、逆方向にしか機能しないようです。

私が欲しいことは可能ですか?

わかりやすくするために編集

4

2 に答える 2

5

EnvDte(Visual Studio Automation)とT4Toolboxを使用して最初のファイルを実行する小さなテストテンプレートを作成しました。プロジェクトを通じてファイルを取得するため、テンプレートを実行する前にコンパイルする必要はありません。実際、保存されていない変更も取得します...

これは基本的にFullSnabelが使用するアプローチと同じですが、Roslynは必要ありません。

<#@ 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);
                    }
                }
            }
        }
    }
#>

これは、元のアプローチに固執したい場合に機能するはずです。

あなたがしているように見えるのは、クラスファイルにデータを保存することです。リストをコードの外部(xmlまたはiniファイル)に保存し、そのデータに基づいて両方のファイルを生成することを検討できます。そうすれば、問題をまとめて回避でき、リストの管理も簡単になる可能性があります。リストの変更をあまり気にしない場合は、辞書をT4テンプレート自体の中に入れることもできます。

別の方法として、コードで完全に処理することもできます。'Pattern'プロパティ(またはGetPattern()関数)を持つDictionaryのサブクラスを作成できます。その後、パーサーはAddrVals.USstates.Patternを使用し、パターンクラスは不要になります。この方法では、コードを生成する必要はありません。

実際のコレクションを非表示にして、実行時に変更されないようにすることができるため、実際のディクショナリのラッパーの方が適している可能性があります。.NETで利用可能な読み取り専用の汎用辞書はありますか?を参照してください。その一例として。

于 2012-07-04T12:16:08.910 に答える
4

roslynを見てください。これにより、ソースファイルを構文ツリーにコンパイルして、そこからコードを検査および生成できます。これはCTPですが、私にとっては非常にうまく機能しました。

(Roslynサンプルを追加)。

ソリューションにclass2.csというファイルを作成しました。

namespace StackOverflow
{
    class Class2
    {
        public static int One() { return 8; }
        public static int Eight(int x, double z) { return 8; }
    }
}

Roslyn CTPを使用して( Visual Studio SDKも必要です)、Roslynを使用してClass2.csを解析し、それに基づいて出力を生成するこの単純なT4テンプレートを作成しました。

<#@ template    hostspecific= "true"                            #>
<#@ assembly    name        = "System.Core"                     #>
<#@ assembly    name        = "Roslyn.Compilers"                #>
<#@ assembly    name        = "Roslyn.Compilers.CSharp"         #>
<#@ import      namespace   = "System.IO"                       #>
<#@ import      namespace   = "System.Linq"                     #>
<#@ import      namespace   = "Roslyn.Compilers.CSharp"         #>

<#

    var host    = Path.GetFullPath(Host.ResolvePath(@".\Class2.cs"));
    var content = File.ReadAllText(host);

    var tree = SyntaxTree.ParseCompilationUnit(content);

    var methods = tree
        .GetRoot()
        .ChildNodes()
        .OfType<NamespaceDeclarationSyntax>()
        .SelectMany(x => x.ChildNodes())
        .OfType<ClassDeclarationSyntax>()
        .SelectMany(x => x.ChildNodes())
        .OfType<MethodDeclarationSyntax>()
        .ToArray()
        ;
#>            

namespace StackOverflow
{
    using System;

    static partial class Program
    {
        public static void Main()
        {
<#
    foreach (var method in methods)
    {
        var parent = (ClassDeclarationSyntax)method.Parent;
        var types = method
            .ParameterList
            .ChildNodes()
            .OfType<ParameterSyntax>()
            .Select(t => t.Type.PlainName)
            .ToArray()
            ;

        var plist = string.Join(", ", types);
#>
            Console.WriteLine("<#=parent.Identifier.ValueText#>.<#=method.Identifier.ValueText#>(<#=plist#>).ToString()");
<#
    }
#>
        }
    }
}

このテンプレートは、Class2.csに基づいて次の出力を生成します。

namespace StackOverflow
{
    using System;

    static partial class Program
    {
        public static void Main()
        {
                Console.WriteLine("Class2.One().ToString()");
                Console.WriteLine("Class2.Eight(int, double).ToString()");
            }
    }
}

お役に立てれば

于 2012-06-27T19:06:37.937 に答える