Slice 类映射到同名的C# 类。对于每一个Slice 数据成员,生成的类都有一个对应的public 数据成员(就像结构和异常的情况),而每一个操作都有一个对应的成员函数。考虑下面的类定义:
class TimeOfDay {
short hour; // 0 - 23
short minute; // 0 - 59
short second; // 0 -59
string format(); // Return time as hh:mm:ss
};
Slice 编译器为这个定义生成这样的代码:
public interface TimeOfDayOperations_
{
string format(Ice.Current __current);
}
public interface TimeOfDayOperationsNC_
{
string format();
}
public abstract class TimeOfDay
: Ice.ObjectImpl,
TimeOfDayOperations_,
TimeOfDayOperationsNC_
{
public short hour;
public short minute;
public short second;
public TimeOfDay()
{
}
public TimeOfDay(short hour, short minute, short second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
public string format()
{
return format(new Ice.Current());
}
public abstract string format(Ice.Current __current);
}
关于生成的代码,注意以下几点:
1. 编译器生成了叫作 TimeOfDayOperations和TimeOfDayOprationsNC_的操作接口。对于类中的每一个 Slice 操作,在操作接口中都有一个对应的方法。
2.
生成的类继承自Ice.Objet,这意味着所有的类默认继承自Ice.Object,Ice.Object是所有类的祖先,注意Ice.Object不同于Ice.ObjectPrx,也就是说,你不能在应该传递代理的地方使用class实例,反之亦然。
如果一个类只有数据成员,没有操作,则Slice2C#编译器生成一个非虚类(non_abstract class).
3. 编译器生成的类为每个slice数据成员生成了一个公共数据字段。
4. 编译器生成的类从操作接口中继承了所有成员函数。
5. 编译器生成的类拥有两个构造函数
上面只是简短的说了一下,下面让我们来详细的说明一下每个部分。
操作接口
<interface-name>Operations_功能接口中的方法都有一个附加的Ice.Curreet类型的参数,而在<interface-name>OprationsNC_功能接口中的方法则都没有这个参数。对于没有显式指定Ice.Current类型参数的方法,它其实有一个默认的Ice.Current参数。
如果一个类只有数据成员,而没有方法,那么编译器将不会生成上面所说的两个操作接口。
继承自Ice.Object
像接口一样,所有的类都隐式的继承自一个公共基类Ice.Object.,类继承自Ice.Object而不是Ice.ObjectPrx(接口继承自Ice.ObjectPrx)。所以,你不能在期望得到代码的地方指定一个类,反之亦然,这是因为他们的基类型是不兼容的。
Ice.Object的定义如下:
namespace Ice
{
public interface Object : System.ICloneable
{
int ice_hash();
bool ice_isA(string s);
bool ice_isA(string s, Current current);
void ice_ping();
void ice_ping(Current current);
string[] ice_ids();
string[] ice_ids(Current current);
string ice_id();
string ice_id(Current current);
void ice_preMarshal();
void ice_postUnmarshal();
}
}
* ice_hash
这个方法返回类的哈希键。
* ice_isA
这个方法当对象支持给定的类型ID时返回true,否则返回false.
* ice_ids
这个方法返回类在运行时的真正类型ID,
* ice_preMarshal
Ice run time在整编一个对象的状态前会调用这个方法,这就给子类提供了一个验证数据成员的机会。
* ice_postUMarshal
Ice run time在解编一个对象的状态后,会调用这个方法。如果子类希望执行附加的初始操作的话,可以重写这个方法。
注意,生成的类并没有重写GetHaseCode和Equals方法,这意味着类之间的比较是比较引用,而不是比较值。另外类还有提供了浅copy功能的Clone方法。
类的数据成员
默认情况下,类数据成员的映射就像结构和映射一样,对每一个数据成员,生成的类中都有一个对应的公共数据字段。
如果你想限制数据成员的访问,你可以用protected元指令改变数据成员的可见。Protected元指令告诉编译器,生成的类中数据成员的可见性为protectd,这意味着数据成员只能在类内部和类的子类间访问,而不能其它地方访问。例如,如下的slice定义:
class TimeOfDay {
["protected"] short hour; // 0 - 23
["protected"] short minute; // 0 - 59
["protected"] short second; // 0 - 59
string format(); // Return time as hh:mm:ss
};
上面的silce定义生成的代码如下:
public abstract class TimeOfDay
: Ice.ObjectImpl,
TimeOfDayOperations_,
TimeOfDayOperationsNC_
{
protected short hour;
protected short minute;
protected short second;
public TimeOfDay()
{
}
public TimeOfDay(short hour, short minute, short second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
// ...
}
如果一个类的所有数据成员都是protected的话,你可以将protected元指令应用在类上,而不用再分别指定每个成员。例如,我们可以重写上面的slice定义。
["protected"] class TimeOfDay {
short hour; // 0 - 23
short minute; // 0 - 59
short second; // 0 - 59
string format(); // Return time as hh:mm:ss
};
另外,如果将clr:property元指令和protected元指令结合使用,那么在生成的类中,将会出现protected可见性的属性,
类的操作
类的操作被映射到虚方法,这意味着,如果一个类有成员函数(就像上面的TimeOfDay类),你必须提供一个实现成员函数的类,而这个类继承自生成的类:
public class TimeOfDayI : TimeOfDay
{
public string format(Ice.Current current)
{
return hour.ToString("D2") + ":"
+ minute.ToString("D2") + ":"
+ second.ToString("D2");
}
}
类工厂
创建了这样的类之后,我们有了一个实现,可以实例化 TimeOfDayI类,但我们不能把它作为返回值进行接收,也不能用作某个操作调用的输出参数。要想知道为什么,考虑下面的简单接口:
interface Time {
TimeOfDay get();
};
当客户调用get操作时,Ice run time 必须实例化TimeOfDay类,返回它的一个实例。但TimeOfDay是一个抽象类,不能实例化。除非我们告诉它,否则 Ice run time 不可能魔法般地知道我们创建了一个TimeOfDayI类,实现了 TimeOfDay抽象类的 format抽象操作。换句话说,我们必须给Ice run time 提供一个工厂,这个工厂知道 TimeOfDay抽象类有一个 TimeOfDayI具体实现。Ice::Communicator接口为我们提供了所需的操作:
module Ice {
local interface ObjectFactory {
Object create(string type);
void destroy();
};
local interface Communicator {
void addObjectFactory(ObjectFactory factory, string id);
void removeObjectFactory(string id);
ObjectFactory findObjectFactory(string id);
// ...
};
};
要把我们的 TimeOfDayI类的工厂提供给 Ice run time ,我们必须实现 ObjectFactory接口:
class ObjectFactory
extends Ice.LocalObjectImpl
implements Ice.ObjectFactory {
public Ice.Object create(String type) {
if(type.equals("::TimeOfDay")) {
return new TimeOfDayI();
}
assert(false);
return null;
}
public void destroy() {
// Nothing to do
}
}
Ice run time 会在需要实例化TimeOfDay 类时调用对象工厂的create操作;并且会在工厂解除注册、或其Communicator销毁时调用工厂的 destroy操作。
create方法的参数是要实例化的类的类型ID。对于我们的TimeOfDay 类,其类型 ID 是 "::TimeOfDay"。我们的create 实现会检查类型 ID:如果是"::TimeOfDay",就实例化并返回一个TimeOfDayI对象。如果是其他类型 ID,断言就会失败,因为它不知道怎样实例化其他类型的对象。
假定我们有一个工厂实现,比如我们的 ObjectFactory,我们必须把这个工厂的存在告知Ice run time:
Ice.Communicator ic = ...;
ic.addObjectFactory(new ObjectFactory(), "::TimeOfDay");
现在,每当 Ice run time 需要实例化类型ID 是 "::TimeOfDay"的类时,它就会调用已注册的ObjectFactory 实例的create方法。
当你调用Communicator::removeObjectFactory 时,或者Communicator销毁时, Ice run time 就会调用对象工厂的destroy操作。这样,你就有了清理你的工厂使用的任何资源的机会。当工厂仍然注册在Communicator 上时,不要调用工厂的destroy——如果你这样做了,Ice run time 不知道这件事情的发生,取决于你的destroy 实现所做的事情,当Ice run time 下一次使用工厂时,这可能会导致不确定的行为。
注意,你不能针对同一个类型ID 两次注册工厂:如果你这样做了,Ice run time 就会抛出AlreadyRegisteredException。与此类似,如果你试图移 除一个并没有注册过的工厂,Ice run time 就会抛出 NotRegisteredException。
最后,要记住,如果一个类只有数据成员,没有操作,你无需为了传送这样的类的实例而创建并注册对象工厂。只有当类有操作时,你才需要定义并注册对象工厂。
从LocalObject继承
注意,前面的例子中的工厂实现扩展了 Ice.LocalObjectImpl。对象工厂是一个本地对象,从Ice.LocalObject继承了一些操作,比如 ice_hash 。Ice.LocalObjectImpl类提供了这些操作的实现,所以你无需自己实现它们。
般而言,所有的本地接口都是从 Ice.LocalObject继承的,而所有的本地接口(比如对象工厂或 servant 定位器的实现,都必须从 Ice.LocalObjectImpl继承。
类的构造函数
生成的类有两个构造函数,一个是无参的默认构造函数,另一个是将全部数据成员作为参数的构造函数,后一个构造函数允许你在声明对象时就初始化数据成员的值,比如:
TimeOfDayI tod = new TimeOfDayI(41,45, 00);
对于子类来说,有参的构造函数包括全部的数据成员,包括从基类继承来的数据成员。比如,考虑下面的Slice定义:
class TimeOfDay {
short hour; // 0 - 23
short minute; // 0 - 59
short second; // 0 - 59
};
class DateTime extends TimeOfDay {
short day; // 1 - 31
short month; // 1 - 12
short year; // 1753 onwards
};
上面的定义生成的代码如下:
public class TimeOfDay : Ice.ObjectImpl
{
public TimeOfDay() {}
public TimeOfDay(short hour, short minute, short second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
// ...
}
public class DateTime : TimeOfDay
{
public DateTime() : base() {}
public DateTime(short hour,
short minute,
short second,
short day,
short month,
short year) : base(hour, minute, second)
{
this.day = day;
this.month = month;
this.year = year;
}
// ...
}
也就是说,如果你想实例化并初始化一个DataTime类,那么你要么使用无参的默认构造函数,要么你就调用有参的构造函数,当然,这时你就得指定所有数据成员的值,包括那些从DataTime的基类继承来的数据成员。