.NET Remoting で カスタムオブジェクト を使用する

Merapi のデモでは、カスタムオブジェクト を扱っていたので .NET Remoting でも扱ってみる。
まず、カスタムオブジェクトを定義する。

using System;

namespace CustomObject
{
  public class RemoteMethod : MarshalByRefObject
  {
    public int Get年(人 p)
    {
      Console.WriteLine(p);
      DateTime sysdate = DateTime.Now.ToLocalTime();
      int 年 = sysdate.Year - p.誕生日.Year;
      年 -= (((sysdate.Month < p.誕生日.Month) || 
              (sysdate.Month == p.誕生日.Month && sysdate.Day < p.誕生日.Day)) ? 1 : 0);
      return 年;
    }
    public 人 Set敬称(人 p)
    {
      Console.WriteLine(p);
      p.名前 = string.Format("{0} さん", p.名前);
      Console.WriteLine(p);
      return p;
    }
  }
  public enum 血液型種類
  {
    A,
    B,
    AB,
    O
  }
  [Serializable]
  public class 人
  {
    public string 名前 { get; set; }
    public DateTime 誕生日 { get; set; }
    public 血液型種類 血液型 { get; set; }
    public override string ToString()
    {
      return string.Format("名前:{0} 誕生日:{1:yyyy/MM/dd} 血液型:{2}", this.名前, this.誕生日, this.血液型);
    }
  }
}

サーバーとクライアントでやり取りするカスタムオブジェクトは、人 クラス。処理を行うクラスは、RemoteMethod クラス とする。


ではサーバー側のコード。前回、IPC だったので、今回は TCP で行う事にした。(参照設定は適当にお願いします。)

using System;
using System.Security.Permissions;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;

namespace TcpRemotingServer
{
  class Program
  {
    [SecurityPermission(SecurityAction.LinkDemand)]
    public static void Main()
    {
      // Set up a server channel.
      TcpServerChannel serverChannel = new TcpServerChannel(9090);
      ChannelServices.RegisterChannel(serverChannel, true);
      // Expose an object for remote calls.
      RemotingConfiguration.RegisterWellKnownServiceType(
        typeof(CustomObject.RemoteMethod), "Remotable", WellKnownObjectMode.SingleCall
      );
      // Wait for method calls.
      Console.WriteLine("Listening...");
      Console.ReadLine();
    }
  }
}

サーバー側のコードは、TcpServerChannelのサンプルコードほぼそのまま。
続いて、クライアント側のコード。(参照設定は適当にお願いします。)

using System;
using System.Security.Permissions;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;

namespace TcpRemotingClient
{
  class Program
  {
    [SecurityPermission(SecurityAction.LinkDemand)]
    public static void Main()
    {
      // Set up a client channel.
      TcpClientChannel clientChannel = new TcpClientChannel();
      ChannelServices.RegisterChannel(clientChannel, true);
      // Obtain a proxy for a remote object.
      RemotingConfiguration.RegisterWellKnownClientType(
        typeof(CustomObject.RemoteMethod), "tcp://localhost:9090/Remotable"
      );
      // Call a method on the object.
      CustomObject.RemoteMethod o = new CustomObject.RemoteMethod();
      CustomObject.人 p = new CustomObject.人() { 名前="テスト", 誕生日=new DateTime(1980, 10, 1), 血液型=CustomObject.血液型種類.A };
      Console.WriteLine("人:{0} 年:{1}", p, o.Get年(p));
      CustomObject.人 敬称追加 = o.Set敬称(p);
      Console.WriteLine("人:{0} 敬称追加:{1}", p, 敬称追加);
      Console.ReadKey();
    }
  }
}

クライアント側のコードも、TcpClientChannelのサンプルコードを少し修正しただけ。
で、実行結果がこれ
ます、サーバー側

続いて、クライアント側


注意点は、サーバーとクライアントでやり取りする カスタムオブジェクトは、シリアライズ可能にする事。

[Serializable]
public class 人{…}

今回は、カスタムオブジェクトのプロパティを全て基本型にしたけど、これもシリアライズ可能な型なら基本型ではなくても大丈夫のはず。
あとクライアント側から、RemoteMethod.Set敬称(人 p) を呼び出している所があるけど

CustomObject.RemoteMethod o = new CustomObject.RemoteMethod();

CustomObject.人 敬称追加 = o.Set敬称(p); // <- ここ

.NET Remoting を使わずに、クライアント側で生成した RemoteMethod インスタンスを使用した場合は、パラメータで渡している 人 インスタンスの 名前 プロパティ も変更されます。


でも、Remoting を使っているのでパラメータの 人 インスタンスがシリアル化されてサーバーに渡り、
サーバー側で 名前 プロパティ に敬称をつけた 「別のインスタンス」がシリアル化されてクライアントに返されます。
なので、パラメータ の 人 インスタンスは何も変更されません。