CodeModel是一个在Visual Studio中发现代码的强大工具。我们将VS2013的CodeModel与T4结合使用,在我们的3层架构中生成大部分繁琐的代码。
我发现以下内容:
我们有2个项目,比方说A和B,其中一个(A)引用(B)。在项目A中,我们仅使用此项目中的模型类生成模型的扩展。这些类在项目B中使用了几个类。以下是其中一个类的示例。
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,但代码非常非常慢。我的猜测是,不仅遍历所有项目和项目项目使其变慢,而且我感觉在CodeModels的解析过程中发现了类中的文本被解析。
我们如何以性能可接受的方式改进搜索算法?
答案 0 :(得分:3)
你是正确的,因为必须解析文件的内容以产生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")
检查,这意味着在进入更昂贵的包含检查之前,您只是从大多数文件中读取第一行。当然,如果你添加新的文件,你需要记住标记可解析的文件。