Using Objects as Parameters and Return Types in Remoting

This article has been excerpted from book "The Complete Visual C# Programmer's Guide" from the Authors of C# Corner.

The example code for this section of the chapter is in the Sample2 folder. This example is a serveractivated sample, and the client will not be using the new operator, only Activator.GetObject(). The reason for this is to demonstrate the use of interfaces in remoting, and interfaces can't be created. The server has access to the actual implementation, while the client references the interface. Trying to use new or Activator.CreateInstance() on the client results in compiler errors. Because of the use of properties, the server will register the remote objects in Singleton mode. In that manner, the objects are able to maintain state. The example consists of a class library (SimpleInterfaceLib) and two console applications (ClientInterfaceExe and ServerInterfaceExe). Don't forget to add references to System.Runtime.Remoting.dll and SimpleInterfaceLib.dll to the console applications.

So far in this chapter, we have looked at remote objects only from a marshal-by-reference perspective. Marshal-by-reference classes must inherit from MarsahlByRefObject. In this section we also look at another type of remotable object, marshal by value. Marshal-by-value classes are declared by using the [Serializable] attribute or implementing the System.Runtime.Serialization.ISerializable interface. These objects are copied (serialized) across the wire and are reconstituted at the other end (deserialized). The objects are independent of each other, and modifying one doesn't change the other. The SimpleInterfaceLib code is shown in Listing 25.7.

Listing 25.7: SimpleInterface.cs

using
System;
using
System.Collections;

namespace
SimpleInterfaceLib
{
    public interface ISimpleInterface
    {
        string this[int index] { get; }
        ArrayList Names { set; }
        SimpleMarshal InvokeMethod(ref SimpleParam param);
    }
}


The areas of note here are the use of the this and the ref keywords. The this keyword, in this context, is used as an indexer. The ref keyword means that any changes to param in the InvokeMethod will be reflected in the original object. There is also an out keyword, not used here, as a parameter modifier. If we were using the out keyword, in this case, param would be passed in as a null, and the object would be created on the server and assigned to param.

Listing 25.8: SimpleMarshal.cs and SimpleParam.cs


public
class SimpleMarshal : MarshalByRefObject
{
    private string name = null;
    public SimpleMarshal()
    {
        Console.WriteLine("In SimpleMarshal constructor");
    }

    public string Name
    {
        get
        {
            Console.WriteLine("SimpleMarshal.Name.get");
            return (name);
        }
        set
        {
            Console.WriteLine("SimpleMarshal.Name.set");
            name = value;
        }
    }
}

[Serializable]

public
class SimpleParam
{
    private ArrayList arrayNames = null;
    public SimpleParam()
    {
    }

    public ArrayList ArrayNames
    {
        get
        {
            Console.WriteLine("SimpleParam.ArrayNames.get");
            return (arrayNames);
        }
        set
        {
            Console.WriteLine("SimpleParam.ArrayNames.set");
            arrayNames = value;
        }
    }
}


In Listing 25.8 the SimpleMarshal class derives from MarshalByRefObject. The SimpleParam class definition uses the [Serializable] attribute. The ISerializable interface could have been used in place of the attribute. Running this code on the server, results in the output shown in Figure 25.8.

Figure-25.8_ServerInterface.gif

Figure 25.8: ServerInterfaceExe Output

Listing 25.9: ServerInterfaceExe.cs

namespace ServerInterfaceExe
{
    class ServerInterface
    {
        static int Main(string[] args)
        {
            HttpChannel http = null;
            http = new HttpChannel(1234);
            ChannelServices.RegisterChannel(http);
            RemotingConfiguration.RegisterWellKnownServiceType(
            typeof(SimpleInterfaceImpl),
            "InterfaceImpl",
            WellKnownObjectMode.Singleton);
            Console.WriteLine(
            "Press <enter> to exit.");
            Console.ReadLine();
            return (0);
        }
    }

