I believe that after several additional attempts to match them appropriately, there are no “right” methods available for comparing these two types of objects.
But I found another solution that involves calculating the index of a function relative to any other function defined inside the class. This can be a bit more complicated when we start accepting the constructors defined in the IL code, but I still find it appropriate to post this answer here, as that was my final decision. And, to be honest, the solution as a whole is quite simple.
Let me lay out a simple class for demo purposes:
class Class1 { public static void Function1(string arg1, string arg2) {
Function index
What should be my function index? Using my Class1 example, just add the appropriate function indexes:
Of course, the specified function index is not some property that comes with EnvDTE. I have to figure it out myself. To implement it, I created a class that contains the EnvDTE.CodeFunction property, as well as the int property (intended for the index of the function).
public class CodeFunctionWithIndex { public CodeFunction CodeFunction { get; set; } public int Index { get; set; } }
As for our Mono.Cecil.MethodDefinition function Mono.Cecil.MethodDefinition , since we are fixated on them (see the main post), we can easily calculate their indexes.
This is not just the end! There are several things that I should mention if you want to use the same approach.
To my limited understanding of what happens behind the convenient Mono.Cecil library, the MethodDefinition list that we MethodDefinition over contains all the functions that were generated in the IL code after our dll was compiled. But the class from which our EnvDTE.CodeFunctions does not compile.
Does the Mono.Cecil.Type class (AKA class) have as many functions as EnvDTE.ProjectItem (referring to the class)?
Not! Here's what we will need to consider: constructor (s). A class may or may not have explicitly defined continua. But (AKA - an object of the Mono.Cecil class) must contain at least one constructor . And believe me, if you do not explicitly define your own constructor, then in Mono.Cecil.Type !
Finding if the constructor is explicitly defined in our EnvDTE.ProjectItem (referring to the class) is not such a difficult task. Well ... if you don't think the following code is complicated.
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 kind equals the requested kind else if (element.Kind == pRequestedCodeElementKind) { nbCodeFunction++; //if it 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; }
And this is how I call this function to find out if I have any explicitly defined constructors:
GetExplicitlyDefinedConstructors( vsCMElement.vsCMElementFunction, DTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElements) .Any();
But, if it doesn't have any constructors, how Mono.Cecil.MethodDefinition our Mono.Cecil.MethodDefinition index match our EnvDTE.CodeFunction ? Function index.
Here is the big idea of my solution (tested):
In VB.Net, if there is no explicitly defined constructor in the class, the constructor in the IL code will be located at the beginning of the class (function index 0).
In C # .Net, if there is no explicitly defined constructor in the class, the constructor in the IL code will be located at the end of the class (the last function index).
Here is what my CompareMethodDefinitionWithCodeFunction function CompareMethodDefinitionWithCodeFunction in my first post today (yes, it was renamed ... I apologize for this):
public MethodDefinition FindMethodDefinition(CodeFunctionWithIndex pCodeFunction, bool pHasAnExplicitlyDefinedCtor) { //Get the assembly that should contain the function we seek //Note : this is done by comparing pCodeFunction assembly name to every assembly 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; }
This code is extracted from the working draft.
The assemblies variable is a property of a class of type List<ModuleDefinition> . By the time this function is called, it will contain an assembly in which you can find the desired function.
Bear with me as I can only make this clear. The project is quite large, in my opinion, in any case, and it can perform many operations that I need to omit from this position, since this is not directly related to the issue.
Hope this helps at least a little. I apologize for the text wall.