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



    在客户端, 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);

    }

  • 相关阅读:
    从yield关键字看IEnumerable和Collection的区别
    Windows Azure Developer Guidance Map(含PDF下载)
    关于CLR内存管理一些深层次的讨论[上篇]
    关于CLR内存管理一些深层次的讨论[下篇]
    当你的博客文章的作者变成“编辑整理”,你作何感想?
    我看周马,以及3Q大战背后的社会问题
    一个完整的用于追踪数据改变的解决方案
    与VS集成的若干种代码生成解决方案[博文汇总(共8篇)]
    如果在BackgroundWorker运行过程中关闭窗体…
    采用一个自创的"验证框架"实现对数据实体的验证[扩展篇]
  • 原文地址:https://www.cnblogs.com/zhangronghua/p/1196958.html
Copyright © 2020-2023  润新知