    public class SimpleInterfaceImpl : MarshalByRefObject,
    ISimpleInterface
    {
        private ArrayList arrayNames = new ArrayList();
        public SimpleInterfaceImpl()
            : base()
        {
            Console.WriteLine("SimpleInterfImpl const");
        }

        public string this[int index]
        {
            get
            {
                string ret = null;
                if (arrayNames.Count == 0)
                {
                    ret = "No names in list";
                }
                else if (index >= arrayNames.Count)
                {
                    ret = "Index beyond end of list.";
                }
                else
                {
                    ret = (string)arrayNames[index];
                }
                Console.WriteLine("SimpleInterfImpl indexer");
                return (ret);
            }
        }
        public ArrayList Names
        {
            set
            {
                arrayNames = value;
            }
        }
        public SimpleMarshal InvokeMethod(ref SimpleParam param)
        {
            ArrayList arrayTmp = new ArrayList();
            ArrayList arrayParam = null;
            arrayParam = param.ArrayNames;
            for (int x = 0; x < arrayParam.Count; x++)
            {
                arrayTmp.Add(arrayParam[x] + " InvokeMethod");
            }
            param.ArrayNames = arrayTmp;
            Console.WriteLine("SimpleInterfImpl.InvokeMethod()");
            SimpleMarshal paramNew = new SimpleMarshal();
            paramNew.Name = "SimplMarshal from MarshalByRefObj";
            return (paramNew);
        }
    }
}


In the main() in Listing 25.9, the code is essentially the same as in our first example. The only changes have been the use of HttpChannel instead of HttpServerChannel and the switch to Singleton mode. HttpChannel combines the functionality of HttpServerChannel and HttpClientChannel. HttpServerChannel receives messages, while HttpClientChannel sends messages. HttpChannel transports messages in either direction.

Just below the main(), the SimpleInterfaceImpl class is defined. It inherits from MarshalByRefObject and ISimpleInterface, by implementing the indexer and Names properties and the InvokeMethod(). When designing remote objects, it is worth considering generic interfaces for their potential reuse.

Figure-25.9-ClientInterface.gif

Figure 25.9: ClientInterfaceExe Output

Listing 25.10: ClientInterface.cs


static
int Main(string[] args)
{
    HttpChannel http = null;
    http = new HttpChannel();
    ChannelServices.RegisterChannel( http);
    ISimpleInterface simple = null;
    simple = (ISimpleInterface)Activator.GetObject(
    typeof(ISimpleInterface),
    "http://localhost:1234/InterfaceImpl");
    ArrayList arrayParam = new ArrayList();
    arrayParam.Add( "Sam");
    arrayParam.Add( "Tom");
    arrayParam.Add( "Heidi");
    arrayParam.Add( "Rose");
    arrayParam.Add( "Babe");
    simple.Names = arrayParam;
    SimpleParam param1 = null;
    param1 = new SimpleParam();
    param1.ArrayNames = (ArrayList)arrayParam.Clone();
    SimpleMarshal param2 = null;
    param2 = simple.InvokeMethod( ref param1);
    ArrayList arrayRet = null;
    arrayRet = param1.ArrayNames;
    for( int x = 0; x < arrayRet.Count; x++)
    {
    Console.WriteLine( "indexer {0} - ref {1}.",
    simple[x], arrayRet[x]);
    }
    Console.WriteLine( "SimpleMarshal \"{0}\"", param2.Name);
    return( 0);
    }


In Listing 25.10, the param2 returned by InvokeMethod() is a transparent proxy. The client's output is captured in Figure 25.9.

Conclusion

Hope this article would have helped you in understanding Using Objects as Parameters and Return Types in Remoting. See other articles on the website on .NET and C#.

visual C-sharp.jpg The Complete Visual C# Programmer's Guide covers most of the major components that make up C# and the .net environment. The book is geared toward the intermediate programmer, but contains enough material to satisfy the advanced developer.


Similar Articles