简体   繁体   English

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

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

If I have various subclasses of something, and an algorithm which operates on instances of those subclasses, and if the behaviour of the algorithm varies slightly depending on what particular subclass an instance is, then the most usual object-oriented way to do this is using virtual methods. 如果我有各种类的子类,以及对这些子类的实例进行操作的算法,并且如果算法的行为根据实例的特定子类略有不同,那么最常用的面向对象的方法是使用虚拟方法。

For example if the subclasses are DOM nodes, and if the algorithm is to insert a child node, that algorithm differs depending on whether the parent node is a DOM element (which can have children) or DOM text (which can't): and so the insertChildren method may be virtual (or abstract) in the DomNode base class, and implemented differently in each of the DomElement and DomText subclasses. 例如,如果子类是DOM节点,并且算法要插入子节点,则该算法根据父节点是DOM元素(可以有子节点)还是DOM文本(不能)来区别:所以insertChildren方法可以是DomNode基类中的虚拟(或抽象),并且在每个DomElementDomText子类中实现不同。

Another possibility is give the instances a common property, whose value can be read: for example the algorithm might read the nodeType property of the DomNode base class; 另一种可能性是为实例提供一个公共属性,可以读取其值:例如,算法可能会读取DomNode基类的nodeType属性; or for another example, you might have different types (subclasses) of network packet, which share a common packet header, and you can read the packet header to see what type of packet it is. 或者另一个例子,您可能有不同类型(子类)的网络数据包,它们共享一个公共数据包标头,您可以读取数据包标头以查看它是什么类型的数据包。

I haven't used run-time-type information much, including: 我没有太多使用运行时类型信息,包括:

  • The is and as keywords in C# C#中的isas关键字
  • Downcasting 溯造型
  • The Object.GetType method in dot net 点网中的Object.GetType方法
  • The typeid operator in C++ C ++中的typeid运算符

When I'm adding a new algorithm which depends on the type of subclass, I tend instead to add a new virtual method to the class hierarchy. 当我添加一个取决于子类类型的新算法时,我倾向于在类层次结构中添加一个新的虚方法。

My question is, when is it appropriate to use run-time-type information, instead of virtual functions? 我的问题是,何时使用运行时类型信息而不是虚函数?

When there's no other way around. 当没有别的办法时。 Virtual methods are always preferred but sometimes they just can't be used. 虚拟方法总是首选,但有时它们无法使用。 There's couple of reasons why this could happen but most common one is that you don't have source code of classes you want to work with or you can't change them. 有几个原因导致这种情况发生,但最常见的原因是您没有要使用的类的源代码,或者您无法更改它们。 This often happens when you work with legacy system or with closed source commercial library. 当您使用旧系统或使用闭源商业库时,通常会发生这种情况。

In .NET it might also happens that you have to load new assemblies on the fly, like plugins and you generally have no base classes but have to use something like duck typing. 在.NET中,你可能还需要动态加载新的程序集,比如插件,你通常没有基类,但必须使用像duck typing这样的东西。

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

This constructions ("is" and "as") are very familiar for Delphi developers since event handlers usually downcast objects to a common ancestor. 这种结构(“是”和“as”)对于Delphi开发人员来说非常熟悉,因为事件处理程序通常会将对象转发给共同的祖先。 For example event OnClick passes the only argurment Sender: TObject regardless of the type of the object, whether it is TButton, TListBox or any other. 例如,事件OnClick传递唯一的argurment Sender:TObject,无论对象的类型是什么,无论是TButton,TListBox还是其他任何类型。 If you want to know something more about this object you have to access it through "as", but in order to avoid an exception, you can check it with "is" before. 如果您想了解有关此对象的更多信息,则必须通过“as”访问它,但为了避免异常,您可以在之前使用“is”进行检查。 This downcasting allows design-type binding of objects and methods that could not be possible with strict class type checking. 这种向下转换允许通过严格的类类型检查无法实现的对象和方法的设计类型绑定。 Imagine you want to do the same thing if the user clicks Button or ListBox, but if they provide us with different prototypes of functions, it could not be possible to bind them to the same procedure. 想象一下,如果用户单击Button或ListBox,您想要执行相同的操作,但如果它们为我们提供了不同的函数原型,则无法将它们绑定到相同的过程。

In more general case, an object can call a function that notifies that the object for example has changed. 在更一般的情况下,对象可以调用通知对象例如已经改变的函数。 But in advance it leaves the destination the possibility to know him "personally" (through as and is), but not necessarily. 但事先它让目的地“亲自”(通过as和is)了解他,但不一定。 It does this by passing self as a most common ancestor of all objects (TObject in Delphi case) 它通过将self作为所有对象的最常见祖先(Delphi案例中的TObject)来实现这一点

dynamic_cast<>, if I remember correctly, is depending on RTTI. dynamic_cast <>,如果我没记错的话,取决于RTTI。 Some obscure outer interfaces might also rely on RTTI when an object is passed through a void pointer (for whatever reason that might happen). 当一个对象被通过一个空指针传递(对于可能发生任何原因)有些晦涩外部接口也可能依赖RTTI。

That being said, I haven't seen typeof() in the wild in 10 years of pro C++ maintenance work. 话虽这么说,我在10年的专业C ++维护工作中还没有看到typeof()。 (Luckily.) (幸运)。

You can refer to More Effective C# for a case where run-time type checking is OK. 对于运行时类型检查正常的情况,可以参考更有效的C#。

Item 3. Specialize Generic Algorithms Using Runtime Type Checking 项3.使用运行时类型检查专门化通用算法

You can easily reuse generics by simply specifying new type parameters. 只需指定新类型参数,即可轻松重用泛型。 A new instantiation with new type parameters means a new type having similar functionality. 具有新类型参数的新实例化意味着具有类似功能的新类型。

All this is great, because you write less code. 这一切都很棒,因为你写的代码较少。 However, sometimes being more generic means not taking advantage of a more specific, but clearly superior, algorithm. 然而,有时更通用意味着不利用更具体但明显优越的算法。 The C# language rules take this into account. C#语言规则考虑到了这一点。 All it takes is for you to recognize that your algorithm can be more efficient when the type parameters have greater capabilities, and then to write that specific code. 只需要您认识到,当类型参数具有更强大的功能时,您的算法可以更高效,然后编写该特定代码。 Furthermore, creating a second generic type that specifies different constraints doesn't always work. 此外,创建指定不同约束的第二个泛型类型并不总是有效。 Generic instantiations are based on the compile-time type of an object, and not the runtime type. 通用实例化基于对象的编译时类型,而不是运行时类型。 If you fail to take that into account, you can miss possible efficiencies. 如果您没有考虑到这一点,您可能会错过可能的效率。

For example, suppose you write a class that provides a reverse-order enumeration on a sequence of items represented through IEnumerable<T>. 例如,假设您编写了一个类,该类在通过IEnumerable <T>表示的项目序列上提供反向枚举。 In order to enumerate it backwards you may iterate it and copy items into an intermediate collection with indexer access like List<T> and than enumerate that collection using indexer access backwards. 为了向后枚举它,您可以迭代它并将项目复制到具有索引器访问权限的中间集合(如List <T>),然后使用索引器访问向后枚举该集合。 But if your original IEnumerable is IList why not take advantage of it and provide more performant way (without copying to intermediate collection) to iterate items backwards. 但是如果你的原始IEnumerable是IList,为什么不利用它并提供更高效的方式(不复制到中间集合)来向后迭代项目。 So basically it is a special we can take advantage of but still providing the same behavior (iterating sequence backwards). 所以基本上它是一个特殊的我们可以利用但仍然提供相同的行为(迭代序列向后)。

But in general you should carefully consider run-time type checking and ensure that it doesn't violate Liskov Substituion Principle. 但一般来说,您应该仔细考虑运行时类型检查,并确保它不违反Liskov Substituion Principle。

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

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