You could do it with Mono.Cecil.
Here is a simple LINQPad program that demonstrates:
const string AssemblyFilePath = @"pathoassembly.dll";
void Main()
{
var assembly = ModuleDefinition.ReadModule(AssemblyFilePath);
var calls =
(from type in assembly.Types
from caller in type.Methods
where caller != null && caller.Body != null
from instruction in caller.Body.Instructions
where instruction.OpCode == OpCodes.Call
let callee = instruction.Operand as MethodReference
select new { type, caller, callee }).Distinct();
var directRecursiveCalls =
from call in calls
where call.callee == call.caller
select call.caller;
foreach (var method in directRecursiveCalls)
Debug.WriteLine(method.DeclaringType.Namespace + "." + method.DeclaringType.Name + "." + method.Name + " calls itself");
}
This will output the methods that calls themselves directly. Note that I only processed the Call instruction, I'm not certain how to handle the other call instructions correctly here, or even if that is even possible.
Caveat: Note that this will, because I only handled that single instruction, only work with statically compiled calls.
Virtual calls, calls through interfaces, that just happen to go back to the same method, will not be detected by this, a lot more advanced code is necessary for that.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…