Client-Side C# Mapping for Interfaces
Proxy Interfaces
On the client side, a Slice interface maps to a C# interface with methods that correspond to the operations on that interface. Consider the following Slice interface:
interface Simple
{
["cs:identifier:Op"]
void op();
}
The Slice compiler generates the following definition for use by the client:
public partial interface SimplePrx : Ice.ObjectPrx
{
Task OpAsync(
Dictionary<string, string>? context = null,
Progress<bool>? progress = null,
CancellationToken cancel = default);
// Synchronous "overload" provided for backwards compatibility.
void Op(Dictionary<string, string>? context = null);
}
As you can see, the compiler generates a proxy interfaceSimplePrx. In general, the generated name is <interface-name>Prx. If an interface is nested in a module M, the generated interface is part of namespace M, so the fully-qualified name is M.<interface-name>Prx.
In the client's address space, an instance of SimplePrx is the local ambassador for a remote instance of an Ice object that implements Simpleand is known as a proxy instance. All the details about the server-side object, such as its address, what protocol to use, and its object identity are encapsulated in that instance.
Creating a Proxy
For each Slice interface, apart from the proxy interface, the Slice-to-C# compiler creates a helper class: for an interface Simple, the name of the generated helper class is SimplePrxHelper.
This helper class provides the createProxy method. With our previous example:
public class SimplePrxHelper : ...
{
public static SimplePrx createProxy(
Ice.Communicator communicator,
string proxyString) { ... }
}
Use createProxy to create a proxy from a communicator and a “stringified” proxy:
SimplerPrx simple = SimplePrxHelper.createProxy(
communicator, "simple:tcp -h localhost -p 4061");
Inheritance from Ice.ObjectPrx
All generated proxy interfaces inherit directly or indirectly from the Ice.ObjectPrx interface, reflecting the fact that all Slice interfaces implicitly inherit from Object.
Interface Inheritance
Inheritance relationships among Slice interfaces are maintained in the generated C# classes. For example:
module M
{
interface A { ... }
interface B { ... }
interface C extends A, B { ... }
}
The generated code for CPrx reflects the inheritance hierarchy:
namespace M;
public interface CPrx : APrx, BPrx
{
...
}
Given a proxy for C, a client can invoke any operation defined for interface C, as well as any operation inherited from C's base interfaces.
Casting a Proxy
In addition to createProxy, the generated helper class provides two static methods for converting a proxy into a proxy of another type:
public class SimplePrxHelper : ...
{
public static SimplePrx? uncheckedCast(Ice.ObjectPrx? proxy)
public static async Task<SimplePrx?> checkedCastAsync(
Ice.ObjectPrx proxy,
Dictionary<string, string>? context = null
Progress<bool>? progress = null,
CancellationToken cancel = default)
}
uncheckedCast
The helper’s uncheckedCast static method allows you to convert any proxy into the helper’s proxy type. For example:
// Convert a SimplePrx into a WidgetPrx, even though the two types are unrelated.
WidgetPrx widget = WidgetPrxHelper.uncheckedCast(simple);
uncheckedCast is a local operation that always succeeds.
checkedCastAsync
checkedCastAsyncis a conditional cast of the proxy: this method makes a remote call to the target object to check if this object implements the proxy’s Slice interface. For example:
// Call operation ice_isA on the Ice object to check if it implements Slice interface
// Widget.
WidgetPrx? widget = await WidgetPrxHelper.checkedCastAsync(simple);
If the target object implements the Slice interface, checkedCastAsyncreturns a non-null proxy, just like uncheckedCast. If the target object doesn’t implement this interface, checkedCastAsync returns null. checkedCastAsynccan also throw an exception, for example if it cannot reach the remote object.
The generated proxy helper also provides a synchronous overload: checkedCast. We recommend you always use async methods when making remote calls, and avoid these synchronous overloads provided for backwards compatibility.
While checkedCastAsync sounds safer than uncheckedCast (you’re making an additional check before casting), in practice you know or should know the type of your proxies and calling checkedCastAsync is rarely necessary.
Proxy Factory Methods
The base proxy interface ObjectPrx supports a variety of methods for customizing a proxy. Since proxies are immutable, each of these factory methods returns a copy of the original proxy that contains the desired modification. For example, you can obtain a proxy configured with a ten second invocation timeout as shown below:
GreeterPrx greeter = GreeterPrxHelper.createProxy(...);
// Create a new GreeterPrx and assign it to greeter.
greeter = GreeterPrxHelper.uncheckedCast(greeter.ice_invocationTimeout(10000));
ice_invocationTimeout and other factory methods in C# return an Ice.ObjectPrx. You need to down-cast this proxy to the correct proxy type as shown above.