Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get enum type in a T4 template

Tags:

c#

enums

t4

envdte

In the process of writing a T4 text template I ran into an issue I'm struggling to get by. I need to know the type of the enum I'm processing.

I have enums that are based on byte and ushort. I need the T4 text template to write code to cast the enum to the correct value type in order to serialize the enum and put it into a byte array.

This is an example enum of type byte

namespace CodeEnumType
{
    public enum MyEnum : byte
    {
        Member1 = 0,
        Member2 = 1,
    }
}

And this is my T4 text template

<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="EnvDte" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Collections.Generic" #>
<#
var serviceProvider = this.Host as IServiceProvider;
var dte = serviceProvider.GetService(typeof(DTE)) as DTE;
var project = dte.Solution.FindProjectItem(this.Host.TemplateFile).ContainingProject as Project;
var projectItems = GetProjectItemsRecursively(project.ProjectItems);
foreach(var projectItem in projectItems)
{
    var fileCodeModel = projectItem.FileCodeModel;
    if(fileCodeModel == null)
    {
        continue;
    }

    CodeElements codeElements = fileCodeModel.CodeElements;
    ProcessCodeElements(codeElements);
}
#>
<#+
public void ProcessCodeElements(CodeElements codeElements)
{
    if(codeElements == null)
    {
        return;
    }

    foreach(CodeElement codeElement in codeElements)
    {
        switch(codeElement.Kind)
        {
            case vsCMElement.vsCMElementNamespace:
                CodeNamespace codeNamespace = codeElement as CodeNamespace;
                CodeElements childCodeElements = codeNamespace.Members;
                ProcessCodeElements(childCodeElements);
                break;
            case vsCMElement.vsCMElementEnum:
                CodeEnum codeEnum = codeElement as CodeEnum;
                WriteLine(codeEnum.Name);
                //
                // here I would like the enum type
                //
                break;
        }
    }
}
public IEnumerable<ProjectItem> GetProjectItemsRecursively(ProjectItems items)
{
    if(items == null)
    {
        yield break;
    }

    foreach(ProjectItem item in items)
    {
        yield return item;

        var childItems = GetProjectItemsRecursively(item.ProjectItems);
        foreach(ProjectItem childItem in childItems)
        {
            yield return childItem;
        }
    }
}
#>

Notice the part where I wrote

        //
        // here I would like the enum type
        //

Here I have my enum information in the variable codeEnum and this is where my problem is. How do I get byte or ushort type from codeEnum?

I'm not using Reflection here as Type.GetType() does not work well if the assembly have not been compiled.

like image 643
Robin Theilade Avatar asked Aug 22 '16 11:08

Robin Theilade


1 Answers

The underlying enum type is not exposed by the EnvDTE object model. As a workaroud, you can retrieve the source code of the enum type definition and parse out the base type, e.g. using a regular expression:

foreach(CodeElement codeElement in codeElements)
{
    switch(codeElement.Kind)
    {
        case vsCMElement.vsCMElementNamespace:
            CodeNamespace codeNamespace = codeElement as CodeNamespace;
            CodeElements childCodeElements = codeNamespace.Members;
            ProcessCodeElements(childCodeElements);
            break;
        case vsCMElement.vsCMElementEnum:
            CodeEnum codeEnum = codeElement as CodeEnum;
            Write(codeEnum.Name);

            // get the source code of the enum
            string sourceCodeEnum = 
                codeEnum.StartPoint.CreateEditPoint().GetText(codeEnum.EndPoint);

            // a regular expression capturing the base type
            System.Text.RegularExpressions.Regex regex 
                = new System.Text.RegularExpressions.Regex(
                    @"\benum .*\s*\:\s*(?<underlyingType>\w*)");

            var match = regex.Match(sourceCodeEnum);
            if (match.Success)
            {
                WriteLine(" : " + match.Groups["underlyingType"].Value);
            }

            break;
    }
}

Note that the regex in the sample is just a very simple pattern that might need to be adjusted to cope with different source code formatting.

like image 177
Dirk Vollmar Avatar answered Oct 16 '22 12:10

Dirk Vollmar