8. Creating Remote Objects
If
the client has registered a type as a remote type (either
programmatically or administratively), the client can use the plain new
operator to create any kind of remote type, be it client- or
server-activated. However, as mentioned already, .NET provides clients
with several ways to connect to remote objects.
These options differ in their need for pre-registration of the type and
in their ability to connect to client-activated objects.
8.1. RemotingServices.Connect( )
The client can choose to connect explicitly to a remote object using the static method Connect( ) of the RemotingServices class:
public static object Connect(Type classToProxy, string url);
Calling Connect( ) explicitly creates a proxy to the remote object on the client side. You can use Connect( ) only to connect to server-activated objects, and you can use only the default constructor. Using the same definitions as in Example 10-5, here is how to create a remote server-activated object using Connect( ):
Type serverType = typeof(MyClass);
string url = "tcp://localhost:8005/MyRemoteServer";
MyClass obj;
obj = (MyClass)RemotingServices.Connect(serverType,url);
obj.Count( );
Connect( ) doesn't
affect other attempts of the client to create instances of the type,
meaning that if no type registration takes place, after a call to Connect( ), calls to new
create the type locally in the client's app domain. In fact, you can
even register the type, associate it with one location, and then have Connect( ) activate an instance at another location.
8.2. Activator.GetObject( )
The Activator class provides the static method GetObject( ):
public static object GetObject(Type type,string url);
GetObject( ) works like RemotingServices.Connect( ) does, allowing the client to connect to a server-activated object:
Type serverType = typeof(MyClass);
string url = "tcp://localhost:8005/MyRemoteServer";
MyClass obj;
obj = (MyClass)Activator.GetObject(serverType,url);
obj.Count( );
GetObject( ) doesn't require type registration on the client side, and it doesn't affect registration of the type.
8.3. Activator.CreateInstance( )
The Activator class provides many overloaded versions of the static method CreateInstance( ). CreateInstance( )
lets you create any object type (client- or server-activated) and lets
you have fine-grained control over the creation process. Some of the
versions require type registration before invocation. This version:
public static object CreateInstance(Type type);
is no different from new and creates the instance at the remote location registered:
Type serverType = typeof(MyClass);
MyClass obj;
obj = (MyClass)Activator.CreateInstance(serverType);
obj.Count( );
CreateInstance( ) also offers a generic version:
public static T CreateInstance<T>( );
The generic version is, of course, superior to the object-based version, as it is type-safe:
MyClass obj = Activator.CreateInstance<MyClass>( );
obj.Count( );
The following
version allows you to pass in construction parameters for
client-activated objects in the form of an array of objects:
public static object CreateInstance(Type type, object[] args);
CreateInstance( )
chooses the best-fitting constructor, similarly to how the compiler
chooses a constructor. If you want to create a remote instance without
registering the type beforehand, use a version that accepts an array of
activation attributes:
public static object CreateInstance(Type type,object[] args,
object[] activationAttributes);
You can pass in as an activation attribute an instance of UrlAttribute, defined in the System.Runtime.Remoting.Activation namespace:
using System.Runtime.Remoting.Activation;
using RemoteServer;
object[] attArray = {new UrlAttribute("tcp://localhost:8005")};
Type serverType = typeof(MyClass);
//No registration is required:
MyClass obj = (MyClass)Activator.CreateInstance(serverType,null,attArray);
obj.Count( );
CreateInstance( ) is available for advanced creation scenarios. This version:
public static ObjectHandle CreateInstance(string assemblyName,string typeName);
creates an instance
of the specified type and delays setting up a proxy and loading the
server assembly until the object handle is unwrapped:
using RemoteServer;
ObjectHandle handle;
handle = Activator.CreateInstance("ServerAssembly","RemoteServer.MyClass");
//Proxy is only set up here, and assembly is loaded locally
MyClass obj = (MyClass)handle.Unwrap( );
obj.Count( );
Note that you have to specify the fully qualified type name. Other versions of CreateInstance( ) allow you to specify locale and security information.
Table 1 compares the various options available when creating or activating remote objects.
Table 1. Creating and activating remote object options
Creating option | Requires registration | Client-activated objects | Server-activated objects |
---|
new | Yes | Yes | Yes |
RemotingServices.Connect( ) | No | No | Yes |
Activator.GetObject( ) | No | No | Yes |
Activator.CreateInstance( ) | Depends | Yes | Yes |
8.4. Creating remote generic objects
You can use the Activator class to create instances of generic remote objects, such as this one:
public class MyServer<T> : MarshalByRefObject
{...}
You need to
provide the specific type parameters to use, and you must provide the
specific types when explicitly casting the returned object:
string url = "tcp://localhost:8005/MyRemoteServer";
Type serverType = typeof(MyServer<int>);
MyServer<int>obj;
obj = (MyServer<int>)Activator.GetObject(serverType,url);
You can, of course, also use the generic CreateInstance( ) method to create generic types:
MyServer<int> obj;
obj = Activator.CreateInstance<MyServer<int>>( );
It would have been useful if GetObject( ) were to offer a generic version, like this:
public static T GetObject<T>(string url);
|