Server-Side C-Sharp Mapping for Interfaces

The server-side mapping for interfaces provides an up-call API for the Ice run time: by implementing methods in a servant class, you provide the hook that gets the thread of control from the Ice server-side run time into your application code.

On this page:

Skeleton Classes in C#

On the client side, interfaces map to proxy classes. On the server side, interfaces map to skeleton classes. A skeleton is a class that has an abstract method for each operation on the corresponding interface. For example, consider our Slice definition for the Node interface:

Slice
module Filesystem
{
    interface Node
    {
        idempotent string name();
    }
}

The Slice compiler generates the following definitions for this interface:

C#
namespace Filesystem
{
    public interface NodeOperations_ 
    {
        string name(Ice.Current current = null);
    }

    public partial interface Node : Ice.Object, NodeOperations_ 
    {
    }

    public abstract class NodeDisp_ : Ice.ObjectImpl, Node
    {
        public abstract string name(Ice.Current current = null);

        // Mapping-internal code here...
    }
}

The important points to note here are:

  • As for the client side, Slice modules are mapped to C# namespaces with the same name, so the skeleton class definitions are part of the Filesystem namespace.
  • For each Slice interface <interface-name>, the compiler generates the C# interface <interface-name>Operations_(NodeOperations_ in this example). This interface contains a method for each operation in the Slice interface. (You can ignore the Ice.Current parameter for now.)
  • For each Slice interface <interface-name>, the compiler generates a C# interface <interface-name> (Node in this example). That interface extends Ice.Object and the operations interface.
  • For each Slice interface <interface-name>, the compiler generates an abstract class <interface-name>Disp_ (NodeDisp_ in this example). This abstract class is the actual skeleton class; it is the base class from which you derive your servant class.

The Operations_ interface is used by tie classes. Without them, there would be no need for this additional interface.

Ice.Object Base Interface for C# Servants

Object is mapped to the Ice.Object interface in C#:

C#
namespace Ice
{
    public interface Object : ICloneable
    {
        bool ice_isA(string s, Current current = null);
        void ice_ping(Current current = null);
        string[] ice_ids(Current current = null);
        string ice_id(Current current = null);

        Task<OutputStream> ice_dispatch(Request request);

        ...
    }
}

The methods of Ice.Object behave as follows:

  • ice_isA
    This method returns true if target object implements the given type ID, and false otherwise.
  • ice_ping
    ice_ping provides a basic reachability test for the servant.
  • ice_ids
    This method returns a string array representing all of the type IDs implemented by this servant, including ::Ice::Object.
  • ice_id
    This method returns the type ID of the most-derived interface implemented by this servant.
  • ice_dispatch
    This method dispatches an incoming request to a servant. It is used in the implementation of dispatch interceptors.

Servant Classes in C#

In order to provide an implementation for an Ice object, you must create a servant class that inherits from the corresponding skeleton class. For example, to create a servant for the Node interface, you could write:

C#
public class NodeI : NodeDisp_ 
{
    public NodeI(string name)
    {
        _name = name;
    }

    public override string name(Ice.Current current)
    {
        return _name;
    }

    private string _name;
}

By convention, servant classes have the name of their interface with an I-suffix, so the servant class for the Node interface is called NodeI. (This is a convention only: as far as the Ice run time is concerned, you can choose any name you prefer for your servant classes.) Note that NodeI extends NodeDisp_, that is, it derives from its skeleton class.

As far as Ice is concerned, the NodeI class must implement only a single method: the abstract name method that it inherits from its skeleton. This makes the servant class a concrete class that can be instantiated. You can add other methods and data members as you see fit to support your implementation. For example, in the preceding definition, we added a _name member and a constructor. (Obviously, the constructor initializes the _name member and the name method returns its value.)

Server-Side Normal and idempotent Operations in C#

Whether an operation is an ordinary operation or an idempotent operation has no influence on the way the operation is mapped. To illustrate this, consider the following interface:

Slice
interface Example
{
   void              normalOp();
   idempotent void   idempotentOp();
}

The operations class for this interface looks like this:

C#
public interface ExampleOperations_ 
{
    void normalOp(Ice.Current current = null);
    void idempotentOp(Ice.Current current = null);
}

Note that the signatures of the methods are unaffected by the idempotent qualifier.

See Also