Use the [Serializable] attribute or subclassing fr

2019-03-09 21:47发布

问题:

I'd like to use an object across AppDomains.

For this I can use the [Serializeable] attribute:

[Serializable]
class MyClass
{
    public string GetSomeString() { return "someString" }
}

Or subclass from MarshalByRefObject:

class MyClass: MarshalByRefObject
{
    public string GetSomeString() { return "someString" }
}

In both cases I can use the class like this:

AppDomain appDomain = AppDomain.CreateDomain("AppDomain");
MyClass myObject = (MyClass)appDomain.CreateInstanceAndUnwrap(
                   typeof(MyClass).Assembly.FullName,
                   typeof(MyClass).FullName);
Console.WriteLine(myObject.GetSomeString());

Why do both approaches seem to have the same effect? What is the difference in both approaches? When should I favor the one approach over the other?

EDIT: At the surface I know that there are differences between both mechanisms, but if someone jumped out of a bush and asked me the question I couldn't give him a proper answer. The questions are quite open questions. I hoped that someone can explain it better than I could do.

回答1:

Using MarshallByRef will execute your methods in the remote AppDomain. When you use CreateInstanceAndUnwrap with a Serializable object, a copy of the object is made to the local AppDomain, so any method call will be executed in the local AppDomain.

If what you want is to communicate between AppDomains go with the MarshallByRef approach.

An example:

using System;
using System.Reflection;

[Serializable]
public class SerializableClass
{
    public string WhatIsMyAppDomain()
    {
        return AppDomain.CurrentDomain.FriendlyName;
    }
}

public class MarshallByRefClass : MarshalByRefObject
{
    public string WhatIsMyAppDomain()
    {
        return AppDomain.CurrentDomain.FriendlyName;
    }
}    

class Test
{

    static void Main(string[] args)
    {
        AppDomain ad = AppDomain.CreateDomain("OtherAppDomain");

        MarshallByRefClass marshall = (MarshallByRefClass)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "MarshallByRefClass");
        SerializableClass serializable = (SerializableClass)ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "SerializableClass");

        Console.WriteLine(marshall.WhatIsMyAppDomain());
        Console.WriteLine(serializable.WhatIsMyAppDomain());

    }
}

This code will display "OtherAppDomain" when you call WhatIsMyAppDomain from the MarshallByRef object, and your default AppDomain name when you call from the Serializable object.



回答2:

These approaches have dramatically different effects.

With the MarshalByRef version you are creating 1 instance of your object. It will live in the newly created AppDomain. All accesse to the object is done via a TransparentProxy.

With the Serializable version you are created 2 instances of your object. One is created in the newly created AppDomain. The CreateInstanceAndUnwrap call will then serialize this object and deserialize it in the original app domain. This creates a second version of the object that is completely independent from the first. In fact, the very next GC will almost certainly eliminate the original object and you'll be left with one instance.



回答3:

Why do both approaches have the same effect?

They do not have the same effect.

With MarshalByRefObject you are referencing one object across AppDomain boundaries. With [Serializable] a copy of the object is being made. This will show up if the state of the object is modified in the child domain and then examined again (or execute the Console.WriteLine inside the child AppDomain).



回答4:

MarshalByRefValue and Serializable implement different semantics for remoting/cross AppDomain communication. MarshalByRefValue essentially gives you reference semantics through a proxy object, while Serializable gives you value semantics (i.e. the state of the object is copied).

In other words MarshalByRefValue will let you modify the instance across different AppDomains, while Serializable will not. The latter is useful when you just need to get the information from one AppDomain to another, e.g. to get the content of an exception from one AppDomain to another.