Copying, Cloning, and Marshalling in .NET
Pages: 1, 2, 3
Listing 6: Playing around with inter-appdomain marshalling
using System;
// not [Serializable]
struct SimpleValueType
{
public int a;
public int b;
public int c;
}
class MainMosdule
{
static void Main()
{
// Create a "remote" appdomain
AppDomain testDomain =
AppDomain.CreateDomain( "testDomain");
MyRemoteableClass remoteObject =
(MyRemoteableClass)testDomain.CreateInstanceAndUnwrap(
"test",
"MyRemoteableClass");
// Initialize a SimpleValueType
SimpleValueType x1 = new SimpleValueType();
x1.a = x1.b = x1.c = 7;
// Try to send it across the wire
// (will fail unless [Serializable]!)
remoteObject.DoSomethingWithSimpleValueType(x1);
// Access the property X remotely
remoteObject.X = remoteObject.X+1;
Console.WriteLine("{0}", remoteObject.X);
}
}
The resulting exception looks something like this (long, boring stack trace
omitted for brevity). But uncomment the
[Serializable]
attribute, and the program will run successfully.
Unhandled Exception:
System.Runtime.Serialization.SerializationException:
The type SimpleValueType in Assembly test,
Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null is not marked as serializable.
To override this default MBV behavior, one can simply derive one's class from
System.MarshalByRefObject -- and this is exactly what
MyRemoteableClass
does, as seen in Listing 7. (This is the same class referenced in Listing
6. We were skipping ahead a bit, but without at least one MBR object in
the picture, there would be nothing to do the marshalling!)
Note that MarshalByRefObject is a base class, not an attribute, nor even an
interface. This has a number of implications, the most obvious of which is that
value-types cannot be made to marshal by reference (value-types in .NET cannot
explicitly inherit from any base class other than System.ValueType). Perhaps
the next most obvious implication is that you can't just revisit any old class
to make it MBR -- .NET does not allow multiple inheritance of base classes, so
you'll likely have to plan for that capability, from the ground up (or else end
up writing a bunch of "MarshalByRefWrapper" classes, which
isn't very fun).
Listing 7: A simple MBR-capable class (as seen in Listing 6)
class MyRemoteableClass : System.MarshalByRefObject
{
private int x;
public void DoSomethingWithSimpleValueType(SimpleValueType vt)
{ this.x = vt.a + vt.b + vt.c; }
public int X
{
get { return this.x; }
set { this.x = value; }
}
}
Why is MarshalByRefObject a base class, rather than an interface or attribute?
The easiest explanation is that MBR objects need quite a bit of boilerplate
functionality (with regard to activation policies, lifetime services, etc.)
that is best encapsulated in a base class, and re-used with
implementation-inheritance. No other solution would allow us to create a MBR
class with just a single line of code (let alone a single keyword).
More Fun With Marshalling: ref and out Parameters (Again)
Earlier, we saw that how the C#
ref
and
out keywords affected the semantics of parameters
passed to method calls. How do these keywords affect parameters marshalled
between appdomains? In the intra-AppDomain case, we saw that using either
ref or
out was tantamount to passing a pointer to the
argument (regardless of whether the argument was a reference-type or not).
But one cannot pass a pointer from one process to another -- let alone from one
machine to another! Instead, one must think of marshalling as a form of
messaging (which it is). Either way, the effect is largely the same. The C#
code may look like an ordinary function call, but under the hood, the method's
arguments are being packaged into an envelope and mailed away ... The
ref keyword is an instruction to wait for a response,
because the argument will travel both to and from the remote destination, along
the remoting channel. Parameters with the
out keyword (as well as return values) are only sent
back, from callee to caller, along the channel. (Arguably, the IDL attribute
keywords used to describe the same concepts in DCOM and RPC were more
intuitive. These are listed in Table 2.) Just like DCOM (and RPC before
it) it's the proxy objects on either side of the remoting channel that do all
the dirty work.
Table 2: Analagous marshalling keywords in DCOM and .NET
| RPC/DCOM keyword | C# equivalent remoting keyword |
|---|---|
[in] |
(default) |
[in,out] |
ref |
[out] |
out |
Conclusion
Hopefully this article has shed some light on some basic aspects of .NET programming that should be quite straightforward -- moving copies of objects around from one place to another. But of course, nothing in modern computing is straightforward, anymore (especially if it involves remoting in any way). The .NET framework is a vast and complicated space to work in, but at the same time its design is vastly more intuitive for programmers than anything that came before.
We've examined everything from the differences between value-types and
reference-types, the semantics of return-values, C#'s
out
and
ref
keywords, the ICloneable interface, the MemberwiseClone method, the special
semantics of the framework's string class, and even a little bit of remoting
and marshalling. Whew! Who knew that pushing bits around would be so hard?
References
"Passing Parameters" C# Programmer's Reference, MSDN Library
"Argument Passing ByVal and ByRef" Visual Basic Language Concepts, MSDN Library
"Nice Box. What's in It?" Eric Gunnerson, MSDN Library.
"Open the Box! Quick!" Eric Gunnerson, MSDN Library.
".NET Remoting: Design and Develop Seamless Distributed Applications for the Common Language Runtime" Dino Esposito, MSDN Magazine.
Return to ONDotnet.com

