The mapping to skeleton classes we saw in Section 16.4 requires the servant class to inherit from its skeleton class. Occasionally, this creates a problem: some class libraries require you to inherit from a base class in order to access functionality provided by the library; because C# does not support multiple implementation inheritance, this means that you cannot use such a class library to implement your servants because your servants cannot inherit from both the library class and the skeleton class simultaneously.
To allow you to still use such class libraries, Ice provides a way to write servants that replaces inheritance with delegation. This approach is supported by
tie classes. The idea is that, instead of inheriting from the skeleton class, you simply create a class (known as an
implementation class or
delegate class) that contains methods corresponding to the operations of an interface. You use the
‑‑tie option with the
slice2cs compiler to create a tie class. For example, for the
Node interface we saw in
Section 16.4.1, the
‑‑tie option causes the compiler to create exactly the same code as we saw previously, but to also emit an additional tie class. For an interface
<interface‑name>, the generated tie class has the name
<interface‑name>Tie_:
This looks a lot worse than it is: in essence, the generated tie class is simply a servant class (it extends
NodeDisp_) that delegates each invocation of a method that corresponds to a Slice operation to your implementation class (see
Figure 16.1)
.
The Ice.TieBase interface defines the
ice_delegate methods that allow you to get and set the delegate.
Note that this class is identical to our previous implementation, except that it implements the
NodeOperations_ interface and does not extend
NodeDisp_ (which means that you are now free to extend any other class to support your implementation).
When using tie classes, it is important to remember that the tie instance is the servant, not your delegate. Furthermore, you must not use a tie instance to incarnate (see
Section 16.8) an Ice object until the tie has a delegate. Once you have set the delegate, you must not change it for the lifetime of the tie; otherwise, undefined behavior results.
You should use the tie approach only if you need to, that is, if you need to extend some base class in order to implement your servants: using the tie approach is more costly in terms of memory because each Ice object is incarnated by two C# objects instead of one, the tie and the delegate. In addition, call dispatch for ties is marginally slower than for ordinary servants because the tie forwards each operation to the delegate, that is, each operation invocation requires two function calls instead of one.
Also note that, unless you arrange for it, there is no way to get from the delegate back to the tie. If you need to navigate back to the tie from the delegate, you can store a reference to the tie in a member of the delegate. (The reference can, for example, be initialized by the constructor of the delegate.)