Client-Side C# Mapping for Operations
Mapping for Operations
As we saw in the Client-Side C# Mapping for Interfaces, for each operation on an interface, the generated proxy class contains two methods for this operation. To invoke an operation, you call one of these methods on the proxy. For example, let’s take the generated code from the greeter example:
module VisitorCenter
{
interface Greeter
{
["cs:identifier:Greet"]
string greet(string name);
}
}
The proxy interface generated from the Greeter interface, after removing extra details, is as follows:
namespace VisitorCenter
{
public partial interface GreeterPrx : Ice.ObjectPrx
{
string Greet(
string name,
Dictionary<string, string>? context = null);
Tasks.Task<string> GreetAsync(
string name,
Dictionary<string, string>? context = null,
IProgress<bool>? progress = null,
CancellationToken cancel = default);
}
}
Given a proxy to an object of type Greeter, the client can invoke the greet operation as follows:
GreeterPrx greeter = GreeterPrxHelper.createProxy(
communicator, "greeter:tcp -h localhost -p 4061");
string greeting = await greeter.GreetAsync("Alice"); // Get greeting via RPC
Sync and Async Methods
For each operation, the Slice compiler generates 2 methods on the proxy class:
a “sync” method with the same name as the operation. When you call this method, your thread waits synchronously until the invocation completes. A successful invocation completes with a return value (which can be void), while an unsuccessful invocation completes with an exception.
an “async” method, named
<operation-name>Async. When you call this method, your thread marshals the arguments to the method synchronously, but the remainder of this invocation is asynchronous, and the method returns immediately. You get the result (return value or exception) through aTask. These async methods are described in more detail in Asynchronous Method Invocation (AMI) in C#.
The “sync” methods are provided for backwards compatibility: you should only use the async methods in modern C# code.
Exception Handling
Any operation invocation may throw a runtime exception and, if the operation has an exception specification, may also throw user exceptions. Suppose we have the following simple interface:
exception Tantrum
{
["cs:identifier:Reason"]
string reason;
}
interface Child
{
["cs:identifier:AskToCleanUp"]
void askToCleanUp() throws Tantrum;
}
Slice exceptions are thrown as C# exceptions, so you can simply enclose one or more operation invocations in a try-catch block:
ChildPrx child = ...; // Get child proxy...
try
{
await child.AskToCleanUpAsync();
}
catch (Tantrum t)
{
Console.WriteLine($"The child says: {t.Reason}");
}