51

I'm playing around with the C# reflection API. I can easily load Type information of classes, methods etc. in an assembly, however, now I wonder how can I load and read the code inside a method?

Sam
  • 7,252
  • 16
  • 46
  • 65
koraytaylan
  • 873
  • 2
  • 8
  • 15
  • What is the scenario that makes this useful? It breaks one of the fundamental tenants of OO which is encapsulation. But furthermore the code as IL is not really the same. – DevelopingChris Apr 22 '10 at 19:44
  • 9
    @DevelopingChris: If you're writing a debugger or code analysis tool, it may be useful to be able to load an assembly and analyze the body of a method. This is, in fact, what tools like FxCop do. – LBushkin Apr 22 '10 at 20:01
  • The target audience of .NET is US corporations (and secondarily perhaps some small businesses). If you could bypass .NET obfuscation, the (expensive to produce) software could easily fly into a competitor's fingertips. – micahhoover Sep 04 '14 at 17:29
  • 1
    @micahhoover Every statement in your comment is provably false both at the time of writing and now. – Hnus Aug 06 '22 at 09:38

7 Answers7

40

Basic Answer:

You can't with the reflection API (System.Reflection).

The reason is that the reflection api is designed to work on Metadata (Type of Classes, Name and Signature of Methods, ...) but not on the data level (which would be the IL-stream itself).

Extended Answer:

You can emit (but not read) IL with System.Reflection.Emit (e.g. ILGenerator Class).

Through MethodInfo.GetMethodBody() you can get the binary IL-stream for the implementation of a method. But thats usually completely useless by itself.

There are external libraries (like Cecil) that you can use to read/modify/add/delete code inside a method.

