生成枚举的JavaScript表示

本文关键字:JavaScript 表示 枚举 | 更新日期: 2023-09-27 18:14:32

我的代码中有一些[Flags]枚举,我想在没有复制粘贴的情况下暴露给JavaScript。SignalR似乎正在为他们的hub代理做类似的事情,通过将URL映射到返回由反射生成的JavaScript存根的Action。因为代码是在运行时生成的,所以它似乎不可能被包含到bundle中。

作为替代方案,我实现了一个T4模板来在设计时生成一个js文件:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="EnvDte" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".js" #>
Enums = {
<#
    var visualStudio = (Host as IServiceProvider).GetService(typeof(EnvDTE.DTE))
                        as EnvDTE.DTE;
    var project = visualStudio.Solution.FindProjectItem(this.Host.TemplateFile)
                                        .ContainingProject as EnvDTE.Project;
    foreach(EnvDTE.ProjectItem item in GetProjectItemsRecursively(project.ProjectItems))
    {
        if (item.FileCodeModel == null) continue;
        foreach(EnvDTE.CodeElement elem in item.FileCodeModel.CodeElements)
        {
            if (elem.Kind == EnvDTE.vsCMElement.vsCMElementNamespace)
            {
                foreach (CodeElement innerElement in elem.Children)
                {
                    if (innerElement.Kind == vsCMElement.vsCMElementEnum)
                    {
                        CodeEnum enu = (CodeEnum)innerElement;
#>  <#= enu.Name #>: {
<#
        Dictionary<string, string> values = new Dictionary<string, string>();
        foreach (CodeElement child in enu.Members)
        {
            CodeVariable value = child as CodeVariable;
            if (value != null) {
                string init = value.InitExpression as string;
                int unused;
                if (!int.TryParse(init, out unused))
                {
                    foreach (KeyValuePair<string, string> entry in values) {
                        init = init.Replace(entry.Key, entry.Value);
                    }
                    init = "(" + init + ")"; 
                }
                values.Add(value.Name, init);
                WriteLine("'t't" + value.Name + ": " + init + ",");
            }
        }
#>
    },
<#
                    }
                }
            }
        }
    }
#>
};
<#+
  public List<EnvDTE.ProjectItem> GetProjectItemsRecursively(EnvDTE.ProjectItems items)
  {
      var ret = new List<EnvDTE.ProjectItem>();
      if (items == null) return ret;
      foreach(EnvDTE.ProjectItem item in items)
      {
        ret.Add(item);
        ret.AddRange(GetProjectItemsRecursively(item.ProjectItems));
      }
      return ret;
  }
#>

然而,对于EnvDTE,这感觉很脆弱。特别是处理枚举的逻辑,如:

[Flags]
public enum Access
{
    None = 0,
    Read = 1,
    Write = 2,
    ReadWrite = Read | Write
}
使用复合值的

是使用字符串替换的肮脏hack。上面的T4模板将生成:

Enums = {
    Access: {
        None: 0,
        Read: 1,
        Write: 2,
        ReadWrite: (1 | 2),
    },
};

是否有更干净的方法来实现这一点?理想情况下,使用某种设计时反射来生成js文件,这样它就可以用于绑定了。

生成枚举的JavaScript表示

我认为你可以使用Bundle Transforms来完成这个…

http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

您可以定义一个bundle并附加一个IBundleTransform类,该类插入源文件的内容。你应该能够使用反射将JavaScript写入输出流。

如果你想用一种简单的方式来做,这是相当容易做到的。你可以给它提供一个空文件,然后硬编码你的类来使用反射来编写你想要的JavaScript。

现在,如果您想以一种不需要修改IBundleTransform类的方式来设计它,以便在javascript输出中添加额外的枚举,则需要在实际结构中投入一些额外的工作。例如,假设你在一个名为Enums.cs的文件中有所有的枚举,你可以将该文件添加到bundle的Include列表中,你可以动态解析它的枚举声明,然后使用反射根据名称找到它们,以便在结果JavaScript文件中输出每个枚举。

我喜欢在我的应用程序中使用自定义属性和控制器动作。在开发过程中,我添加了一个enum值并点击'Run'。我浏览到我的控制器动作(一个只有在调试期间可用的链接)。控制器抓取所有实现自定义[GenerateJavascriptEnum]属性的枚举,瞧,我看到一个弹出的浏览器窗口,里面有所有漂亮的javascript。我复制/粘贴并刷新浏览器以获得客户机上的更改。我感觉很舒服,没什么大惊小怪的。