IMZRH的日志

努力成为一个有用的人

导航

ICE框架之Slice2CSharp映射---接口的映射

Posted on 2008-05-14 16:49  张荣华  阅读(3515)  评论(0编辑  收藏  举报


在客户端, Slice 映射到C# 接口,后者的成员函数与前者的操作相对应。考虑下面的简单接口:

interface Simple {

void op();

};

Slice 编译器生成以下定义,供客户使用:

public interface SimplePrx : Ice.ObjectPrx {

void op();

void op(Ice.Context __context);

}

你可以看到,编译器生成了一个代理接口 SimplePrx。一般而言,生成的代理接口的名字是 <interface-name>Prx。如果接口是嵌在模块M 中的,生成的类就是M命名空间的一部分,所以受到完全限定的名字是M.<interface-name>Prx。

在客户的地址空间中, SimplePrx的实例是 “远地的服务器中的Simple接口的实例”的 “本地大使”,叫作代理实例。与服务器端对象有关的所有细节,比如其地址、所用协议、对象标识,都封装在该实例中。

注意,SimplePrx继承自 Ice.ObjectPrx。这反映了这样一个事实:所有的Ice 接口都隐式地继承自 Ice::Object。

对于接口中的每个操作,代理类都有一个同名的成员函数。就前面的例子而言,我们会发现操作op 已经被映射到成员函数op。还要注意,op是重载的:op的第二个版本有一个参数__context,类型是Ice.Context。Ice run time 用这个参数存储关于请求的递送方式的信息;你通常并不需要使用它 .

因为所有的<interface-name>Prx类型都是接口,你不能实例化这种类型的对象。相反,代理实例总是由 Ice run time 替客户实例化,所以客户代码永远都不需要直接实例化代理。Ice run time 给出的代理引用的类型总是 <interface-name>Prx;这种接口的具体实现是Ice run time 的一部分,与应用代码无关。

Null值说明这是一个空代理,空代理用来指示一个“不在这里”的代理,既没有这个对象。

Ice.ObjectPrx接口

所有Ice 对象的最终祖先都是Object,所以所有代理都继承自Ice.ObjectPrx。ObjectPrx提供了一些方法:

Namespace Ice;

public interface ObjectPrx {

Identity ice_getIdentity();

int ice_hash();

bool ice_isA(string id);

string ice_id();

void ice_ping();

int GetHashCode();

bool Equals(object r);

// Defined in a helper class:

//

public static bool Equals(Ice.ObjectPrx lhs,

Ice.ObjectPrx rhs);

public static bool operator==(ObjectPrx lhs,

ObjectPrx rhs);

public static bool operator!=(ObjectPrx lhs,

ObjectPrx rhs);

// ...

}

}

注意,static类型的方法并不是在Ice.ObjectPrx中定义的,而是在一个会变成代理实例基类的helper类中定义的。然而,这部分工作由C# Mapping进行,在概念上,这些静态方法仍属于Ice.ObjectPrx,所以仍把它们放在这里讨论。

这些方法的行为如下:

* ice_getIdentity

这个方法返回的是代理所代表的对象的标识。Ice 对象标识的 Slice类型是:

module Ice {

struct Identity {

string name;

string category;

};

};

想知道两个代理代表的是否是同一个对象,首先获取每个对象的标识,然后对其进行比较:

Ice.ObjectPrx o1 = ...;

Ice.ObjectPrx o2 = ...;

Ice.Identity i1 = o1.ice_getidentity();

Ice.Identity i2 = o2.ice_getidentity();

if (i1.equals(i2))

// o1 and o2 denote the same object

else

// o1 and o2 denote different objects

* ice_hash

这个方法返回代理的哈希键。(和GetHashCode完成一样的功能)

* ice_isA

这个方法确定代理所代表的对象是否支持特定接口。 ice_isA的参数是一个类型ID。例如,要想知道一个ObjectPrx 类型的代理代表的是否是 Printer对象,我们可以编写:

Ice.ObjectPrx o = ...;

if (o != null && o.ice_isA("::Printer"))

// o denotes a Printer object

else

// o denotes some other type of object

注意,在调用 ice_isA方法之前,我们先测试了代理是否为null。这样,如果代理为null,就不会引发NullPointerException 了。

* ice_id

这个方法返回代理所代表的对象的类型ID。注意,返回的类型是实际对象的类型,其派生层次可能比代理的静态类型更深。例如,如果我们有一个 BasePrx类型的代理,其静态的类型ID 是 ::Base, ice_id的返回值可能会是 ::Base,也可能是派生层次更深的对象的类型ID,比如 ::Derived。

* ice_ping

这个方法为对象提供了基本的可到达测试。如果对象可以从物理上联系到 (也就是说,对象存在,它的服务器在运行,并且可到达),调用就会正常完成;否则,它就会抛出异常,说明对象为何不能到达,比如 ObjectNotExistException or ConnectTimeoutException。

* equals

这个操作比较两个代理是否相等。注意,这个操作会比较代理的所有方面,比如代理的通信端点。这意味着,一般而言,如果两个代理不相等,那并不说明它们代表的是不同的对象。例如,如果两个代理代表的是同一个Ice 对象,但所用传输端点不同,那么即使它们代表的是同一个对象, equals也会返回 false。

注意,ObjectPrx还有另外一些方法,在此没有给出。这些方法提供了不同的调用分派方式,同时还可以访问对象的facets.

代理助手

对于每个 Slice 接口,除了代理接口以外, Slice2C#编译器还会创建一个助手类:对于Simple接口,所生成的助手类的名字是SimplePrxHelper。助手类含有两个有意思的方法:

