确定Mono.Cecil.MethodDefinition是否引用与给定EnvDTE.CodeFunction相同的函数

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)

目标

目标是确定是否pCodeFunctionpMethodDefinition正在引用相同的函数定义.到目前为止,我能够比较函数的名称和它们具有的参数数量.我很清楚,仅仅证明他们确实在引用相同的功能是不够的.我需要帮助改进我的比较.例如,我认为应该总是比较参数类型以便考虑潜在的功能覆盖.

以前的尝试

我试过比较参数类型,但我的尝试都没有占上风.请允许我演示.

我以为我可以将类型比较为字符串,所以我添加了代码的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)

Kra*_*ven 0

我相信,在多次尝试对它们进行适当比较之后,没有任何“正确”的方法可以用来比较这两种类型的对象。

但是,我找到了一种不同的解决方案,该解决方案意味着计算相对于类中定义的每个其他函数的函数索引。当我们开始考虑 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 示例,简单地说,该函数的相应索引为:

  • 0
  • 1
  • 2

当然,所说的函数索引并不是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并未编译。

a(又名类)是否包含与 a (指类)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函数索引匹配呢?

这是我的解决方案的主要思想(经过测试):

  1. 在VB.Net中,如果类中没有显式定义的构造函数,则IL代码中的构造函数将位于类的开头(函数索引0)。

  2. 在 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>。当这个函数被调用时,它将包含我们要查找的函数所在的程序集。

请耐心等待,因为我只能澄清这么多。无论如何,在我看来,这个项目相当大,它可以执行许多我需要从这篇文章中省略的操作,因为它首先与问题没有直接关系。

希望这至少有一点帮助。我为文字墙道歉。