-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathChainedIs.qhelp
More file actions
89 lines (78 loc) · 5.13 KB
/
ChainedIs.qhelp
File metadata and controls
89 lines (78 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Long sequences of type tests are primarily used to dispatch to different bits of code based on
the type of a variable, as shown in the example later in this topic. They are often used to
simulate pattern-matching in languages that do not support it. Whilst they do work as a dispatch
method, they have a number of disadvantages:</p>
<ul>
<li>They are difficult to maintain, because it is easy to add a new subtype and forget to
modify all of the type test sequences throughout your code.</li>
<li>They introduce unwanted dependencies on concrete classes. Code that could be written only
in terms of an interface must now take account of all the special cases.</li>
<li>They can be error-prone - if you inadvertently test for a base type before a derived type,
the code that handles the derived type is never executed.</li>
<li>They can exhibit poor performance, because they effectively iterate through a list of
types in linear time, checking each one in turn.</li>
</ul>
</overview>
<recommendation>
<p>There are a number of possible solutions to the problem, depending on the exact
circumstances:</p>
<ul>
<li><strong>Polymorphism</strong>. This involves adding a virtual method to the type hierarchy
and putting the bits of code to be called in the relevant override for each concrete class. It
is a tidy solution when you can change the type hierarchy in question and the operation being
implemented is a core part of the functionality that the types in question should implement.
It is important to be careful not to introduce unwanted dependencies if you choose this
approach - if the operation depends on things that themselves depend on the type hierarchy,
then you cannot move the operation to the type hierarchy without creating a dependency cycle.
</li>
<li><strong>The visitor pattern</strong>. This involves introducing a visitor interface
containing a <code>visit</code> method for each type in the type hierarchy, and adding an
<code>accept</code> method to each type in the hierarchy that takes such a visitor as its
parameter. Each type's <code>accept</code> method calls the visitor's <code>visit</code>
method on <code>this</code>. Concrete visitors then implement the interface and do whatever is
necessary for each specific type. This is a good solution when you can change the type
hierarchy in question and the type hierarchy should not know about the operation being
implemented, either for dependency reasons or because it is not part of the core functionality
of the types in the hierarchy. It is also sensible if you want to provide multiple operations
with the same structure on the same set of types and you want the types themselves to control
the way in which the operation is structured (for example 'visit this tree using an in-order
walk and do whatever is necessary for each node'). The disadvantages are that the basic
visitor pattern is cyclically-dependent, and that the infrastructure involved is comparatively
heavyweight.</li>
<li><strong>Reflection</strong>. This involves looking up one of a set of overloaded methods
based on the type of one of the method parameters, and calling it manually. This option should
never be the first choice because it necessitates a loss of type safety and is rather untidy,
but there are times when it can be sensible. In particular, it is useful when you cannot
change the type hierarchy in question (for example because it is third-party code) and your
code must compile with versions of C# that do not support <code>dynamic</code> (pre-4.0).</li>
<li><strong>Use <code>dynamic</code></strong>. This involves converting (either explicitly or
implicitly) the type of the object to <code>dynamic</code> so as to perform a
dynamically-resolved call on it. It is only an option in C# 4.0 and later. As with reflection,
it necessitates a loss of type safety, although it is somewhat cleaner from a syntactic
perspective. It is a useful approach when you cannot change the type hierarchy in question or
you are keen to avoid a heavyweight solution like the visitor pattern (you are effectively
losing some type safety to gain some readability).</li>
</ul>
</recommendation>
<example>
<p>This example uses a series of chained <code>is</code> statements to perform a different action
depending on what kind of <code>Animal</code> is being iterated over.</p>
<sample src="ChainedIs.cs" />
<p>Polymorphism is illustrated in the example below.</p>
<sample src="ChainedIsFix1.cs" />
<p>Here is the same example again using the visitor pattern. This is a better solution if the
idea of animal noises should be separate from the idea of animals.</p>
<sample src="ChainedIsFix2.cs" />
<p>More details on reflection and the use of <code>dynamic</code> can be found in the references.
</p>
</example>
<references>
<li>J. Albahari and B. Albahari, <em>C# 4.0 in a Nutshell - The Definitive Reference</em>, Chapters 18 and 19, O'Reilly Media, 2010.</li>
<li>R. Johnson, J. Vlissides, R. Helm and E. Gamma, <em>Design Patterns: Elements of Reusable Object-Oriented Software</em>, Addison-Wesley Professional, 1994.</li>
</references>
</qhelp>