Visual Basic一大吸引人的地方是它支持直接的后期绑定。就是说可以直接在代码里访问那些要到运行时才能确定的对象成员。比如这样一段代码:
1
Dim f As Object = New Form2()
2
f.Show()
Object是没有Show方法的,但是这段代码可以正确运行。这个功能让VB有了很强的灵活性,它能够直接操作没有类型库的COM对象,而C#则很麻烦。在COM时代,VB的后期绑定是通过IDispatch实现的,而.NET时代,它是由Reflection实现的。为了揭开VB后期绑定的秘密,后面几篇就来讨论一下后期绑定的实现原理。
首先,对方法的调用是后期绑定最重要的一个环节,因为实现了后期绑定的方法调用,就能实现对属性的访问和对事件的操作,这基本上就是对象操作的全部内容。VB的编译器在发现后期绑定的调用后,会用Microsoft.VisualBasic.CompilerServices中的相关操作类实现后期绑定的方法调用。其中,LateBinding.InternalLateCall是这个操作的桥梁。我们来看看这个方法的实现。
这段代码是我从IL手工翻译来的,因为流行的反编译器都不能正确反编译VB的When语句。这段代码很可能有错,凑合着看吧:
1
<DebuggerStepThrough(), _
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![]()
'以下变量的名字已经被我安我的理解改变过
1
Dim binder As Microsoft.VisualBasic.CompilerServices.VBBinder
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的具体实现,我们下次继续讨论。
以上为转载!没有经过作者的允许。特此声明!