2006年02月11日

F#で.NET Remoting

 F#で.NET Remotingしてみる。

 この記事のサンプルコードとほぼ同じことをしよう。まずはリモートオブジェクトのクラスを定義するソース(obj.fs)から。


module RemoteTest

open System

type MyRemoteObject =
class
inherit MarshalByRefObject
val hole : unit
new () =
{
inherit MarshalByRefObject();
hole = Console.WriteLine("Constructor called.")
}
override x.Finalize() = Console.WriteLine("Destructor called.")
member x.sayHello s = "Hello, " ^ s ^ "!"
end

 解説しよう。holeというunit型変数は、コンストラクタ中で手続きを実行するための非常手段だ。正しいやり方があるのかもしれないが、見つけられなかった。F#自体にはデストラクタの構文がないので、Finalize()をオーバーライドしている。正しく動くかどうかは自信がない。


 リモートオブジェクトは.NETアセンブリとしてpublicなクラスでなければならない。F#が作る.NETアセンブリのクラスをpublicにするには、インターフェイスファイル(拡張子fsi)が要る。


module RemoteTest

open System

type MyRemoteObject =
class
inherit MarshalByRefObject
new : unit->MyRemoteObject
member sayHello : string -> string
end

 本当にこれでいいのか知らないが、とりあえず現在のコンパイラ(バージョン1.1.8.1)ではこれで動く。


 Visual Studioでこれらのファイルをビルドする際には、Custom Buildを使う必要がある。引数指定のなかで、インターフェイスファイルを先に、ソースファイルを後に書く。-a
-g obj.fsi obj.fsという具合である。


 続いてサーバを。


module ServerTest

open System
open System.Threading
open System.Runtime.Remoting
open System.Runtime.Remoting.Channels
open System.Runtime.Remoting.Channels.Tcp
open Microsoft.FSharp.Idioms

#r @"obj.dll";;
open RemoteTest

let remoteObjectType = (typeof() : typ<MyRemoteObject>).result

let channel = new TcpChannel(16383)
do ChannelServices.RegisterChannel(channel, true)
do RemotingConfiguration.RegisterWellKnownServiceType(
remoteObjectType,
"MyRemoteObject",
WellKnownObjectMode.SingleCall)
let sync = new Object()
let _ = lock(sync) (
fun () -> Monitor.Wait(sync))

 remoteObjectTypeがわかりにくい。F#には組み込みのtypeofがないので、こういう妙なことになる。lockはbooleanを返すので_にletしている。


 そしてクライアント。


module ClientTest

open System
open System.Threading
open System.Runtime.Remoting
open System.Runtime.Remoting.Channels
open System.Runtime.Remoting.Channels.Tcp
open Microsoft.FSharp.Idioms

#r @"obj.dll";;
open RemoteTest

let remoteObjectType = (typeof() : typ<MyRemoteObject>).result

let channel = new TcpChannel()
do ChannelServices.RegisterChannel(channel, true)
let remote = Activator.GetObject(
remoteObjectType,
"tcp://localhost:16383/MyRemoteObject")
let msg = match remote with
| null -> "Remote object not found."
| _ ->
let remoteAsSimpleType = remote :?> MyRemoteObject
in remoteAsSimpleType.sayHello "World"
do Console.WriteLine(msg)

 パターンマッチングで変数をletする。これがF#の醍醐味だ。

Posted by hajime at 2006年02月11日 04:18
Comments