ソースコードを C# から F# に変換


F# プロジェクト内では C# のコードが利用できないので C# から F# に変換できたら良いですね。 F# PowerPack には CodeDOM の実装があるのでこれを使えば良さそうです。

CodeDOM を使って C# のコードを F# に変換するためにはまず C# のコードを CodeCompileUnit に落とし込む必要があります。 CodeCompileUnit 形式のオブジェクトは次のように F# のコードに変換できます。

let fsharpCode compileUnit =
   use provider = new FSharpCodeProvider ()
   let options = CodeGeneratorOptions (IndentString = "   ")
   let builder = StringBuilder ()
   use writer = new StringWriter (builder)
   provider.GenerateCodeFromCompileUnit (compileUnit, writer, options)
   builder.ToString ()

インデントが 3 スペースなのは完全に趣味です。

C# のコードを CodeCompileUnit にするには CSharpCodeProvider.Parse メソッドを使えば良いように見えますが, NotImplementedException が投げられます。そこで NRefactory を用います。

let compileUnit csharpSourcePath =
   let compilation =
      let projectContent = CSharpProjectContent ()
      SimpleCompilation (projectContent)
   let syntaxTree =
      let parser = CSharpParser ()
      use reader = new StreamReader (csharpSourcePath)
      parser.Parse (reader)
   let visitor = CodeDomConvertVisitor ()
   visitor.Convert (compilation, syntaxTree, null)

例えば次の C# コードを変換します。

using System;

class Program
{
   static void Main()
   {
      Console.WriteLine("Hello, World!");
   }
}

結果は次のようになります。


//------------------------------------------------------------------------------
// <autogenerated>
//     This code was generated by a tool.
//     Runtime Version: 4.0.30319.17929
//
//     Changes to this file may cause incorrect behavior and will be lost if 
//     the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------

namespace global

namespace global
    // Generated by F# CodeDom
    #nowarn "49" // uppercase argument names
    #nowarn "67" // this type test or downcast will always hold
    #nowarn "66" // this upcast is unnecessary - the types are identical
    #nowarn "58" // possible incorrect indentation..
    #nowarn "57" // do not use create_DelegateEvent
    #nowarn "51" // address-of operator can occur in the code
    #nowarn "1183" // unused 'this' reference
    
    exception ReturnException3b9ed1539eac46fc9190777a400ea3c7 of obj
    exception ReturnNoneException3b9ed1539eac46fc9190777a400ea3c7
    [<AutoOpen>]
    module FuncConvertFinalOverload3b9ed1539eac46fc9190777a400ea3c7 =
      // This extension member adds to the FuncConvert type and is the last resort member in the method overloading rules. 
      type global.Microsoft.FSharp.Core.FuncConvert with
          /// A utility function to convert function values from tupled to curried form
          static member FuncFromTupled (f:'T -> 'Res) = f
    
    type
        
        Program = class
            new() as this =
                {
                }
            static member Main  () =
                Console.WriteLine("Hello, World!") |> ignore
        end

System 名前空間が open されていなかったり, Main メソッドが EntryPoint でなかったりして,残念ながら完全とはいえません。しかし,一から書き直すよりは大分負担が少ないのではないでしょうか。