繁体   English   中英

何时使用运行时类型信息?

[英]When to use run-time type information?

如果我有各种类的子类,以及对这些子类的实例进行操作的算法,并且如果算法的行为根据实例的特定子类略有不同,那么最常用的面向对象的方法是使用虚拟方法。

例如,如果子类是DOM节点,并且算法要插入子节点,则该算法根据父节点是DOM元素(可以有子节点)还是DOM文本(不能)来区别:所以insertChildren方法可以是DomNode基类中的虚拟(或抽象),并且在每个DomElementDomText子类中实现不同。

另一种可能性是为实例提供一个公共属性,可以读取其值:例如,算法可能会读取DomNode基类的nodeType属性; 或者另一个例子,您可能有不同类型(子类)的网络数据包,它们共享一个公共数据包标头,您可以读取数据包标头以查看它是什么类型的数据包。

我没有太多使用运行时类型信息,包括:

  • C#中的isas关键字
  • 溯造型
  • 点网中的Object.GetType方法
  • C ++中的typeid运算符

当我添加一个取决于子类类型的新算法时,我倾向于在类层次结构中添加一个新的虚方法。

我的问题是,何时使用运行时类型信息而不是虚函数?

当没有别的办法时。 虚拟方法总是首选,但有时它们无法使用。 有几个原因导致这种情况发生,但最常见的原因是您没有要使用的类的源代码,或者您无法更改它们。 当您使用旧系统或使用闭源商业库时,通常会发生这种情况。

在.NET中,你可能还需要动态加载新的程序集,比如插件,你通常没有基类,但必须使用像duck typing这样的东西。

在C ++中,在其他一些不起眼的案例中(主要处理较差的设计选择),RTTI是一种实现所谓多方法的方法

这种结构(“是”和“as”)对于Delphi开发人员来说非常熟悉,因为事件处理程序通常会将对象转发给共同的祖先。 例如,事件OnClick传递唯一的argurment Sender:TObject,无论对象的类型是什么,无论是TButton,TListBox还是其他任何类型。 如果您想了解有关此对象的更多信息,则必须通过“as”访问它,但为了避免异常,您可以在之前使用“is”进行检查。 这种向下转换允许通过严格的类类型检查无法实现的对象和方法的设计类型绑定。 想象一下,如果用户单击Button或ListBox,您想要执行相同的操作,但如果它们为我们提供了不同的函数原型,则无法将它们绑定到相同的过程。

在更一般的情况下,对象可以调用通知对象例如已经改变的函数。 但事先它让目的地“亲自”(通过as和is)了解他,但不一定。 它通过将self作为所有对象的最常见祖先(Delphi案例中的TObject)来实现这一点

dynamic_cast <>,如果我没记错的话,取决于RTTI。 当一个对象被通过一个空指针传递(对于可能发生任何原因)有些晦涩外部接口也可能依赖RTTI。

话虽这么说,我在10年的专业C ++维护工作中还没有看到typeof()。 (幸运)。

对于运行时类型检查正常的情况,可以参考更有效的C#。

项3.使用运行时类型检查专门化通用算法

只需指定新类型参数,即可轻松重用泛型。 具有新类型参数的新实例化意味着具有类似功能的新类型。

这一切都很棒,因为你写的代码较少。 然而,有时更通用意味着不利用更具体但明显优越的算法。 C#语言规则考虑到了这一点。 只需要您认识到,当类型参数具有更强大的功能时,您的算法可以更高效,然后编写该特定代码。 此外,创建指定不同约束的第二个泛型类型并不总是有效。 通用实例化基于对象的编译时类型,而不是运行时类型。 如果您没有考虑到这一点,您可能会错过可能的效率。

例如,假设您编写了一个类,该类在通过IEnumerable <T>表示的项目序列上提供反向枚举。 为了向后枚举它,您可以迭代它并将项目复制到具有索引器访问权限的中间集合(如List <T>),然后使用索引器访问向后枚举该集合。 但是如果你的原始IEnumerable是IList,为什么不利用它并提供更高效的方式(不复制到中间集合)来向后迭代项目。 所以基本上它是一个特殊的我们可以利用但仍然提供相同的行为(迭代序列向后)。

但一般来说,您应该仔细考虑运行时类型检查,并确保它不违反Liskov Substituion Principle。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM