T4 - 错误 60:字段初始值设定项无法引用非静态字段、方法或 属性
T4 - Error 60: A field initializer cannot reference the non-static field, method, or property
我尝试使用 Microsoft documentation 的解决方案。
在我的 .tt
文件的开头,我输入:
<#@ template language="C#" hostSpecific="True" #>
<#@ output extension="cs" #>
<#@ import namespace="System.IO" #>
然后我尝试从我之前转换的文件中获取一些变量:
<#+
string myFile = File.ReadAllText(Host.ResolvePath("AssemblyInfo1.cs"));
#>
在上面的行中我得到了错误。如果该行被注释,则一切正常。
错误输出为:
Error 60: A field initializer cannot reference the non-static field,
method, or property
好的,这是一个例子:
主模板
<#@ template language="C#" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
public class <#= ClassName #>
{
public string Property
{
get { return _property; }
set { _property = value; }
}
}
<#+
string ClassName
{
get { return (string)CallContext.LogicalGetData("ClassName"); }
}
#>
调用模板
<#@ template language="C#" hostspecific="True" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#
CallContext.LogicalSetData("ClassName", "TestClass");
string output = ProcessTemplate("Template.tt");
Write(output);
#>
<#+
string ProcessTemplate(string templateFileName)
{
string template = File.ReadAllText(Host.ResolvePath(templateFileName));
Engine engine = new Engine();
return engine.ProcessTemplate(template, Host);
}
#>
输出
public class TestClass
{
public string Property
{
get { return _property; }
set { _property = value; }
}
}
工作原理 主模板将其参数定义为只读属性,这些属性从远程 CallContext 获取值,例如上例中的 ClassName 属性。调用模板通过将参数值放在 CallContext 中来提供参数值。然后它使用 T4 引擎编译和执行主模板并保存其输出。
优点
独立模板技术允许参数化一次性模板并重新使用它来生成相同类型的多个工件。这种技术提供了对主模板的良好封装,并允许组合模板以从单个调用模板生成多个输出。此技术可用于从外部代码调用 T4 模板,例如控制台应用程序。
缺点
独立模板技术需要使用外部弱类型机制,例如 CallContext 或 XML 文件,将参数值传递给主模板。
此技术不允许在不修改主模板本身的情况下扩展主模板。
关于T4
T4(Text Template Transformation Toolkit)是一个基于模板的代码生成引擎。它在 Visual Studio 2008 中可用,并可在 Visual Studio 2005 的 DSL 和 GAT 工具包中下载。T4 引擎允许您使用类似于 ASP.NET 的模板语法来生成 C#、T-SQL、XML 或任何其他文本文件。
它与使用 <#+ #> 块而不是使用 <# #> 有关。 <#+ #> 块是 class 功能控制块,只能包含在 class 内工作的代码(即 properties/functions/class 级变量)。有关其工作原理的更多信息,请参阅 MS docs,但基本上在执行时,您的模板会转换为 class 并在另一个应用程序域中执行,并将结果作为文本保存到文件中。本质上,您的代码试图做的是在这样的初始化语句中访问 class 的局部变量:
public class T4GeneratedClass
{
public object Host { get; set; }
private string myFile = File.ReadAllText(this.Host.ResolvePath("AssemblyInfo1.cs"));
}
这不是有效的代码,= 之后的语句是尝试使用 属性 (this.Host) 在同一个 class 上设置值 "myFile"正在定义中。"File.ReadAllText" 没问题,因为这是一个静态函数,而不是相同 class.
的一部分
给出的其他答案应该有效,因为它们正在 class 上创建新函数,并且从这些函数的主体中 "Host" 属性 (this.Host) 是可用的。
为了让您的代码按原样工作,请将控制块类型从 <#+ #> 更改为 <# #>。这将在主函数中创建您的 "myFile" 变量,它将可以访问 "Host" 属性.
如果您想了解更多有关 T4 工作原理的信息,可以下载 my extension,我有一个功能(它是社区版本的一部分,因此您可以免费使用)可以向您展示实际 class 由 T4 框架生成和使用,它应该有助于阐明 T4 正在做什么以及为什么这不起作用。
我尝试使用 Microsoft documentation 的解决方案。
在我的 .tt
文件的开头,我输入:
<#@ template language="C#" hostSpecific="True" #>
<#@ output extension="cs" #>
<#@ import namespace="System.IO" #>
然后我尝试从我之前转换的文件中获取一些变量:
<#+
string myFile = File.ReadAllText(Host.ResolvePath("AssemblyInfo1.cs"));
#>
在上面的行中我得到了错误。如果该行被注释,则一切正常。 错误输出为:
Error 60: A field initializer cannot reference the non-static field, method, or property
好的,这是一个例子:
主模板
<#@ template language="C#" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
public class <#= ClassName #>
{
public string Property
{
get { return _property; }
set { _property = value; }
}
}
<#+
string ClassName
{
get { return (string)CallContext.LogicalGetData("ClassName"); }
}
#>
调用模板
<#@ template language="C#" hostspecific="True" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#
CallContext.LogicalSetData("ClassName", "TestClass");
string output = ProcessTemplate("Template.tt");
Write(output);
#>
<#+
string ProcessTemplate(string templateFileName)
{
string template = File.ReadAllText(Host.ResolvePath(templateFileName));
Engine engine = new Engine();
return engine.ProcessTemplate(template, Host);
}
#>
输出
public class TestClass
{
public string Property
{
get { return _property; }
set { _property = value; }
}
}
工作原理 主模板将其参数定义为只读属性,这些属性从远程 CallContext 获取值,例如上例中的 ClassName 属性。调用模板通过将参数值放在 CallContext 中来提供参数值。然后它使用 T4 引擎编译和执行主模板并保存其输出。 优点 独立模板技术允许参数化一次性模板并重新使用它来生成相同类型的多个工件。这种技术提供了对主模板的良好封装,并允许组合模板以从单个调用模板生成多个输出。此技术可用于从外部代码调用 T4 模板,例如控制台应用程序。
缺点 独立模板技术需要使用外部弱类型机制,例如 CallContext 或 XML 文件,将参数值传递给主模板。
此技术不允许在不修改主模板本身的情况下扩展主模板。
关于T4 T4(Text Template Transformation Toolkit)是一个基于模板的代码生成引擎。它在 Visual Studio 2008 中可用,并可在 Visual Studio 2005 的 DSL 和 GAT 工具包中下载。T4 引擎允许您使用类似于 ASP.NET 的模板语法来生成 C#、T-SQL、XML 或任何其他文本文件。
它与使用 <#+ #> 块而不是使用 <# #> 有关。 <#+ #> 块是 class 功能控制块,只能包含在 class 内工作的代码(即 properties/functions/class 级变量)。有关其工作原理的更多信息,请参阅 MS docs,但基本上在执行时,您的模板会转换为 class 并在另一个应用程序域中执行,并将结果作为文本保存到文件中。本质上,您的代码试图做的是在这样的初始化语句中访问 class 的局部变量:
public class T4GeneratedClass
{
public object Host { get; set; }
private string myFile = File.ReadAllText(this.Host.ResolvePath("AssemblyInfo1.cs"));
}
这不是有效的代码,= 之后的语句是尝试使用 属性 (this.Host) 在同一个 class 上设置值 "myFile"正在定义中。"File.ReadAllText" 没问题,因为这是一个静态函数,而不是相同 class.
的一部分给出的其他答案应该有效,因为它们正在 class 上创建新函数,并且从这些函数的主体中 "Host" 属性 (this.Host) 是可用的。
为了让您的代码按原样工作,请将控制块类型从 <#+ #> 更改为 <# #>。这将在主函数中创建您的 "myFile" 变量,它将可以访问 "Host" 属性.
如果您想了解更多有关 T4 工作原理的信息,可以下载 my extension,我有一个功能(它是社区版本的一部分,因此您可以免费使用)可以向您展示实际 class 由 T4 框架生成和使用,它应该有助于阐明 T4 正在做什么以及为什么这不起作用。