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:
module Filesystem { interface Node { idempotent string name(); } }
The Slice compiler generates the following definitions for this interface:
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 theIce.Current
parameter for now.) - For each Slice interface
<interface-name>
, the compiler generates a C# interface<interface-name>
(Node
in this example). That interface extendsIce.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#:
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 returnstrue
if target object implements the given type ID, andfalse
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:
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:
interface Example { void normalOp(); idempotent void idempotentOp(); }
The operations class for this interface looks like this:
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.