XWiśniowiecki
XWiśniowiecki

Reputation: 1843

T4 - Error 60: A field initializer cannot reference the non-static field, method, or property

I try to use the solution from Microsoft documentation.

At the beginning of my .tt file I put:

<#@ template language="C#" hostSpecific="True" #>
<#@ output extension="cs" #>
<#@ import namespace="System.IO" #>

Then I try to get some variables from my previosly transformed file:

<#+
    string myFile = File.ReadAllText(Host.ResolvePath("AssemblyInfo1.cs"));
#>

In the line above I get error. If the line is commented everything is OK. Error output is:

Error 60: A field initializer cannot reference the non-static field, method, or property

Upvotes: 0

Views: 624

Answers (2)

Mohamed Zammit
Mohamed Zammit

Reputation: 31

OK this is a example :

Main Template

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

Calling Template

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

Output

public class TestClass
{
    public string Property
    {
        get { return _property; }
        set { _property = value; }
    }
}

How It Works The main template defines its parameters as read-only properties that get their values from the remoting CallContext, such as ClassName property in the example above. The calling template provides parameter values by placing them in the CallContext. It then uses T4 Engine to compile and execute the main template and save its output. Pros Standalone Template technique allows to parameterize a one-off template and reuse it to generate multiple artifacts of the same type. This technique provides good encapsulation of the main template and allows to compose templates to generate multiple outputs from a single calling template. This technique can be used to invoke a T4 template from external code, such as a console application.

Cons Standalone Template technique requires using an external, weakly-typed mechanism, such as CallContext or an XML file, to pass parameter values to the main template.

This technique does not allow to extend the main template without resorting to modification of the main template itself.

About T4 T4 (Text Template Transformation Toolkit) is a template-based code generation engine. It is available in Visual Studio 2008 and as a download in DSL and GAT toolkits for Visual Studio 2005. T4 engine allows you to use ASP.NET-like template syntax to generate C#, T-SQL, XML or any other text files.

Upvotes: 2

Frank
Frank

Reputation: 2198

It has do with using a <#+ #> block instead of using a <# #>. <#+ #> blocks are class feature control blocks and can only contain code that works within a class (ie properties/functions/class level variables). See the MS docs for more info on how this works but basically at execution your template is transformed into a class and executed in another app domain and the results as text are saved to a file. Essentially what your code was trying to do is access a local variable of the class in an initialization statement like this:

public class T4GeneratedClass 
{
    public object Host { get; set; }

    private string myFile = File.ReadAllText(this.Host.ResolvePath("AssemblyInfo1.cs"));
}

This is not valid code, the statement after = is a trying to set the value "myFile" using a property (this.Host) on the same class it is being defined in. "File.ReadAllText" is ok because that is a static function and not part of the same class.

The other answers given should work because they are creating new functions on the class and from the body of those functions the "Host" property (this.Host) is available.

For your code to work as is, change the control block type from <#+ #> to <# #>. This will create your "myFile" variable in the main function and it will have access to the "Host" property.

If you want to understand more about how T4 works you can download my extension, I have a feature (its part of the community version so you can use it for free) that will show you the actually class that is generated and used by the T4 framework, it should help shed some light on what T4 is doing and why this would not work.

Upvotes: 1

Related Questions