6

Using the Roslyn API with Visual Studio 2015, can I convert an object instance to source code? Can I create an extension method like ".ToSourceCode()" as shown below?

class Foo { }
class Program
{
    static string classSourceCode = "class Foo { }";
    static void Main()
    {
        var instance = new Foo();
        var instanceSourceCode = instance.GetType().ToSourceCode();
        System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
    }
}
Robin
  • 2,278
  • 3
  • 26
  • 46
  • Didn't took a look to Rslyn yet, but if that's possible then hacking ANY .net program would be just a matter of 5 minutes... – Gusman Apr 10 '15 at 17:39
  • Yes, you certainly can write such a method. It is not built into Roslyn and there is little in Roslyn to help you with this. – usr Apr 10 '15 at 17:39
  • 1
    yes or no questions make poor stack overflow questions. What research have you done about Roslyn's capabilities? What have you tried that failed and what errors are you getting that are different than your expected behavior. – Steve Mitcham Apr 10 '15 at 17:39
  • @Gusman decompilers already exist without Roslyn – Steve Mitcham Apr 10 '15 at 17:40
  • Yes, I know, but it's a bit harder to use than just doing "ToSourceCode()" XD – Gusman Apr 10 '15 at 17:41
  • 1
    Using "ToSourceCode" isn't the hard part, the hard part is WRITING "ToSourceCode". – Bradley Uffner Apr 10 '15 at 17:45
  • Please explain the down vote so I can improve my question. – Robin Apr 10 '15 at 17:50
  • @Steve are there decompilers that you can recommend to help me write the extension method I illustrated in the example? – Robin Apr 10 '15 at 17:51
  • @Bradley are there any suggestions you can give to help me write the "ToSourceCode" extension method? – Robin Apr 10 '15 at 17:51
  • Decompilers are not trivial things, you aren't going to be able to use one to write one. Roslyn makes it easier to do something like this, but a complete answer is way off-topic for here and the source of lucrative commericial tool profit. – Steve Mitcham Apr 10 '15 at 17:54
  • 1
    @Robin the question is borderline on topic here. Yes/no is not the kind of question that is allowed here. So the question should be "How to write a ToSourceCode method?". But that is too broad because it kind of requires a tutorial or CodeProject article. – usr Apr 10 '15 at 17:57
  • https://github.com/icsharpcode/ILSpy – Bradley Uffner Apr 10 '15 at 17:57
  • For any given `MethodInfo`, you can call [`MethodBody`](https://msdn.microsoft.com/en-us/library/system.reflection.methodbody%28v=vs.110%29.aspx) which "*Provides access to the metadata and MSIL for the body of a method.*". Interpreting the metadata and msil is an exercise left for the reader. The rest you can do with reflection. – Wai Ha Lee Apr 10 '15 at 18:01
  • @Bradley does ILSpy have a public API that I can use from a C# program or is it just a GUI? – Robin Apr 10 '15 at 18:04
  • @Wai Ha Lee thank you for the constructive comment – Robin Apr 10 '15 at 18:05
  • ILSpy is a front end for Cecil, which is what actually does the decompiling. The soruce code for Both ILSpy and Cecil is included in that GitHub Repository. It's all open source so even if the API were private you could take the source and build it in to your application anyway. – Bradley Uffner Apr 10 '15 at 18:06
  • @Robin - i don't think you can ever get the exact code as source file, give the way compiler compiles lambda, Task and anon object! – Parimal Raj Apr 10 '15 at 18:39

1 Answers1

13

No. However, ILSpy can.

Based on the comments on the question and what I understand about Roslyn, decompilation is not supported. However, thanks to @Bradley's ILSpy tip, there is a solution:

  1. Download the ILSpy binaries from http://ilspy.net/
  2. Reference the following assemblies: ICSharpCode.Decompiler.dll, ILSpy.exe, Mono.Cecil.dll, ILSpy.BamlDecompiler.Plugin.dll
  3. Implement the ".ToSourceCode()" extension method as shown below:
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy;
using Mono.Cecil;
class Foo { }
class Program
{
    static string classSourceCode = "using System; internal class Foo { } ";
    static void Main()
    {
        var instance = new Foo();
        var instanceSourceCode = instance.GetType().ToSourceCode();
        System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
    }
}

static class TypeExtensions
{
    public static string ToSourceCode(this Type source)
    {
        var assembly = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
        var type = assembly.MainModule.Types.FirstOrDefault(t => t.FullName == source.FullName);
        if (type == null) return string.Empty;
        var plainTextOutput = new PlainTextOutput();
        var decompiler = new CSharpLanguage();
        decompiler.DecompileType(type, plainTextOutput, new DecompilationOptions());
        return Regex.Replace(Regex.Replace(plainTextOutput.ToString(), @"\n|\r", " "), @"\s+", " ");
    }
}
Robin
  • 2,278
  • 3
  • 26
  • 46
  • 3
    Robin is absolutely correct. ILSpy is a better example of an analyzer for already-compiled code, which is now in IL form. If you use his/her example, then keep in mind that in order to recompile you'll need to also decompile or reference all of that object's types that it references - both in internal implementation or part of its signatures (e.g. supertypes, return types, parameter types). Roslyn is intended for analysis of C# and VB source code, though has facilities for understanding signatures from referenced IL assemblies, but not the method implementations. – Theo Yaung Apr 11 '15 at 18:29
  • 1
    Great answer! I would define assembly like this `var assembly = AssemblyDefinition.ReadAssembly(Assembly.GetAssembly(source).Location); ` in order to make it work even when the type is defined in somewhere else. – Jack Jun 23 '19 at 02:51
  • hey @Robin do you find how to decompile an actual object instance to C# source code, and not a type definition? Give `class Foo { public baa { get; set; } public int n { get; set; } }` then `var f = new Foo { baa = "str", n = 10 };` then a call to imaginary-decompile function `decompile(f)` would emit the very same C# source code: `var f = new Foo { baa = "str", n = 10 };`. The `CSharpLanguage` seems to decompile types only. – Jack Jun 24 '19 at 19:23