Visual Basic一大吸引人的地方是它支持直接的后期绑定。就是说可以直接在代码里访问那些要到运行时才能确定的对象成员。比如这样一段代码:
2 f.Show()
Object是没有Show方法的,但是这段代码可以正确运行。这个功能让VB有了很强的灵活性,它能够直接操作没有类型库的COM对象,而C#则很麻烦。在COM时代,VB的后期绑定是通过IDispatch实现的,而.NET时代,它是由Reflection实现的。为了揭开VB后期绑定的秘密,后面几篇就来讨论一下后期绑定的实现原理。
首先,对方法的调用是后期绑定最重要的一个环节,因为实现了后期绑定的方法调用,就能实现对属性的访问和对事件的操作,这基本上就是对象操作的全部内容。VB的编译器在发现后期绑定的调用后,会用Microsoft.VisualBasic.CompilerServices中的相关操作类实现后期绑定的方法调用。其中,LateBinding.InternalLateCall是这个操作的桥梁。我们来看看这个方法的实现。
这段代码是我从IL手工翻译来的,因为流行的反编译器都不能正确反编译VB的When语句。这段代码很可能有错,凑合着看吧:
2
3 DebuggerHidden() > _
4
5 Friend Function InternalLateCall() Function InternalLateCall( _
6
7 ByVal o As Object, _
8
9 ByVal objType As System.Type, _
10
11 ByVal name As String, _
12
13 ByVal args() As Object, _
14
15 ByVal paramnames() As String, _
16
17 ByVal CopyBack() As Boolean, _
18
19 ByVal IgnoreReturn As Boolean _
20
21) As Object
22
23
'以下变量的名字已经被我安我的理解改变过
2
3 Dim flags As System.Reflection.BindingFlags
4
5 Dim result As Object
6
7 Dim correctIReflect As System.Reflection.IReflect
8
9 Dim members() As System.Reflection.MemberInfo
10
11 Dim argNumber As Integer
12
13 Dim firstMember As System.Reflection.MemberInfo
14
15 Dim firstMethodBase As System.Reflection.MethodBase
16
17 Dim params() As System.Reflection.ParameterInfo
18
19 Dim paramsNumber As Integer
20
21 Dim paramsInfo As System.Reflection.ParameterInfo
22
23 Dim paramsAttr() As Object
24
25
26
27 If IgnoreReturn Then
28
29 flags = & H4015D Or & H1000000
30
31 Else
32
33 flags = & H4015D
34
35 End If
36
37
38
39 If objType Is Nothing Then
40
41 If o Is Nothing Then
42
43 Throw ExceptionUtils.VbMakeException( 91 )
44
45 Else
46
47 objType = o. GetType ()
48
49 End If
50
51 End If
52
53
54
55 correctIReflect = LateBinding.GetCorrectIReflect(o, objType)
56
57
58
59 If objType.IsCOMObject Then
60
61 LateBinding.CheckForClassExtendingCOMClass(objType)
62
63 End If
64
65
66
67 If name Is Nothing Then
68
69 name = " "
70
71 End If
72
73
74
75 binder = New VBBinder(CopyBack)
76
77
78
79 If Not objType.IsCOMObject() Then
80
81 ' 以下代码针对非COM对象
82
83
84
85 ' 通过Reflection获取指定名称成员
86
87 members = _
88
89 LateBinding.GetMethodsByName(correctIReflect, name, flags)
90
91
92
93 If members Is Nothing OrElse members.Length = 0 Then
94
95
96
97 Throw New System.MissingMemberException( _
98
99 Utils.GetResourceString( _
100
101 " MissingMember_MemberNotFoundOnType2 " , _
102
103 name, _
104
105 Utils.VBFriendlyName(objType, o)))
106
107
108
109 ElseIf LateBinding.MemberIsField(members) Then
110
111
112
113 Throw New System.MissingMemberException( _
114
115 Utils.GetResourceString( _
116
117 " ExpressionNotProcedure " , _
118
119 name, _
120
121 Utils.VBFriendlyName(objType, o)))
122
123
124
125 End If
126
127
128
129 If members.Length = 1 Then
130
131 ' member的长度为1,意味着只有一个重载
132
133 ' 因此下面的代码针对没有别名的情况
134
135
136
137 firstMember = members( 0 )
138
139 If firstMember.MemberType = 16 Then
140
141 ' 如果该成员是属性,则继续获取“GET”访问器的信息
142
143
144
145 firstMember = _
146
147 CType (firstMember, _
148
149 System.Reflection.PropertyInfo).GetGetMethod()
150
151
152
153 If firstMember Is Nothing Then
154
155
156
157 Throw New System.MissingMemberException( _
158
159 Utils.GetResourceString( _
160
161 " MissingMember_MemberNotFoundOnType2 " , _
162
163 name, _
164
165 Utils.VBFriendlyName(objType, o)))
166
167
168
169 End If
170
171 End If
172
173
174
175 ' 将成员确定为方法类型,准备调用
176
177 firstMethodBase = CType (firstMember, System.Reflection.MethodBase)
178
179 params = firstMethodBase.GetParameters()
180
181
182
183 argNumber = args.Length
184
185 paramsNumber = params.Length
186
187
188
189 If argNumber = paramsNumber Then
190
191 ' 没有任何缺省参数和参数数组的情况
192
193
194
195 Return LateBinding.FastCall( _
196
197 o, firstMethodBase, params, _
198
199 args, objType, correctIReflect)
200
201 Else
202
203 If CopyBack Is Nothing Then
204
205 If LateBinding.NoByrefs(params) Then
206
207 ' 没有按引用传递的参数
208
209 ' 判断最后一个参数是否是参数数组()
210
211 paramsInfo = params(paramsNumber - 1 )
212
213 If paramsInfo.ParameterType. IsArray Then
214
215 ' 通过读取自定义Attribute来判断是否参数数组
216
217 paramsAttr = paramsInfo.GetCustomAttributes( _
218
219 GetType (System.ParamArrayAttribute), False )
220
221
222
223 If Not (paramsAttr Is Nothing _
224
225 OrElse paramsAttr.Length = 0 ) Then
226
227
228
229 ' 没有参数数组,继续用FastCall
230
231 Return LateBinding.FastCall( _
232
233 o, firstMethodBase, params, _
234
235 args, objType, correctIReflect)
236
237 End If
238
239 Else
240
241 ' 没有参数数组,继续用FastCall
242
243 Return LateBinding.FastCall( _
244
245 o, firstMethodBase, params, _
246
247 args, objType, correctIReflect)
248
249 End If
250
251 End If
252
253 End If
254
255 End If
256
257 End If
258
259 End If
260
261
262
263 ' COM对象、有参数数组、有方法别名或者有ByRef参数需要回送值的情况
264
265 Try
266
267 ' 用VBBinder来进行所需的处理
268
269 result = binder.InvokeMember( _
270
271 name, flags, objType, correctIReflect, o, _
272
273 args, Nothing , Nothing , paramnames)
274
275
276
277 Exit Try
278
279 Catch misMember As System.MissingMemberException
280
281
282
283 Throw misMember
284
285
286
287 Catch ex As System.Exception When _
288
289 LateBinding.IsMissingMemberException(ex)
290
291
292
293 Throw New System.MissingMemberException( _
294
295 Utils.GetResourceString( _
296
297 " MissingMember_MemberNotFoundOnType2 " , _
298
299 name, _
300
301 Utils.VBFriendlyName(objType, o)))
302
303
304
305 Catch targetInv As _
306
307 System.Reflection.TargetInvocationException
308
309
310
311 Throw targetInv.InnerException
312
313
314
315 End Try
316
317
318
319 Return result
320
321
322
323 End Function
324
325
关键部分我加了少量注释。总的来说,这段代码首先分解了传递的参数,通过反射从对象的类型中查找所需的方法。在调用的时候,它建立了一个VBBinder类的对象,VBBinder继承自System.Reflection.Binder类,是一个管理实参和型参结合的类。除了用VBBinder调用以外,我们还能看出,如果符合下列情况,将通过另一个方法LateBinding.FastCall来调用方法:
1、不是COM对象的方法
2、方法没有别名(没有重载)
3、没有使用参数数组和可选参数
4、没有按引用传递和接受返回参数的情况
FastCall运行速度可能要比VBBinder.InvokeMember快,它忽略别名和其它高级的用法。
关于VBBinder和LateBinding的具体实现,我们下次继续讨论。
以上为转载!没有经过作者的允许。特此声明!