Как получить тип перечисления в шаблоне T4

В процессе написания текстового шаблона T4 я столкнулся с проблемой, которую никак не могу решить. Мне нужно знать тип обрабатываемого перечисления.

У меня есть перечисления, основанные на byte и ushort. Мне нужен текстовый шаблон T4, чтобы написать код для приведения перечисления к правильному типу значения, чтобы сериализовать перечисление и поместить его в массив байтов.

Это пример перечисления типа byte

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

А это мой текстовый шаблон T4

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

Обратите внимание на ту часть, где я написал

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

Здесь у меня есть информация о перечислении в переменной codeEnum, и здесь моя проблема. Как мне получить тип byte или ushort от codeEnum?

Я не использую здесь Reflection, поскольку Type.GetType () не работает, если сборка не была скомпилирована.


person Robin Theilade    schedule 22.08.2016    source источник
comment
Почему вы хотите привести перечисление к правильному типу значения?   -  person qxg    schedule 22.08.2016
comment
Что такое CodeEnum? Какую технику генерации кода вы на самом деле используете - T4 или CodeDom? Похоже на причудливую смесь, если Code* относятся к классам CodeDom.   -  person Ondrej Tucny    schedule 22.08.2016
comment
@OndrejTucny В процессе написания текстового шаблона T4   -  person Francis Lord    schedule 22.08.2016
comment
Я не очень хорошо знаком с T4, но не могли бы вы использовать отражение типа Enum, чтобы узнать его наследование?   -  person Francis Lord    schedule 22.08.2016
comment
Возможно, вы ищете это: msdn.microsoft.com/en-us/library/ Однако ваш вопрос неоднозначен. T4 / CodeDom / Отражение? Это совершенно разные концепции.   -  person Ondrej Tucny    schedule 22.08.2016
comment
@qxg Я добавил причину в описание проблемы.   -  person Robin Theilade    schedule 22.08.2016
comment
@OndrejTucny Я использую EnvDTE (CodeDom?) В T4, так как не знаю, как читать код внутри T4 без EnvDTE   -  person Robin Theilade    schedule 22.08.2016
comment
@FrancisLord Я мог бы использовать Reflection, но я не знаю, как мне перейти от типа CodeEnum к System.Type того же перечисления.   -  person Robin Theilade    schedule 22.08.2016
comment
@OndrejTucny, мой вопрос не о отражении. Если вы называете EnvDTE CodeDom, то в этом случае T4 и CodeDom смешиваются.   -  person Robin Theilade    schedule 22.08.2016
comment
@JeppeStigNielsen правильно   -  person Robin Theilade    schedule 22.08.2016
comment
Что дает foreach (var b in codeEnum.Bases) { WriteLine(b); } или подобное? Я подозреваю, что базовый тип можно найти через свойство .Bases.   -  person Jeppe Stig Nielsen    schedule 22.08.2016
comment
@JeppeStigNielsen Это ближайшие три уровня выводимых баз: System.Enum - ›System.ValueType -› System.Object   -  person Robin Theilade    schedule 22.08.2016
comment
Очень жаль; тогда это похоже на .BaseType во время размышления. Тогда я подумал, что это может сработать, потому что метод AddBase spec сказал что-то вроде: Для CodeEnum объектов Base - это вариант, содержащий полное имя типа или CodeType объект, на котором основано новое перечисление. Для C # это базовый тип перечисления. Не то, чтобы я много об этом знаю.   -  person Jeppe Stig Nielsen    schedule 22.08.2016
comment
@JeppeStigNielsen Я тоже на это надеялся. Я также рассмотрел реализацию Type.GetEnumUnderlyingType() и посмотрел на тип первого члена перечисления. Я пробовал это раньше, но безуспешно, но, может быть, мне стоит попробовать еще раз.   -  person Robin Theilade    schedule 22.08.2016
comment
Интересно. Тип имеет нестатическое поле, которое называется value__ и содержит базовое целочисленное значение. Это не первая из констант перечисления, определяемых типом (они статические, и констант перечисления может быть нулевым). То, что делает этот метод, эквивалентно (отражение): .GetField("value__").FieldType, которое действительно работает! Однажды я задался вопросом об этом поле экземпляра в другом вопросе: Действительно ли типы перечислений .NET являются изменяемыми типами значений?   -  person Jeppe Stig Nielsen    schedule 22.08.2016
comment
Давайте продолжим это обсуждение в чате.   -  person Robin Theilade    schedule 22.08.2016
comment
Можете ли вы обмануть и использовать отражение с помощью: Type.GetType(codeEnum.FullName).GetEnumUnderlyingType()?   -  person Jeppe Stig Nielsen    schedule 22.08.2016
comment
@JeppeStigNielsen, это могло бы быть возможно, но мне пришлось бы полагаться на сборку, которая была скомпилирована один раз после каждого изменения .. не оптимально   -  person Robin Theilade    schedule 22.08.2016


Ответы (2)


Базовый тип перечисления не предоставляется объектной моделью EnvDTE. В качестве обходного пути вы можете получить исходный код определения типа перечисления и проанализировать базовый тип, например используя регулярное выражение:

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

Обратите внимание, что регулярное выражение в примере - это просто очень простой шаблон, который, возможно, потребуется настроить, чтобы справиться с различным форматированием исходного кода.

person Dirk Vollmar    schedule 25.08.2016
comment
Фантастика! Я изменил последний. * На \ w *, чтобы избежать пробелов в конце значения группы. @ \ benum. * \ s * \: \ s * (? ‹underlyingType› \ w *). Также я не знал \ b, это очень умно. Спасибо - person Robin Theilade; 25.08.2016

Этот метод должен вернуть то, что вы ищете.

static Type GetEnumType<T>() where T : struct, IComparable
{
    var ti = (TypeInfo)typeof(T);
    return ti.DeclaredFields.First().FieldType;
}

Надеюсь это поможет ;)

person sQuir3l    schedule 25.08.2016
comment
Это может сработать, но OP ищет решение без размышлений, поскольку для этого потребуется, чтобы исходный код уже был скомпилирован. - person Dirk Vollmar; 25.08.2016
comment
@DirkVollmar правильно. Я не ищу решение, основанное на отражении - person Robin Theilade; 25.08.2016