c# - 使用 Visual Studio 的代码模型查找类的更快方法是什么?

标签 c# visual-studio t4 c#-code-model

CodeModel 是在 Visual Studio 中发现代码的强大工具。我们使用VS2013的CodeModel结合T4来生成我们的三层架构中的大部分繁琐的代码。

我的发现如下:

我们有 2 个项目,假设是 A 和 B,其中 (A) 引用了 (B)。在项目 A 中,我们仅使用该项目中的模型类生成模型扩展。这些类使用项目 B 中的多个类。下面是其中 1 个类的示例。

using Common.Library;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace Projecten.Model.DataContracts.Statistiek
{
    [DataObject]
    [CustomResultClass("FactuurStatistiek")]
    public partial class FactuurStatistiek : BaseStatistiek
    {
        public FactuurStatistiek Copy()
        {
            FactuurStatistiek copy = new FactuurStatistiek();

            copy.AddRange(this);

            return copy;
        }
    }
}

我们用 2 个属性修饰了该类,其中 [CustomResultClass] 属性用于代码生成。

问题是,当我们在项目 A 中找到此类的 CodeModel 时,这些属性不可用。我编写了一个搜索例程来搜索该类的整个解决方案。代码如下:

    public CodeType CodeType
    {
        get
        {
            if (m_CodeType == null)
            {
                m_CodeType = GetCodeType();
            }

            return m_CodeType;
        }
    }

    /// <summary>
    /// Get the CodeType for the property
    /// </summary>
    private CodeType GetCodeType()
    {
        string name = Name;
        CodeType codeType = null;

        if (name == "FactuurStatistiek")
        {
            codeType = FindCodeType(CodeProperty.Type.AsFullName);
        }

        if (codeType == null)
        {
            CodeTypeRef codeTypeRef = CodeProperty.Type;

            if (codeTypeRef.CodeType.IsCodeType)
            {
                codeType = codeTypeRef.CodeType;
            }

            if (codeType == null)
            {
                if (string.IsNullOrEmpty(CodeProperty.Type.AsFullName))
                {
                    codeType = CodeModel.CodeTypeFromFullName(CodeProperty.Type.AsString);
                }
                else
                {
                    codeType = CodeModel.CodeTypeFromFullName(CodeProperty.Type.AsFullName);
                }
            }
        }

        return codeType;
    }

    private CodeType FindCodeType(string fullName)
    {
        CodeType codeType = null;

        foreach (Project project in Metadata.Dte.Solution.Projects)
        {
            foreach (ProjectItem projectItem in project.ProjectItems)
            {
                codeType = RecurseProjectItems(projectItem.ProjectItems, fullName);

                if (codeType != null)
                {
                    return codeType;
                }
            }
        }

        return null;
    }

    private CodeType RecurseProjectItems(ProjectItems projectItems, string fullName)
    {
        CodeType codeType = null;

        if (projectItems != null)
        {
            foreach (ProjectItem projectItem in projectItems)
            {
                if (projectItem.FileCodeModel != null)
                {
                    codeType = RecurseCodeElements(projectItem.FileCodeModel.CodeElements, fullName);

                    if (codeType != null)
                    {
                        break;
                    }
                }

                codeType = RecurseProjectItems(projectItem.ProjectItems, fullName);

                if (codeType != null)
                {
                    break;
                }
            }
        }

        return codeType;
    }

    private CodeType RecurseCodeElements(CodeElements codeElements, string fullName)
    {
        CodeType codeType = null;

        if (codeElements != null)
        {
            foreach (CodeElement codeElement in codeElements)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementNamespace)
                {
                    codeType = RecurseCodeElements(((CodeNamespace)codeElement).Members, fullName);
                }
                else if (codeElement.Kind == vsCMElement.vsCMElementClass)
                {
                    string classFullName = ((CodeClass)codeElement).FullName;

                    if (((CodeClass)codeElement).FullName == fullName)
                    {
                        codeType = (CodeType)codeElement;
                    }
                }

                if (codeType != null)
                {
                    break;
                }
            }
        }

        return codeType;
    }

这段代码工作正常,我们确实为该类获得了正确的 CodeModel,但代码非常非常慢。我的猜测是,不仅遍历所有项目和项目项会使其变慢,而且我感觉在解析 CodeModel 期间发现类中的文本已被解析。

我们如何改进搜索算法以使性能变得可以接受?

最佳答案

您是对的,一般情况下缓慢是由于必须解析文件内容以生成 CodeElements 造成的。我在我们的一个项目中解决了这个问题,方法是根据我正在搜索的内容的一些基本启发来限制我处理的文件。

您可以执行一个简单的String.Contains,在解析文件之前查看您关心的内容是否在文件中。这是一个简单的示例,您需要最后执行 FileCodeModel null 检查,因为访问该属性需要进行一些解析。

var fileName = projectItem.FileNames[1];
var shouldProcessFile = File.Exsits(fileName)
    && Path.GetExtension(fileName) == ".cs"
    && File.ReadAllText(fileName).Contains("FactuurStatistiek")
    && projectItem.FileCodeModel != null;
if(shouldProcessFile)
{
    codeType = RecurseCodeElements(projectItem.FileCodeModel.CodeElements, fullName);
}

上述方法是我采用的方法,并且对我们来说效果很好。它仍然需要读取项目中所有“.cs”文件的所有内容并在其内容中查找某些内容,但这比解析所有文件要快。

如果您的项目中碰巧有大量文件,但知道真正需要解析的文件很少,我想到的另一种方法是通过在文件的第一行上添加注释来实际标记文件例如,如果您将要考虑解析的文件的第一行设置为 //PARSEME 那么上面的代码可以修改为。

var fileName = projectItem.FileNames[1];
var shouldProcessFile = File.Exsits(fileName)
    && Path.GetExtension(fileName) == ".cs"
    && File.ReadLines(fileName).First().Contains("//PARSEME")
    && File.ReadAllText(fileName).Contains("FactuurStatistiek")
    && projectItem.FileCodeModel != null;
if(shouldProcessFile)
{
    codeType = RecurseCodeElements(projectItem.FileCodeModel.CodeElements, fullName);
}

添加 File.ReadLines(fileName).First().Contains("//PARSEME") 检查意味着您之前只真正读取大多数文件的第一行你会得到更昂贵的包含检查。当然,这样做的缺点是,如果添加新文件,您需要记住标记可解析的文件。

关于c# - 使用 Visual Studio 的代码模型查找类的更快方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23069263/

相关文章:

c# - LINQ vs Lambda vs 匿名方法 vs 委托(delegate)

c# - 使用手机时需要从网站推荐手机应用商店链接

C# 将所需的 DLL 放在输出根目录以外的位置

c - 如何修复此错误,无法在 C 中写入另一个文件

.net - 简单的 T4 模板在 Visual Studio 2010 中不起作用,但在 MonoDevelop 中起作用吗?

c# - 从数据库中获取值并分配按钮文本

c# - 用于命名 CLS 兼容替代属性的模式

c# - 玩家在 Y 轴上不需要的旋转

.net - T4 文本模板 - 是否可以从主机获取编译符号?

c# - 在 T4 模板中使用 .NET Core 程序集