Kra*_*ven 5 c# vb.net envdte mono.cecil visual-studio-2012
我已经尝试了jbEvain强大的Mono.Cecil库大约两周了.我创建了以下功能:
/// <summary>
/// Returns true only if they match.
/// </summary>
private bool CompareMethodDefinitionWithCodeFunction(
EnvDTE.CodeFunction pCodeFunction,
Mono.Cecil.MethodDefinition pMethodDefintion)
{
return pMethodDefintion.Name.Equals(pCodeFunction.Name)
&& pMethodDefintion.Parameters.Count == pCodeFunction.Parameters.Count;
}
Run Code Online (Sandbox Code Playgroud)
目标是确定是否pCodeFunction和pMethodDefinition正在引用相同的函数定义.到目前为止,我能够比较函数的名称和它们具有的参数数量.我很清楚,仅仅证明他们确实在引用相同的功能是不够的.我需要帮助改进我的比较.例如,我认为应该总是比较参数类型以便考虑潜在的功能覆盖.
我试过比较参数类型,但我的尝试都没有占上风.请允许我演示.
我以为我可以将类型比较为字符串,所以我添加了代码的abit,如下所示:
/// <summary>
/// Returns true only if they match.
/// </summary>
private bool CompareMethodDefinitionWithCodeFunction(
EnvDTE.CodeFunction pCodeFunction,
Mono.Cecil.MethodDefinition pMethodDefintion)
{
foreach (ParameterDefinition paramDef in pMethodDefintion.Parameters)
{
Debug.WriteLine(paramDef.ParameterType.FullName);
}
foreach (CodeElement ce in pCodeFunction.Parameters)
{
CodeParameter codeParameter = ce as CodeParameter;
Debug.WriteLine(codeParameter.Type.AsFullName);
}
return pMethodDefintion.Name.Equals(pCodeFunction.Name)
&& pMethodDefintion.Parameters.Count == pCodeFunction.Parameters.Count;
}
Run Code Online (Sandbox Code Playgroud)
鉴于这pCodeFunction是在运行时引用以下VB.Net函数
Public Function SomeFunction(ByVal arg As List(Of String)) As Object
Return New Object()
End Function
Run Code Online (Sandbox Code Playgroud)
我得到了以下输出
System.Collections.Generic.List`1<System.String>
System.Collections.Generic.List(Of System.String)
Run Code Online (Sandbox Code Playgroud)
我宁愿不要乱用这两个输出值并尝试解析它们以使它们匹配,因为这似乎不是比较类型的非常"可靠"的方法.比较参数类型最可靠的方法是什么?
只要源代码是VB或C#,此函数必须能够查找函数的定义.
我目前正在使用最新的Mono.Cecil版本(3.12.1),您可以在这里下载
如果要使用我的函数并将其插入到您创建的测试类中,则需要以下导入:
using EnvDTE;
using Mono.Cecil;
Run Code Online (Sandbox Code Playgroud)
我相信,在多次尝试对它们进行适当比较之后,没有任何“正确”的方法可以用来比较这两种类型的对象。
但是,我找到了一种不同的解决方案,该解决方案意味着计算相对于类中定义的每个其他函数的函数索引。当我们开始考虑 IL 代码中定义的构造函数时,它可能会变得有点复杂,但我仍然认为在这里发布这个答案是合适的,因为它是我迄今为止的最终解决方案。坦率地说,整个解决方案非常简单。
请允许我出于演示目的布置一个简单的类:
class Class1
{
public static void Function1(string arg1, string arg2)
{
//business logic
}
public static Object Function2(Object arg1)
{
//business logic
}
public static void Function2(List<string> arg1)
{
//business logic
}
}
Run Code Online (Sandbox Code Playgroud)
我的功能索引应该是多少?使用我的 Class1 示例,简单地说,该函数的相应索引为:
当然,所说的函数索引并不是EnvDTE自带的一些属性。我得自己计算一下。EnvDTE.CodeFunction为了实现它,我创建了一个包含属性和int属性(用于函数索引)的类。
public class CodeFunctionWithIndex
{
public CodeFunction CodeFunction { get; set; }
public int Index { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
至于我们的Mono.Cecil.MethodDefinition函数索引,由于我们正在循环它们(请参阅主帖),因此我们可以轻松计算它们的索引。
但事情并没有就此结束!如果您想使用相同的方法,我需要提及一些事情。
根据我对 Mono.Cecil 方便的库背后发生的事情的有限理解,我们循环的列表MethodDefinition包含编译 dll 后在 IL 代码中生成的所有函数。但是,我们所居住的类EnvDTE.CodeFunctions并未编译。
Mono.Cecil.Type一样多的函数?EnvDTE.ProjectItem不!这是我们必须考虑的:构造函数。类可能有也可能没有显式定义的构造函数。但是,a Mono.Cecil.Type(也称为 的类对象Mono.Cecil)必须至少包含一个构造函数。相信我,如果您没有显式定义自己的构造函数,那么Mono.Cecil.Type!
查明构造函数是否在我们的EnvDTE.ProjectItem(指类)中显式定义并不是一项艰巨的任务。好吧...除非你认为下面的代码很复杂。
private List<CodeFunctionWithIndex> GetExplicitlyDefinedConstructors(vsCMElement pRequestedCodeElementKind, CodeElements pCodeElements)
{
int nbCodeFunction = 0; //calculated function index
List<CodeFunctionWithIndex> constructorList = new List<CodeFunctionWithIndex>();
if (pCodeElements != null)
{
foreach (CodeElement element in pCodeElements)
{
//if current element is a namespace
if (element.Kind == vsCMElement.vsCMElementNamespace)
{
constructorList = GetExplicitlyDefinedConstructors(pRequestedCodeElementKind, ((EnvDTE.CodeNamespace)element).Members);
if (!constructorList.Any())
continue;
return constructorList;
}
//if current element is a class
else if (element.Kind == vsCMElement.vsCMElementClass)
{
nbCodeFunction = 0;
constructorList = GetExplicitlyDefinedConstructors(pRequestedCodeElementKind, ((EnvDTE.CodeClass)element).Members);
if (!constructorList.Any()) //because there might be more than one class defined within the active file
continue;
return constructorList;
}
//if current element's kind equals the requested kind
else if (element.Kind == pRequestedCodeElementKind)
{
nbCodeFunction++;
//if it's a constructor, add its index to the list of constructor indexes
if (((CodeFunction)element).FunctionKind.ToString().Contains(vsCMFunction.vsCMFunctionConstructor.ToString()))
{
constructorList.Add(
new CodeFunctionWithIndex()
{
CodeFunction = ((CodeFunction)element),
Index = nbCodeFunction
});
}
}
}
}
return constructorList;
}
Run Code Online (Sandbox Code Playgroud)
以下是我如何调用此函数来查明是否有任何显式定义的构造函数:
GetExplicitlyDefinedConstructors(
vsCMElement.vsCMElementFunction,
DTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElements)
.Any();
Run Code Online (Sandbox Code Playgroud)
但是,如果其中没有定义任何构造函数,我们的Mono.Cecil.MethodDefinition函数索引如何与我们的EnvDTE.CodeFunction函数索引匹配呢?
这是我的解决方案的主要思想(经过测试):
在VB.Net中,如果类中没有显式定义的构造函数,则IL代码中的构造函数将位于类的开头(函数索引0)。
在 C#.Net 中,如果类中没有显式定义的构造函数,则 IL 代码中的构造函数将位于类的末尾(最后一个函数索引)。
这是我CompareMethodDefinitionWithCodeFunction在第一篇文章中提出的函数今天的样子(是的,它已被重命名......对此我表示歉意):
public MethodDefinition FindMethodDefinition(CodeFunctionWithIndex pCodeFunction, bool pHasAnExplicitlyDefinedCtor)
{
//Get the assembly that should contain the function we seek
//Note : this is done by comparing pCodeFunction's assembly name to every assembly's name (without the extension)
ModuleDefinition assemblyContainingMethod = assemblies
.Where(assem =>
assem.Name.Split(new char[] { '.' }).FirstOrDefault()
.Equals(pCodeFunction.CodeFunction.ProjectItem.ContainingProject.Properties.Item("AssemblyName").Value, StringComparison.CurrentCultureIgnoreCase))
.FirstOrDefault();
//Get the class that should contain the function we seek
//Note : pCodeFunction.Parent.Name is the class name of our pCodeFunction
TypeDefinition classContainingMethod =
assemblyContainingMethod.Types
.Where(cl => cl.Name.Equals(((CodeClass)pCodeFunction.CodeFunction.Parent).Name))
.FirstOrDefault();
//below is what you want to see
bool isCtorAtIndexZero = DTE.ActiveDocument.ProjectItem.Name.EndsWith(".vb");
int functionIndex = 0;
for (int i = 0; i < classContainingMethod.Methods.Count; i++)
{
if (!pHasAnExplicitlyDefinedCtor && isCtorAtIndexZero && i == 0)
continue;
if (functionIndex == pCodeFunction.Index)
return classContainingMethod.Methods[i];
functionIndex++;
}
return null;
}
Run Code Online (Sandbox Code Playgroud)
该代码是从一个工作项目中提取的。
该assemblies变量是类型的类属性List<ModuleDefinition>。当这个函数被调用时,它将包含我们要查找的函数所在的程序集。
请耐心等待,因为我只能澄清这么多。无论如何,在我看来,这个项目相当大,它可以执行许多我需要从这篇文章中省略的操作,因为它首先与问题没有直接关系。
希望这至少有一点帮助。我为文字墙道歉。