Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Runtime type checks

Code holding a base type sometimes needs something only one derived type has. is asks, while the program runs, what an object really is.

type Message extends Object
    declare function Summary( ) as ustring
    sender as ustring
end type

function Message.Summary( ) as ustring
    return "from " & sender
end function

type TextMessage extends Message
    text as ustring
end type

type Reply extends TextMessage
    inReplyTo as int32
end type

type Picture extends Message
    w as int32
    h as int32
end type

sub Show( byref m as Message )
    print m.Summary( );
    '' is asks what the object really is, at run time. It is true for
    '' the type named and for anything derived from it.
    if m is TextMessage then
        '' Safe now: the object really is a TextMessage.
        dim as TextMessage ptr t = cast( TextMessage ptr, @m )
        print ": "; t->text;
    end if
    if m is Reply then
        print " (reply to #" & cast( Reply ptr, @m )->inReplyTo & ")";
    end if
    if m is Picture then print ": a picture";
    print
end sub

dim as TextMessage hello
hello.sender = "Ada" : hello.text = "hello"
dim as Reply answer
answer.sender = "Grace" : answer.text = "hi!" : answer.inReplyTo = 1
dim as Picture photo
photo.sender = "Alan" : photo.w = 640 : photo.h = 480

Show( hello )
Show( answer )
Show( photo )

Output

from Ada: hello
from Grace: hi! (reply to #1)
from Alan: a picture

m is TextMessage is true when the object is a TextMessage or anything derived from one, which is why the reply passes both the TextMessage and the Reply tests. Once it is true, cast( TextMessage ptr, @m ) views the same object as a TextMessage, and its text field can be read.

The cast itself checks nothing. Cast to the wrong type and the program reads whatever memory follows the base part, so always guard a cast like this with is.

is reads the answer from the hidden table pointer, so the type must extend Object, and it asks only about types derived from the variable's own type. Both are checked when the program compiles. An interface value is the exception: v is Note and v is ISize ask about the object behind v, whatever its interface (Several interfaces).

AskingRefused with
p is More, where p's type does not extend ObjectTKO0799: 'is' needs run-time type information, and 'plain' has none: it does not extend 'object'
t is TextMessage, where t is already a TextMessageTKO0798: 'is textmessage' needs a type derived from 'textmessage', and 'textmessage' is not one

Pitfall

A chain of if m is TextMessage ... elseif m is Picture ... is a select case on the type, and it has to be edited every time a new kind of message appears. When the question is "what should this object do", the answer is a virtual method, which each new type supplies for itself. Keep is for the few places that truly need a derived type's own data.

Activities

  1. Add a Voice message with a seconds field and make Show print its length.
  2. Replace the is tests with a virtual Detail( ) method that each message type overrides. Which version is easier to extend?