There should be a tool that already does this in a tree format, but none of the usual suspects seem to have it without the Find Usages dance.
So, here's an attempt using the Reflection APIs by finding all the member variables, then going through the methods and figuring out whether they touch them.
You're basically looking at IL at this point, so unless you want to figure the parsing rules yourself you'll want to use a disassembler like Cecil. I'm using this deprecated single file implementation cause it's easy. Something like a Roslyn analyzer or JustDecompile plugin would be another route, but I have no experience there.
There's bound to be edge cases, and if you're looking for an entire dependency graph the code will be a lot more complex - but for quick and dirty analysis, it should at least get you an overview.
So, basically you use Reflection plus an IL reader to make a map from variable -> methods (you could go the other way too, but that's probably less valuable):
var variables = typeof(SmallBallOfMud).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var methods = typeof(SmallBallOfMud).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
var d = new Dictionary<string, List<string>>();
foreach (var v in variables) d.Add(v.Name, new List<string>());
// this particular disassembler chokes on externally implemented methods
foreach (var m in methods.Where(m => (m.MethodImplementationFlags | MethodImplAttributes.IL) == 0)) {
var instructions = MethodBodyReader.GetInstructions(m);
foreach (var i in instructions) {
// we'll only check for direct field access here
var f = i.Operand as FieldInfo;
if (f == null) continue;
d[f.Name].Add(m.Name);
}
}
Our result will be like:
state1: Method1 (1), Method2 (1)
state2: Method2 (2)
state3: Method1 (1), Method2 (2)
which is read as "state3" is used by "Method1" once, and "Method2" twice.