Author: William Egge
If you are new to interfaces and you are use to type casting and type checking with
objects you will find that Interfaces must be dealt with differently. This artical
shows you the basics to get started with doing type checking and type casting with
Interfaces.
Answer:
If you are like me, you are impatient and can get the point without a bunch of
explaining and you hate reading of bunch of stuff just to get a couple bits of
info. So for you guys/girls here is the summary and then if you like you can read
the rest of the artical:
SUMMARY
First off, your interface must have a GUID, use delphi Shift-Ctrl-G to create one.
It should be entered as the first line in your interface definition, ex:
1 ITypeX = interface
2 ['{A002AF60-5684-11D5-B4F9-525405F6BE8D}']
3 procedure ShowX;
4 end;
5
6 //Object Type Cast
7
8 Customer := TCustomer(SomeObject);
9
10 //Interface Way
11
12 //You cannot do it this way with interfaces
13
14 //Object Type Cast (with type checking)
15
16 Customer := SomeObject as TCustomer;
Interface Way
Customer := SomeInterface as ICustomer; // assuming Customer is declared as
ICustomer
Object Type Checking
17 if SomeObject is TCustomer then
18 // Do Something
interface Way
19
20 if SomeInterface.QueryInterface(ICustomer, Customer) = S_OK then
21 begin
22 Customer.DoSomething;
23 end;
Read further if you like more explaining
First I will summarize type casting and type checking with objects to get us on the
same ground and then show how it is done when using interfaces.
Type casting with objects is done in 2 ways
Direct without type checking
Customer := TCustomer(SomeObject);
Using "built in" type checking
Customer := SomeObject as TCustomer
The second will raise an exception if SomeObject is not a TCustomer object or a
descendent of one.
Type checking with objects can be done as....
24 if (SomeObject is TCustomer) then
25 // Do something
Now for Interfaces, you MUST have a GUID for your interface before you can do any
type checking or type casting. To make your interface have a GUID simply insert it
as the first line in your interface definition like this:
26 ITypeX = interface
27 ['{A002AF60-5684-11D5-B4F9-525405F6BE8D}']
28 procedure ShowX;
29 end;
First off you may ask where the heck do I get a GUID, in Delphi just put your
cursor where you want the GUID and then press Shift-Ctrl-G and Delphi will insert
one for you. That easy.
These are the interfaces I will use in my explaining:
30 ITypeX = interface
31 ['{A002AF60-5684-11D5-B4F9-525405F6BE8D}']
32 procedure ShowX;
33 end;
34
35 ITypeY = interface
36 ['{56DA8BE0-5685-11D5-B4F9-525405F6BE8D}']
37 procedure ShowY;
38 end;
39
40 Classes to implement them
41 TTypeX = class(TInterfacedObject, ITypeX)
42 public
43 procedure ShowX;
44 end;
45
46 TTypeY = class(TInterfacedObject, ITypeY)
47 public
48 procedure ShowY;
49 end;
50
51 TTypeXY = class(TInterfacedObject, ITypeX, ITypeY)
52 public
53 procedure ShowX;
54 procedure ShowY;
55 end;
56
57 //Type casting.
58
59 //The following code will not work!
60
61 procedure TForm_Interfaces.TypeCastXtoY;
62 var
63 X: ITypeX;
64 begin
65 X := TTypeXY.Create;
66 ITypeY(X).ShowY;
67 end;
What happens is that the method ShowX gets called rather than ShowY, I do not know
the technical reason... but bottom line, it does not work and you should not type
cast this way.
The proper way to do it is this way:
68 procedure TForm_Interfaces.TypeCastXtoY;
69 var
70 X: ITypeX;
71 begin
72 X := TTypeXY.Create;
73 (X as ITypeY).ShowY;
74 end;
You must use the as operator, also this way of doing it will raise an exception if
X in some way does not implement Y. If you need to do type checking then read
more...
The "is" operator does not work with interfaces, so code like this will not compile
75 if X is ITypeY then
76 // Do Something
77
78 //The proper way to do it is this way:
79
80 U := TTypeY.Create;
81
82 if U.QueryInterface(ITypeX, Ret) = S_OK then
83 ShowMessage('Supports ITypeX')
84 else
85 ShowMessage('DOES NOT Support ITypeX');
86
87 if U.QueryInterface(ITypeY, Ret) = S_OK then
88 ShowMessage('Supports ITypeY')
89 else
90 ShowMessage('DOES NOT Support ITypeY')
Ret will contain a reference to the interface when the result is S_OK.
Note: If you read the Delphi help for QueryInterface you will see this line
After successfully obtaining an interface by calling QueryInterface, clients should
increase the reference count by calling the IUnknown AddRef method.
I wrote code to verify this statement and found that the reference count was
incermented and I did not have to call AddRef.
Full Source of my Research, you can also download the app from my website using the
component link.
PAS
91 unit Frm_Interfaces;
92 {
93 Discovery Typecasting interfaces
94
95 You cannot typecast an interface in this manner,
96 seems your method pointers are messed up
97 X:= ITypeX(SomeInterface);
98 // See "TypeCastXtoY" method to see the error, notice the message
99
100 You must type cast in either of 2 ways
101 1. X:= SomeInterface as ITypeX
102 // See "TypeCastXtoY" method, this can raise an exception
103
104 or
105 2. if SomeInterface.QueryInterface(ITypeX, X) = S_OK then
106 X.ShowX;
107 // See "TypeChecking" method, this does not raise an exception
108
109 Difference between 1 and 2 is that 1 will raise an exception and 2 will not.
110
111 *** IMPORTANT ***
112 In order for this to work you MUST include a GUID in your interface, it is
113 the first line in the interface definition, use keyboard Shift-Ctrl-G to
114 create a GUID.
115 }
116
117 interface
118
119 uses
120 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
121 StdCtrls;
122
123 type
124
125 ITypeX = interface
126 ['{A002AF60-5684-11D5-B4F9-525405F6BE8D}']
127 procedure ShowX;
128 end;
129
130 ITypeY = interface
131 ['{56DA8BE0-5685-11D5-B4F9-525405F6BE8D}']
132 procedure ShowY;
133 end;
134
135 TTypeX = class(TInterfacedObject, ITypeX)
136 public
137 procedure ShowX;
138 end;
139
140 TTypeY = class(TInterfacedObject, ITypeY)
141 public
142 procedure ShowY;
143 end;
144
145 TTypeXY = class(TInterfacedObject, ITypeX, ITypeY)
146 public
147 procedure ShowX;
148 procedure ShowY;
149 end;
150
151 TForm_Interfaces = class(TForm)
152 FbtnTestCreate: TButton;
153 FbtnTypeCastX2Y: TButton;
154 FbtnTypeCastU2Y: TButton;
155 FbtnTypeChecking: TButton;
156 procedure Ev_FbtnTestCreateClick(Sender: TObject);
157 procedure Ev_FbtnTypeCastX2YClick(Sender: TObject);
158 procedure Ev_FbtnTypeCastU2YClick(Sender: TObject);
159 procedure Ev_FbtnTypeCheckingClick(Sender: TObject);
160 private
161 { Private declarations }
162 procedure TestCreate;
163 procedure TypeCastXtoY;
164 procedure TypeCastUnknownToY;
165 procedure TypeChecking;
166 public
167 { Public declarations }
168 end;
169
170 var
171 Form_Interfaces: TForm_Interfaces;
172
173 implementation
174
175 {$R *.DFM}
176
177 { TTypeX }
178
179 procedure TTypeX.ShowX;
180 begin
181 ShowMessage('TTypeX.ShowX: Supports "ITypeX" only');
182 end;
183
184 { TTypeY }
185
186 procedure TTypeY.ShowY;
187 begin
188 ShowMessage('TTypeY.ShowY: Supports "ITypeY" only');
189 end;
190
191 { TTypeXY }
192
193 procedure TTypeXY.ShowX;
194 begin
195 ShowMessage('TTypeXY.ShowX: Supports both "ITypeX" and "ITypeY"');
196 end;
197
198 procedure TTypeXY.ShowY;
199 begin
200 ShowMessage('TTypeXY.ShowY: Supports both "ITypeX" and "ITypeY"');
201 end;
202
203 { TForm1 }
204
205 procedure TForm_Interfaces.Ev_FbtnTestCreateClick(Sender: TObject);
206 begin
207 TestCreate;
208 end;
209
210 procedure TForm_Interfaces.Ev_FbtnTypeCastX2YClick(Sender: TObject);
211 begin
212 TypeCastXtoY;
213 end;
214
215 procedure TForm_Interfaces.Ev_FbtnTypeCastU2YClick(Sender: TObject);
216 begin
217 TypeCastUnknownToY;
218 end;
219
220 procedure TForm_Interfaces.Ev_FbtnTypeCheckingClick(Sender: TObject);
221 begin
222 TypeChecking;
223 end;
224
225 procedure TForm_Interfaces.TestCreate;
226 var
227 TypeX: ITypeX;
228 TypeY: ITypeY;
229 begin
230 TypeX := TTypeX.Create;
231 TypeX.ShowX;
232
233 TypeY := TTypeY.Create;
234 TypeY.ShowY;
235
236 // Implements both
237 TypeX := TTypeXY.Create;
238 TypeX.ShowX;
239
240 TypeY := TTypeXY.Create;
241 TypeY.ShowY;
242 end;
243
244 procedure TForm_Interfaces.TypeCastXtoY;
245 var
246 X: ITypeX;
247 begin
248 // Notice the message, this does not work.
249 X := TTypeXY.Create;
250 ITypeY(X).ShowY;
251
252 // This does work
253 (X as ITypeY).ShowY;
254 end;
255
256 procedure TForm_Interfaces.TypeCastUnknownToY;
257 var
258 U: IUnknown;
259 Y: ITypeY;
260 begin
261 U := TTypeXY.Create;
262 Y := U as ITypeY;
263 Y.ShowY;
264 end;
265
266 procedure TForm_Interfaces.TypeChecking;
267 var
268 U: IUnknown;
269 Ret: IUnknown;
270 begin
271 // Select whichever you want below
272 //********************************
273 // U:= TTypeXY.Create;
274 // U:= TTypeX.Create;
275 U := TTypeY.Create;
276 //********************************
277
278 // "Is" does not work with interfaces.
279 {
280 if U is ITypeX then
281 ShowMessage('Supports ITypeX')
282 else
283 ShowMessage('DOES NOT Support ITypeX')
284
285 if U is ITypeY then
286 ShowMessage('Supports ITypeY')
287 else
288 ShowMessage('DOES NOT Support ITypeY')
289 }
290
291 if U.QueryInterface(ITypeX, Ret) = S_OK then
292 ShowMessage('Supports ITypeX')
293 else
294 ShowMessage('DOES NOT Support ITypeX');
295
296 if U.QueryInterface(ITypeY, Ret) = S_OK then
297 ShowMessage('Supports ITypeY')
298 else
299 ShowMessage('DOES NOT Support ITypeY')
300 end;
301
302 end.
DFM
object Form_Interfaces: TForm_Interfaces
Left = 273
Top = 278
BorderStyle = bsDialog
Caption = 'Experimenting with Typecasting interfaces'
ClientHeight = 222
ClientWidth = 341
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
PixelsPerInch = 96
TextHeight = 13
object FbtnTestCreate: TButton
Left = 118
Top = 39
Width = 105
Height = 25
Caption = 'FbtnTestCreate'
TabOrder = 0
OnClick = Ev_FbtnTestCreateClick
end
object FbtnTypeCastX2Y: TButton
Left = 118
Top = 79
Width = 105
Height = 25
Caption = 'FbtnTypeCastX2Y'
TabOrder = 1
OnClick = Ev_FbtnTypeCastX2YClick
end
object FbtnTypeCastU2Y: TButton
Left = 118
Top = 119
Width = 105
Height = 25
Caption = 'FbtnTypeCastU2Y'
TabOrder = 2
OnClick = Ev_FbtnTypeCastU2YClick
end
object FbtnTypeChecking: TButton
Left = 118
Top = 159
Width = 105
Height = 25
Caption = 'FbtnTypeChecking'
TabOrder = 3
OnClick = Ev_FbtnTypeCheckingClick
end
end
Component Download: http://www.eggcentric.com/InterfaceTypeCast.zip
|