4

我已经开始涉足 T4 并且一开始相处得很好,但后来遇到了一个实际上非常明显并且可能无法解决的问题,但也许有一种方法我只是缺乏了解或了解的经验。

给定以下课程:

public class T4Test : CodeActivity
{
    protected override void Execute(CodeActivityContext context)
    {
    }

    [Input("InX")]
    public InArgument<string> InX { get; set; }

    [Output("OutX")]
    public OutArgument<string> OutX { get; set; }
}

我想要这个作为输出:

public class ActivityWrapper
{
    private readonly T4Test _activity;
    private readonly ActivityContext _context;

    public ActivityWrapper(T4Test activity, ActivityContext context)
    {
        this._activity = activity;
        this._context = context;
    }

    public string InX
    {
        get { return this._activity.InX.Get(this._context); }
    }

    public string OutX
    {
        get { return this._activity.OutX.Get(this._context); }
        set { this._activity.OutX.Set(this._context, value); }
    }
}

我已经弄清楚了我需要的反射的东西,我知道 T4 代码应该是什么样子,但是有一个问题:我需要它与T4Test类在同一个项目中。但是,要加载程序集并对其进行反映,需要对其进行编译——但如果我打算修改同一个程序集的代码,那当然有点困难。(而且我猜 NCrunch 并没有简化事情。)

现在这是我希望仍然可以解决这个问题的事情:

  • 该项目在没有生成的类的情况下编译。这是因为该类将实现由 IoC 容器自动注册/解析的接口。无论如何它也是不可测试的,因为ActivityContext不能被嘲笑。
  • 出于这个原因,它不必一直存在或正确。在实际交付 DLL 之前,我只需要能够说“立即生成”。
  • 出于同样的原因,我也不关心 T4 模板是否真的存在于项目中——只要生成的文件最终出现在项目中(尽管不需要另一个项目作为模板并构建 PostBuild 事件来复制.cs文件) .
  • 准确地说,它甚至不需要是T4。如果有任何其他可行的方法可以做到这一点,我也很乐意使用它。

有什么办法可以做到这一点?(这是否足够清楚?)

4

2 回答 2

6

我想提出一种替代方法来反映生成的程序集,因为转换 T4 仅在项目成功构建并生成正确的输出时才有效,前提是程序集没有过时。

如果您使用特定于主机的 T4 模板,您可以通过 EnvDTE 接口访问 Visual Studio 自动化模型。使用它,您可以遍历当前加载的 Visual Studio 解决方案的 CodeModel,而无需先构建它。

看看我对这个 SO 问题的回答:Design Time Reflection。借助有形模板库中的免费模板,您可以在设计时轻松“反映”现有类并检测用所需属性装饰的属性:

<#
var project = VisualStudioHelper.CurrentProject;

// get all class items from the code model
var allClasses = VisualStudioHelper.GetAllCodeElementsOfType(project.CodeModel.CodeElements, EnvDTE.vsCMElement.vsCMElementClass, false);

// iterate all classes
foreach(EnvDTE.CodeClass codeClass in allClasses)
{
    // iterate all properties
    var allProperties = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.Members, EnvDTE.vsCMElement.vsCMElementProperty, true);
    foreach(EnvDTE.CodeProperty property in allProperties)
    {
        // check if it is decorated with an "Input"-Attribute
        if (property.Attributes.OfType<EnvDTE.CodeAttribute>().Any(a => a.FullName == "Input"))
        {
            ...
        }
    }
}
#>
于 2013-03-26T19:53:53.920 回答
3

T4Test.tt

<#@ include file="Activities.tt" #>
<#
var t4test = new Activity("T4Test")
{
    Input("InX"),
    Output("OutX"),
};
GenerateCode(t4test);
#>

活动.tt

<#@ template language="C#" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#+
class Activity : IEnumerable<Property>
{
    private string name, wrapper;
    private List<Property> properties;
    public Activity(string name, string wrapper = null)
    {
        this.name = name;
        this.wrapper = wrapper ?? name + "Wrapper";
        this.properties = new List<Property>();
    }
    public void Add(Property property)
    {
        this.properties.Add(property);
    }
    public IEnumerator<Property> GetEnumerator()
    {
        return this.properties.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    public void GenerateCode()
    {
        // ...
    }
}
class Property
{
    private bool output;
    private string name, type;
    public Property(bool output, string name, string type)
    {
        this.output = output;
        this.name = name;
        this.type = type;
    }
}
Property Input(string name, string type = "string")
{
    return new Property(false, name, type);
}
Property Output(string name, string type = "string")
{
    return new Property(true, name, type);
}
void GenerateCode(params Activity[] activities)
{
    WriteLine("namespace Foo");
    WriteLine("{");
    PushIndent("   ");
    foreach (var activity in activities)
    {
        WriteLine("class " + activity.name);
        WriteLine("{");
        PushIndent("   ");
        // ...
        PopIndent();
        WriteLine("}");
    }
    PopIndent();
    WriteLine("}");
}
#>
于 2013-03-26T01:30:41.050 回答