Date: Fri, 29 Mar 2024 11:04:00 +0000 (UTC) Message-ID: <2071836789.25389.1711710240875@ae5f4610bf64> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_25388_1397825025.1711710240874" ------=_Part_25388_1397825025.1711710240874 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
The mapping of Slice interfaces revolves around the idea that, to= invoke a remote operation, you call a member function on a local class ins= tance that is a proxy for the remote object. This makes the mapping easy and intuitive to use b= ecause making a remote procedure call is no different from making a local p= rocedure call (apart from error semantics).
On this page:
On the client side, a Slice interface maps to a C# interface with member= functions that correspond to the operations on that interface. Consider th= e following simple interface:
interface Simple { void op(); }
The Slice compiler generates the following definition for use by the cli= ent:
public = ;interface SimplePrx : Ice.ObjectPrx { void op(); void op(Ice.OptionalContext context =3D new Ic= e.OptionalContext()); }
As you can see, the compiler generates a proxy interface =
SimplePrx
. 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 fu=
lly-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 the Simple
inter=
face in a server and is known as a proxy instance. All the details=
about the server-side object, such as its address, what protocol to use, a=
nd its object identity are encapsulated in that instance.
Note that SimplePrx
inherits from Ice.ObjectPrx
. This reflects the =
fact that all Ice interfaces implicitly inherit from Ice::Object.
For each operation in the interface, the proxy class has a member functi=
on of the same name. For the preceding example, we find that the operation =
op
has been mapped to the method op
. The optional=
parameter context
is for use by the Ice run time to stor=
e information about how to deliver a request. You normally do not need to u=
se it. (We examine the context
parameter in detail in Request Contexts. The parameter is=
also used by IceStorm.)
Because all the <interface-name>Prx
types ar=
e interfaces, you cannot instantiate an object of such a type. Instead, pro=
xy instances are always instantiated on behalf of the client by the Ice run=
time, so client code never has any need to instantiate a proxy directly.Th=
e proxy references handed out by the Ice run time are always of type =
<interface-name>Prx
; the concrete implementation of t=
he interface is part of the Ice run time and does not concern application c=
ode.
A value of null
denotes the null proxy. The null proxy is a=
dedicated value that indicates that a proxy points "nowhere" (denotes no o=
bject).
Inheritance relationships among Slice interfaces are maintained in the g= enerated C# classes. For example:
module M { interface A { ... } interface B { ... } interface C extends A, B { ... } }
The generated code for CPrx
reflects the inherita=
nce 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 inheri=
ted from C
's base interfaces.
All Ice objects have Object
as the ultimate ancestor type, =
so all proxies inherit from Ice.ObjectPrx
. ObjectPrx provides a number of methods:
namespace&n= bsp;Ice { public interface ObjectPrx { Identity ice_getIdenti= ty(); bool ice_isA(string&nb= sp;id); string ice_id(); void ice_ping(); int GetHashCode(); bool Equals(object&nbs= p;r); // Defined in&nbs= p;a helper class: // public static boo= l Equals(Ice.ObjectPrx lhs, ObjectPrx rhs); public static boo= l operator=3D=3D(ObjectPrx lhs, ObjectPrx rhs); public static boo= l operator!=3D(ObjectPrx lhs, ObjectPrx rhs); // ... } }
Note that the static methods are not actually defined in Ice.Objec=
tPrx
, but in a helper class that becomes a base class of an instanti=
ated proxy. However, this is simply an internal detail of the C# mapping =
=E2=80=94 conceptually, these methods belong with Ice.ObjectPrx
, so we discuss them here.
The methods behave as follows:
ice_getIdentity
This method returns the identity of the object denoted by the proxy. The i=
dentity of an Ice object has the following Slice type:
module Ice { struct Identity { string name; string category; } }
To see whether two proxies denote the same object, first obtain th= e identity for each object and then compare the identities:
Ice.ObjectP= rx o1 =3D ...; Ice.ObjectPrx o2 =3D ...; Ice.Identity i1 =3D o1.ice_getIdentity(); Ice.Identity i2 =3D o2.ice_getIdentity(); if(i1.Equals(i2)) { // o1 and o2 denote the&nb= sp;same object } else { // o1 and o2 denote differ= ent objects }
ice_isA
The ice_isA
method determines whether the object denoted =
by the proxy supports a specific interface. The argument to ice_isA=
code> is a type ID. For example, to=
see whether a proxy of type
ObjectPrx
denotes a Printer=
object, we can write:
Ice.ObjectP= rx o =3D ...; if(o !=3D null && o.ice_isA("::Printer")) { // o denotes a Printer obj= ect } else { // o denotes some other ty= pe of object }
Note that we are testing whether the proxy is null before attempti=
ng to invoke the ice_isA
method. This avoids getting a N=
ullReferenceException
if the proxy is null.
ice_ids
ice_ids
method returns an array of strings representi=
ng all of the type IDs that the obj=
ect denoted by the proxy supports.ice_id
ice_id
method returns the type ID of the object denoted by the proxy. Note that the type =
returned is the type of the actual object, which may be more derived than t=
he static type of the proxy. For example, if we have a proxy of type =
BasePrx
, with a static type ID of ::Base
, the return va=
lue of ice_id
might be ::Base
, or it might someth=
ing more derived, such as ::Derived
.ice_ping
ice_ping
method provides a basic reachability test f=
or the object. If the object can physically be contacted (that is, the obje=
ct exists and its server is running and reachable), the call completes norm=
ally; otherwise, it throws an exception that indicates why the object could=
not be reached, such as ObjectNotExistException
or Conn=
ectTimeoutException
.
Equals
equals
returns false
even though the proxies=
denote the same object.The ice_isA
, ice_ids
, ice_id
, and=
ice_ping
methods are remote operations and therefore support =
an additional overloading that accepts a request context. Also note that there are other methods in ObjectPrx
, not show=
n here. These methods provide different ways to dispatch a call and also pr=
ovide access to an object's facets.
For each Slice interface, apart from the proxy interface, the Slice-to-C=
# compiler creates a helper class: for an interface Simple
, th=
e name of the generated helper class is SimplePrxHelper
.
You can ignore the ObjectPrxHelperBase
base class =E2=80=94=
it exists for mapping-internal purposes.
The helper class contains two methods of interest:
public = ;class SimplePrxHelper : Ice.ObjectPrxHelperBase, Simpl= ePrx { public static SimplePrx checkedCast(= Ice.ObjectPrx b); public static SimplePrx checkedCast( Ice.ObjectPrx b, System.Collections.Generic.= Dictionary<string, string> ctx); public static SimplePrx uncheckedCas= t(Ice.ObjectPrx b); public static string ice_staticId(); // ... }
For checkedCast
, if the passed proxy is for an object of ty=
pe Simple
, or a proxy for an object with a type derived from <=
code>Simple, the cast returns a non-null reference to a proxy of typ=
e SimplePrx
; otherwise, if the passed proxy denotes an object =
of a different type (or if the passed proxy is null), the cast returns a nu=
ll reference.
Given a proxy of any type, you can use a checkedCast
to det=
ermine whether the corresponding object supports a given type, for example:=
Ice.ObjectP= rx obj =3D ...; &nb= sp;// Get a proxy from somewhere... SimplePrx simple =3D SimplePrxHelper.checkedCast(obj); if(simple !=3D null) { // Object supports the Simple&n= bsp;interface... } else { // Object is not of type&n= bsp;Simple... }
Note that a checkedCast
contacts the server. This is necess=
ary because only the implementation of an object in the server has definite=
knowledge of the type of an object. As a result, a checkedCast
may throw a ConnectTimeoutException
or an ObjectNotExi=
stException
. (This also explains the need for the helper class: the =
Ice run time must contact the server, so we cannot use a C# down-cast.)
In contrast, an uncheckedCast
does not contact the server a=
nd unconditionally returns a proxy of the requested type. However, if you d=
o use an uncheckedCast
, you must be certain that the proxy rea=
lly does support the type you are casting to; otherwise, if you get it wron=
g, you will most likely get a run-time exception when you invoke an operati=
on on the proxy. The most likely error for such a type mismatch is Op=
erationNotExistException
. However, other exceptions, such as a marsh=
aling exception are possible as well. And, if the object happens to have an=
operation with the correct name, but different parameter types, no excepti=
on may be reported at all and you simply end up sending the invocation to a=
n object of the wrong type; that object may do rather nonsensical things. T=
o illustrate this, consider the following two interfaces:
interface Process { void launch(int stackSize, int = dataSize); } // ... interface Rocket { void launch(float xCoord, float = ;yCoord); }
Suppose you expect to receive a proxy for a Process
object =
and use an uncheckedCast
to down-cast the proxy:
Ice.ObjectP= rx obj =3D ...; &nb= sp; //&nbs= p;Get proxy... ProcessPrx process =3D ProcessPrxHelper.uncheckedCast(obj); = // No worries... process.launch(40, 60);  = ; //= Oops...
If the proxy you received actually denotes a Rocket
object,=
the error will go undetected by the Ice run time: because int
=
and float
have the same size and because the Ice protocol doe=
s not tag data with its type on the wire, the implementation of Rocke=
t::launch
will simply misinterpret the passed integers as floating-p=
oint numbers.
In fairness, this example is somewhat contrived. For such a mistake to g=
o unnoticed at run time, both objects must have an operation with the same =
name and, in addition, the run-time arguments passed to the operation must =
have a total marshaled size that matches the number of bytes that are expec=
ted by the unmarshaling code on the server side. In practice, this is extre=
mely rare and an incorrect uncheckedCast
typically results in =
a run-time exception.
A final warning about down-casts: you must use either a checkedCas=
t
or an uncheckedCast
to down-cast a proxy. If you use =
a C# cast, the behavior is undefined.
Another method defined by every helper class is ice_staticId<=
/code>, which returns the type ID<=
/a> string corresponding to the interface. As an example, for the Slic=
e interface
Simple
in module M
, th=
e string returned by ice_staticId
is "::M=
::Simple"
.
The base proxy class ObjectPrx
supports a variety of method=
s for customizing a proxy. Sin=
ce proxies are immutable, each of these "factory methods" returns a copy of=
the original proxy that contains the desired modification. For example, yo=
u can obtain a proxy configured with a ten second invocation timeout as sho=
wn below:
Ice.ObjectP= rx proxy =3D communicator.stringToProxy(...); proxy =3D proxy.ice_invocationTimeout(10000);
A factory method returns a new proxy object if the requested modificatio=
n differs from the current proxy, otherwise it returns the current proxy. W=
ith few exceptions, factory methods return a proxy of the same type as the =
current proxy, therefore it is generally not necessary to repeat a ch=
eckedCast
or uncheckedCast
after using a factory method=
. However, a regular cast is still required, as shown in the example below:=
Ice.ObjectP= rx base =3D communicator.stringToProxy(...); HelloPrx hello =3D HelloPrxHelper.checkedCast(base); hello =3D (HelloPrx)hello.ice_invocationTimeout(10000); # Type is preserved hello.sayHello();
The only exceptions are the factory methods ice_facet
and <=
code>ice_identity. Calls to either of these methods may produce a pr=
oxy for an object of an unrelated type, therefore they return a base proxy =
that you must subsequently down-cast to an appropriate type.
Proxies provide an =
Equals
method that compares proxies:
public inte= rface ObjectPrx { bool Equals(object r); }
Note that proxy comparison with Equals
uses all of=
the information in a proxy for the comparison. This means that not only th=
e object identity must match for a comparison to succeed, but other details=
inside the proxy, such as the protocol and endpoint information, must be t=
he same. In other words, comparison with Equals
(or =3D=
=3D
and !=3D
) tests for proxy identity, no=
t object identity. A common mistake is to write code along the followi=
ng lines:
Ice.ObjectP= rx p1 =3D ...; &nbs= p;// Get a proxy... Ice.ObjectPrx p2 =3D ...;  = ; // Get another proxy... if(!p1.Equals(p2)) { // p1 and p2 denote differ= ent objects // WRONG! } else { // p1 and p2 denote the&nb= sp;same object //= Correct }
Even though p1
and p2
differ, they may denote =
the same Ice object. This can happen because, for example, both p1 and
p2
embed the same object identity, but each use a dif=
ferent protocol to contact the target object. Similarly, the protocols may =
be the same, but denote different endpoints (because a single Ice object ca=
n be contacted via several different transport endpoints). In other words, =
if two proxies compare equal with Equals
, we know that the two=
proxies denote the same object (because they are identical in all respects=
); however, if two proxies compare unequal with Equals
, we kno=
w absolutely nothing: the proxies may or may not denote the same object.
To compare the object identities of two proxies, you can use a helper fu=
nction in the Ice.Util
class:
public = ;sealed class Util { public static int proxyIdentityCompa= re(ObjectPrx lhs, ObjectPrx rhs); public static int proxyIdentityAndFa= cetCompare(ObjectPrx lhs, ObjectPrx rhs); // ...
proxyIdentityCompare
allows you to correctly compare proxie=
s for identity:
Ice.ObjectP= rx p1 =3D ...; &nbs= p;// Get a proxy... Ice.ObjectPrx p2 =3D ...;  = ; // Get another proxy... if(Ice.Util.proxyIdentityCompare(p1, p2) !=3D 0) { // p1 and p2 denote differ= ent objects // Correct } else { // p1 and p2 denote the&nb= sp;same object //= Correct }
The function returns 0 if the identities are equal, -1
if <=
code>p1 is less than p2
, and 1 if p1
is gr=
eater than p2
. (The comparison uses name
as the m=
ajor and category
as the minor sort key.)
The proxyIdentityAndFacetCompare
function behaves similarly=
, but compares both the identity and the facet name.
The C# mapping also provides two helper classes in the Ice
=
namespace that allow you to insert proxies into hashtables or ordered colle=
ctions, based on the identity, or the identity plus the facet name:
public = ;class ProxyIdentityKey : System.Collections.IHashCodeProvider, System.Collections.IComparer { public int GetHashCode(object obj); public int Compare(object obj1, = ;object obj2); } public class ProxyIdentityFacetKey : System.Collections.IHashCodeProvider, System.Collections.IComparer { public int GetHashCode(object obj); public int Compare(object obj1, = ;object obj2); }
Note these classes derive from IHashCodeProvider
and =
IComparer
, so they can be used for both hash tables and ordered coll=
ections.