public class SimplePrxHelper: Ice.ObjectPrxHelper implements SimplePrx {

public static SimplePrx checkedCast(Ice.ObjectPrx b);

public static SimplePrx checkedCast(Ice.ObjectPrx b,

Ice.Context ctx);

public static SimplePrx uncheckedCast(Ice.ObjectPrx b)

// ...

}

checkedCast和uncheckedCast方法实现的都是向下转换:如果传入的代理是 Simple类型的对象的代理,或者是Simple 的派生类型的对象的代理,这两个方法就会返回一个非null 引用,指向的是一个SimplePrx类型的代理;而如果传入的代理代表的是其他类型的对象 (或者传入的代理为null ),返回的就是null 引用。

对于任何类型的代理,你都可以 checkedCast来确定其对应的对象是否支持指定的类型,例如:

Ice.ObjectPrx obj = ...; // Get a proxy from somewhere...

SimplePrx simple = SimplePrxHelper.checkedCast(obj);

if (simple != null)

// Object supports the Simple interface...

else

// Object is not of type Simple...

注意,checkedCast会联系服务器。这是必要的,因为只有服务器情况中的代理实现确切地知道某个对象的类型。所以,checkedCast可能会抛出 ConnectTimeoutException或ObjectNotExistException(这也解释了为何需要助手类:Ice run time 必须联系服务器,所以我们不能使用C#的向下转换)。

与此相反,uncheckedCast 不会联系服务器,而是会无条件地返回具有所请求的类型的代理 。但是,如果你要使用uncheckedCast,你必须确定这个代理真的支持你想要转换到的类型;而如果你弄错了,你很可能会在调用代理上的操作时,引发运行时异常。对于这样的类型失配,最后可能会引发 OperationNotExistException,但也有可能引发其他异常,比如整编异常。而且,如果对象碰巧有一个同名的操作,但参数类型不同,则有可能根本不产生异常,你最后就会把调用发送给类型错误的对象;这个对象可能会做出非常糟糕的事情。为了说明这种情况,考虑下面的两个接口:

interface Process {

void launch(int stackSize, int dataSize);

};

// ...

interface Rocket {

void launch(float xCoord, float yCoord);

};

假定你期望收到的是 Process对象的代理,并且要用 unchecedCast对这个代理进行向下转换:

Ice.ObjectPrx obj = ...; // Get proxy...

ProcessPrx process= ProcessPrxHelper::uncheckedCast(obj); // No worries...

process.launch(40, 60); // Oops...

如果你收到的代理实际上代表 Rocket对象, Ice run time 无法检测到这个错误:因为int和float 的尺寸相同,而Ice 协议在线路上没有标记数据的类型,于是Rocket::launch 的实现就会误把传入的整数当作浮点数。

公平地说,这个例子是人为制造的。要让这样的错误在运行时不被注意到,两个对象都必须有一个同名的操作,而且,传给操作的运行时参数整编后的总尺寸,必须与服务器端的解编代码所期望的字节数相吻合。在实践中,这相当罕见,错误的uncheckedCast通常会导致运行时异常。

关于向下转换的最后一个警告:你必须使用checkedCast或uncheckedCast对代理进行向下转换。如果你使用了C#的强制转换,其行为是不确定的。

对象标识和代理比较

就你上面提到的,代理类提供了Equals方法,Equals方法会比较代码的所有信息,这意味着,不光对象标识符要一样,其它的一些方面也比较一样,比如协议和传输端点。另外,Equals( 以及 == 和!=)比较代理标识符,而不是对象标识符。一个常见的错误是像下面这样写代码:

Ice.ObjectPrx p1 = ...; // Get a proxy...

Ice.ObjectPrx p2 = ...; // Get another proxy...

if (p1.Equals(p2)) {

// p1 and p2 denote different objects // WRONG!

} else {

// p1 and p2 denote the same object // Correct

}

尽管p1和p2是不同的,但它们有可能代理同一个ice对象。这是因为,有可能p1,p2的对象标识符相同,但它们使用了不同的协议去连接目标对象。同样,有可能p1,p2使用了相同的协议,但是代理了不同的对象。(这是因为可以使用几个传输端点连接同一个Ice对象)。换名话说,如果我们用Equals方法判定两个代理相等,我们知道它们代理的是一个对象,因为它们所有的方面都相同。但是如果用Equals方法判定两个代理不相等,我们什么也不知道,它们有可能代理同一个对象,也有可能不是。

因此,比较两个代理的对象标识符,我们必须借助于Ice.Util类的方法:

public sealed class Util {

public static int proxyIdentityCompare(ObjectPrx lhs,

ObjectPrx rhs);

public static int proxyIdentityAndFacetCompare(ObjectPrx lhs,
ObjectPrx rhs);

// ...

proxyIdentityCompare方法允许你正确的进行代理标识符的比较:

Ice.ObjectPrx p1 = ...; // Get a proxy...

Ice.ObjectPrx p2 = ...; // Get another proxy...

if (Ice.Util.proxyIdentityCompare(p1, p2) != 0) {

// p1 and p2 denote different objects // Correct

} else {

// p1 and p2 denote the same object // Correct

}

如果两个代码的标识符是相等的,则方法返回0,如果p1<p2返回-1。如果p1>p2则返回1。

proxyIdentityAndFacetCompare方法完成相似的功能,但是它在比较标识符的同时,还会比较facet。

C#映射还在Ice命名空间下提供了两个帮助类,它们主要根据标识符或是标识符+facet将一个代理类插入到hashtable中或可排序的集合中。

public class ProxyIdentityKey : System.Collections.IHashCodeProvider,

ystem.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);

}