Foxfire
  • 5,675
  • 21
  • 29
  • 1
    Can't you refer to the method via a delegate, then create an ExpressionTree from the delegate and inspect the tree? – Matt Greer Apr 22 '10 at 19:49
  • 1
    @Matt Greer: No. You cannot "reverse engineer" a delegate's method body into an expression tree. – LBushkin Apr 22 '10 at 19:59
  • @Matt: That's not going to give you the code *inside* the method, you'll just get a `MethodCallExpression` that gives you access to the same `MethodInfo` you could have gotten more directly with the reflection API. – Aaronaught Apr 22 '10 at 20:00
  • No unless it could parse a binary IL stream which it cannot. – Foxfire Apr 22 '10 at 20:00
  • @Foxfire: Hi. I don't know if you'll see this, but if you do... Could you quote your sources when you say that the reflection API is designed to work on Metadata, not on the data level? I believe you ;) but I'd like to include this in my master thesis and I need some kind of reference for it. Thanks so much :) – Alix Jun 17 '10 at 12:34
  • No, I don't have any scientific-citeable sources for this ready. But it is A) obvious B) you can easily find MS employees blog posts on this. – Foxfire Jun 20 '10 at 22:59
  • @MattGreer There is a package for that https://nuget.org/packages/DelegateDecompiler/ – Ark-kun Feb 10 '13 at 19:27
  • `MethodInfo.GetMethodBody()` is not available on .NET Core, so to use IL-stream you can go with `MethodInfo.GetMetadataToken()` and `PEReader` (see full example here: https://github.com/dotnet/corefx/issues/4543#issuecomment-222689903) – Konard May 31 '16 at 14:14
22

That depends on what you mean by "read the code." There are 4 forms of the code.

Code Type Can get with Reflection
The source code, i.e. the original C# or VB.NET No
The symbolic IL code No
The JITed assembly code No
The IL bytes, i.e. the actual bytes that IL is compiled to Yes

Take a look at MethodBase.GetMethodBody() for the last one. You can get the IL bytes, the local variables, exception frames etc.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
9

You sort of can. The relevant function is MethodBase.GetMethodBody.

It's not exactly the most useful API. You can get some basic information about what's inside the method, and you can obtain the IL as a byte array. That's about it.

There's a slightly better API in the Mono.Cecil library, which exposes a MethodDefinition class with its own MethodBody implementation which contains actual Instructions, so you don't have to interpret the raw byte code. Still, if you're looking to get C# code out of it à la Reflector, you're going to be sorely disappointed. Also, Cecil isn't very well documented.

If you still want to try, then good luck.

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
2

I'd like to provide an example of how one could explore the code inside a method. As others have explained, this can't be done easily using the native .NET Reflection API. However, using the Mono.Reflection API, you can disassemble the code programmatically using the GetInstructions() method and inspect it at runtime.

For example, the following code inspects a method and computes the number of calls inside it. As a use case for such a code, say I am a teacher (which I am) and instruct my fellow students to program a given method without using any other method, then using this code in unit tests I can verify that the given constrain is respected.

public static class MethodInfoUtil
{
    public static int NbOfInnerCalls(this MethodInfo mi)
    {
        return mi.GetInstructions().Count(
            instruction => instruction.OpCode.FlowControl == FlowControl.Call);
    }
}

Example Console program:

class Program
{
    static int Add(int a, int b) => a + b;
    static int Doubling(int a) => Add(a, a);
    static int Quadrupling(int a) => Add(Add(a, a), Add(a, a));

    static void Main(string[] args)
    {
        Console.WriteLine("Inner method calls");
        Console.WriteLine("        Add: {0}", ((Func<int, int, int>)Add).Method.NbOfInnerCalls());
        Console.WriteLine("   Doubling: {0}", ((Func<int, int>)Doubling).Method.NbOfInnerCalls());
        Console.WriteLine("Quadrupling: {0}", ((Func<int, int>)Quadrupling).Method.NbOfInnerCalls());
    }
}

// Output:
// Inner method calls
//         Add: 0
//    Doubling: 1
// Quadrupling: 3
Frederic
  • 1,580
  • 15
  • 15
1

No
This is a feature slated for the next version of C#. You can use the CodeDom to get more info than reflection, but you cannot interrogate the parse tree yet.

Well there is always mono, in mono the compiler is a service, and you could get the parse trees at runtime.

The better question is why you want to?

DevelopingChris
  • 39,797
  • 30
  • 87
  • 118
1

Yes, there must be a way to achieve this: The .NET Reflector tool does this, too. Can't tell you how it's done there, though.

Sebastian P.R. Gingter
  • 5,955
  • 3
  • 31
  • 73
  • 1
    Why don't you reflect the reflector ;) – ntziolis Apr 22 '10 at 20:00
  • 11
    Reflector does not use reflection at least not in the .NET framework sense of reflection. It actually parses the PE file and dissembles the IL code from the .TEXT section. Then does some very smart and cool pattern matching to map/decompile the IL back to a target language. – Chris Taylor Apr 22 '10 at 20:00
1

If you don't need to do this real-time, have a look at Reflector. You can disassemble any .NET assembly (including the MS core DLLs) and see the code in your language of choice. This can be very educational.

Update Has anyone tried using Reflector on Reflector to figure out how this is done?

3Dave
  • 28,657
  • 18
  • 88
  • 151
  • 1
    Note, this doesn't de-optimize the code. So if there was a compiler optimization done to the IL, it doesn't get returned to its original build source, but to a slightly less human written optimized version. Its a neat way to see what you could be doing most efficiently. – DevelopingChris Apr 22 '10 at 19:55
  • You can't use Reflector on Reflector. Try it and see, you'll get a nice surprise. – Aaronaught Apr 22 '10 at 20:04
  • 1
    Reflector is encrypted such that it can't reflect on itself, I think they used DotFuscator to do it. – DevelopingChris Apr 22 '10 at 20:07
  • I have tons of CIL code that crashes Reflector even without any obfuscation at all. It isn't really possible to convert something to C# that could never have been written in that language in the first place. – hoodaticus Feb 06 '17 at 23:19
  • 1
    @hoodaticus While I respect your answer, it's always possible to convert if your target language is Turing-complete. Now, whether or not it's easy, fast or a good idea in general is an entirely different discussion. :) – 3Dave Feb 07 '17 at 01:22
  • @DavidLively - how do you represent the calli instruction - calling native function pointer as a method - in C#? – hoodaticus Feb 07 '17 at 14:13