いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

MarshalByRefObjectとSerializableのちょっとした違い (アプリケーションドメイン, 境界越え, Assembly, AppDomain, CreateInstanceAndUnwrap)


以下、自分用のメモ書きです。


リモート処理を行ったりする際に, MarshalByRefObjectを継承したクラスを作成しますが
MarshalByRefObjectを継承したオブジェクトは、アプリケーションドメインの境界を越えて実行されます。


では、Serializable属性を付加しただけのオブジェクトはどうなのかってのをちょっと実行してみました。


以下、サンプルです。

#region MarshalByRefObjectSamples-01
    public class MarshalByRefObjectSamples01 : IExecutable {

        class CanNotMarshalByRef {

            public void PrintDomain() {
                Console.WriteLine(
                  "Object is executing in AppDomain \"{0}\"", 
                  AppDomain.CurrentDomain.FriendlyName); 
            }
        }

        class CanMarshalByRef : MarshalByRefObject {

            public void PrintDomain() {
                Console.WriteLine(
                  "Object is executing in AppDomain \"{0}\"", 
                  AppDomain.CurrentDomain.FriendlyName); 
            }
        }

        [Serializable]
        class CanSerialize {

            public void PrintDomain() {
                Console.WriteLine(
                  "Object is executing in AppDomain \"{0}\"", 
                  AppDomain.CurrentDomain.FriendlyName); 
            }
        }

        public void Execute() {

            CanNotMarshalByRef obj1 = new CanNotMarshalByRef();
            obj1.PrintDomain();

            AppDomain newDomain = AppDomain.CreateDomain("new domain");

            /* ** ERROR **  
               "Gsf.Samples.MarshalByRefObjectSamples01+CanNotMarshalByRef"は
               シリアル化可能として設定されていません。
               
            CanNotMarshalByRef obj2 = 
                    (CanNotMarshalByRef) newDomain.CreateInstanceAndUnwrap(
                            Assembly.GetExecutingAssembly().FullName, 
                            typeof(CanNotMarshalByRef).FullName
                    );

            obj2.PrintDomain();
            */

            CanMarshalByRef obj3 = 
                    (CanMarshalByRef) newDomain.CreateInstanceAndUnwrap(
                            Assembly.GetExecutingAssembly().FullName, 
                            typeof(CanMarshalByRef).FullName
                    );

            obj3.PrintDomain();

            //
            // Serializable属性を付加しただけでは、実行は行えるが、別のAppDomain内からの
            // 実行ではなくて、呼び元のAppDomainでの実行となる。
            // (つまり、AppDomainの境界を越えていない。)
            //
            CanSerialize obj4 = 
                    (CanSerialize) newDomain.CreateInstanceAndUnwrap(
                            Assembly.GetExecutingAssembly().FullName, 
                            typeof(CanSerialize).FullName
                    );

            obj4.PrintDomain();
        }
    }
#endregion


上記を実行すると、呼び元のアプリケーションドメインが"MySamples.exe"という
名前だとすると、

  Object is executing in AppDomain "MySamples.exe"
  Object is executing in AppDomain "new domain"
  Object is executing in AppDomain "MySamples.exe"

と表示されます。


注意すべき点は、Serializableを付加したクラスは
アプリケーションドメインの境界は越えないけど、CreateInstanceAndUnwrapメソッドで
作成は可能という事です。


つまり、CreateInstanceAndUnwrapメソッドでオブジェクトは取得できますが
実行すると、呼び出し元のAppDomainで実行されます。


補足:
MarshalByRefObjectを継承している場合、プロキシを経由してマーシャリングが行われます。(参照渡し)
対して、MarshalByRefObjectを継承せず、Serializableのみを適用している場合、暗黙的に値渡しでマーシャリングされます。
つまり、呼び元はオブジェクトのコピーを利用していることになります。

================================
過去の記事については、以下のページからご参照下さい。