【问题标题】:Is it possible to compile C# into IL and decompile the resulting IL into F#?是否可以将 C# 编译为 IL 并将生成的 IL 反编译为 F#?
【发布时间】:2020-02-20 12:59:20
【问题描述】:

C# 编译成 IL,然后很容易使用 ILSpy 或 dotPeek 反编译回 C#。

是否有任何工具可以将相同的 IL 反编译成 F#?

即有没有通过 IL 将 C# 转换为 F# 的偷偷摸摸的方法?

理由:

  • 我们有一个非常大的 C# 项目,想要“发挥作用”。
  • 翻译是一项艰巨的工作。
  • 如果第一次翻译可以自动化,我们可以在之后手动改进它

即这会比在 F# 中从头开始编写所有内容更快吗?

我们还没有 F# 的经验,因此这个问题可能很幼稚。

进一步说明 是的,我们在 C# 中使用函数式的东西,但出于各种原因,我们希望在 F# 中实现完整的函数式。这个选择在这里没有争议,但感谢 cmets 对此的支持。

【问题讨论】:

  • 请记住,C# 和 F# 通常使用不同的数据类型(即使对于像列表这样的东西)。即使可以将 C# 转换为 IL 到有效的 F#(它可能),它也不会是 惯用的 F#,它看起来像你手写的东西
  • 为什么? .NET 的一大亮点是 .NET 语言的互操作性。您可以从 F# 调用 C# 代码,反之亦然。将 C# 代码转换为极差的 F# 代码有什么意义?
  • 互操作性并不意味着能够像那样交叉编译。您可以 CALL - 但 F# 不同的东西并为此提供支持库。最重要的是,大多数反编译器都是硬编码的,并且可能会遇到问题,因为反编译器的编写者使用了 C# 生成的源代码。而且你会缺少像 cmets 这样的东西。
  • 如果有使用自动翻译的有效方法,我相信我现在已经找到了。这里描述了好的策略:fsharpforfunandprofit.com/posts/porting-to-csharp-intro

标签: c# .net f# decompiling


【解决方案1】:

惯用的 F# 代码使用不同的类型、方法和结构。将 C# 生成的 IL “反编译”为 F# 并没有什么好处,尤其是当您认为可以很容易地与 C# 代码交互时。更糟糕的是,反编译任何东西都会让你丢失很多信息——本地人的名字、常量、cmets 等等。这不是不可能的,但无论如何你都在浪费大量的精力来重建一个中间步骤。

将您的 C# 代码分解成可以慢慢替换为 F# 代码的部分。这样,您就有机会生成良好的 F# 代码,并根据需要逐步替换所有内容。不要从头开始编写任何东西 - 尽可能多地保留旧代码,只更改需要更改的内容以改进与 F# 代码的接口。

C# 已经是一种“功能强大”的语言 - 不像 F# 那样简洁和富有表现力,但是您可以在 F# 中做的事情很少,但在 C# 中却不能以相当简单的方式进行。这意味着逐渐转变为功能风格并不像听起来那么困难——而且这是你无论如何都无法避免的工作,如果你的目标是(出于某种原因;我希望你也有一个实际的目标,这是只是一个中间步骤 :) 功能代码。

【讨论】:

    【解决方案2】:

    正如这里所反映的,当您编译为 IL 时,您会丢失宝贵的信息。阅读 C# 代码来翻译所有高级信息更为可行。

    刚刚拼凑了一个SyntaxWalker

      public class FsVisitor : CSharpSyntaxWalker
            {
                public override void VisitUsingDirective(UsingDirectiveSyntax node)
                {
                    PrintLn("open {0}", node.Name);
                }
    
                public override void VisitClassDeclaration(ClassDeclarationSyntax node)
                {
                    PrintLn("type {0} =", node.Identifier);
                    Enter();
                    base.VisitClassDeclaration(node);
                    Exit();
                }
    
                public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
                {
                    var isStatic = node.Modifiers.Any(t => t.ValueText == "static");
                    var modifiers = String.Join(" ", node.Modifiers);
    
                    PrintLn("{0}{1} ({2}) = ",
                        isStatic ? $"{modifiers} member " : $"member {modifiers} this.",
                        node.Identifier,
                        String.Join(", ", node.ParameterList.Parameters.Select(p => $"{p.Identifier} : {p.Type}")));
                    Enter();
                    base.VisitMethodDeclaration(node);
                    Exit();
                }
    
                public override void VisitInvocationExpression(InvocationExpressionSyntax node)
                {
                    PrintLn("{0}", node);
                    base.VisitInvocationExpression(node);
                }
    
                private StringBuilder builder = new StringBuilder();
                private int intendLevel = 0;
    
                void Enter() => intendLevel++;
                void Exit() => intendLevel--;
    
                void Print(string format, params object[] args)
                {
                    if (format == null) return;
                    builder.Append('\t', intendLevel);
                    builder.AppendFormat(format, args);
                }
    
                void PrintLn(string format = default, params object[] args)
                {
                    Print(format, args);
                    builder.AppendLine();
                }
    
                public override string ToString() => builder.ToString();
            }
    

    我用一个简单的 C# 程序试了一下:

            var code =
            @"    
            using System;
    
            class Program
            {
                static void Main(string[] args)
                {
                    Console.WriteLine(""Hello World"");
                    Console.ReadKey();
                }
    
                private void SayHello()
                {
                    Console.WriteLine(""Hello"");
                }
            }";
    
        var visitor = new FsVisitor();
        visitor.Visit(CSharpSyntaxTree.ParseText(code).GetCompilationUnitRoot());
        Console.WriteLine(visitor.ToString());
    

    输出:

    open System
    type Program =
            static member Main (args : string[]) =
                    Console.WriteLine("Hello World")
                    Console.ReadKey()
            member private this.SayHello () =
                    Console.WriteLine("Hello")
    

    当然,输出是一个简单的程序。 支持合理的转换需要更多的工作,但也许转换不一定是完美的。也许如果它为您提供了脚手架,您可能可以用惯用的 F# 自己填充它。

    在自动化方面一如既往:

    【讨论】:

    • 很棒的答案。谢谢!
    【解决方案3】:

    我只是一个业余爱好者,所以我不保证以下内容完全准确...首先,我将假设您当前的 C# 基础是用惯用的 C# 编写的,因此您当前的 c# 项目遵循面向对象范例。如果这是准确的,并且您正在寻求“功能齐全”,那么您唯一真正的选择是按照功能范式重新编写您的项目。如果存在这样的 IL 转换器,那么转换后的将以面向对象的方式将您的 C# 编译为 F#(F# 是一种功能优先语言,但也可以用作面向对象的语言)。此外,正如其他人所指出的,您可以从 F# 调用您的 C# 代码,但这不会将您的 C# 项目转换为以功能方式运行。您仍然必须按照设计使用的方式使用您的 C# 项目 - 通过 F# 以面向对象的方式。您可能可以在 F# 中围绕您的 C# 程序编写某种形式的包装器,以使其以功能方式运行,但如果您的 C# 项目基于对象层次结构,那么这可能不是太琐碎,您可能会发现您没有t 最终得到一个功能齐全的包装器。

    【讨论】:

      猜你喜欢
      • 2010-09-13
      • 2015-11-05
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      相关资源
      最近更新 更多