Visual Basic一大吸引人的地方是它支持直接的后期绑定。就是说可以直接在代码里访问那些要到运行时才能确定的对象成员。比如这样一段代码:
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语句。这段代码很可能有错,凑合着看吧:
<DebuggerStepThrough(), _2

3
DebuggerHidden()> _4

5
Friend 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 Object22

23

'以下变量的名字已经被我安我的理解改变过
Dim binder As Microsoft.VisualBasic.CompilerServices.VBBinder2

3
Dim flags As System.Reflection.BindingFlags4

5
Dim result As Object6

7
Dim correctIReflect As System.Reflection.IReflect8

9
Dim members() As System.Reflection.MemberInfo10

11
Dim argNumber As Integer12

13
Dim firstMember As System.Reflection.MemberInfo14

15
Dim firstMethodBase As System.Reflection.MethodBase16

17
Dim params() As System.Reflection.ParameterInfo18

19
Dim paramsNumber As Integer20

21
Dim paramsInfo As System.Reflection.ParameterInfo22

23
Dim paramsAttr() As Object24

25
26

27
If IgnoreReturn Then28

29
flags = &H4015D Or &H100000030

31
Else32

33
flags = &H4015D34

35
End If36

37
38

39
If objType Is Nothing Then40

41
If o Is Nothing Then42

43
Throw ExceptionUtils.VbMakeException(91)44

45
Else46

47
objType = o.GetType()48

49
End If50

51
End If52

53
54

55
correctIReflect = LateBinding.GetCorrectIReflect(o, objType)56

57
58

59
If objType.IsCOMObject Then60

61
LateBinding.CheckForClassExtendingCOMClass(objType)62

63
End If64

65
66

67
If name Is Nothing Then68

69
name = ""70

71
End If72

73
74

75
binder = New VBBinder(CopyBack)76

77
78

79
If Not objType.IsCOMObject() Then80

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 Then94

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) Then110

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 If126

127
128

129
If members.Length = 1 Then130

131
'member的长度为1,意味着只有一个重载132

133
'因此下面的代码针对没有别名的情况134

135
136

137
firstMember = members(0)138

139
If firstMember.MemberType = 16 Then140

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 Then154

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 If170

171
End If172

173
174

175
'将成员确定为方法类型,准备调用176

177
firstMethodBase = CType(firstMember, System.Reflection.MethodBase)178

179
params = firstMethodBase.GetParameters()180

181
182

183
argNumber = args.Length184

185
paramsNumber = params.Length186

187
188

189
If argNumber = paramsNumber Then190

191
'没有任何缺省参数和参数数组的情况192

193
194

195
Return LateBinding.FastCall( _196

197
o, firstMethodBase, params, _198

199
args, objType, correctIReflect)200

201
Else202

203
If CopyBack Is Nothing Then204

205
If LateBinding.NoByrefs(params) Then206

207
'没有按引用传递的参数208

209
'判断最后一个参数是否是参数数组()210

211
paramsInfo = params(paramsNumber - 1)212

213
If paramsInfo.ParameterType.IsArray Then214

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) Then226

227
228

229
'没有参数数组,继续用FastCall230

231
Return LateBinding.FastCall( _232

233
o, firstMethodBase, params, _234

235
args, objType, correctIReflect)236

237
End If238

239
Else240

241
'没有参数数组,继续用FastCall242

243
Return LateBinding.FastCall( _244

245
o, firstMethodBase, params, _246

247
args, objType, correctIReflect)248

249
End If250

251
End If252

253
End If254

255
End If256

257
End If258

259
End If260

261
262

263
'COM对象、有参数数组、有方法别名或者有ByRef参数需要回送值的情况264

265
Try266

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 Try278

279
Catch misMember As System.MissingMemberException280

281
282

283
Throw misMember284

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.TargetInvocationException308

309
310

311
Throw targetInv.InnerException312

313
314

315
End Try316

317
318

319
Return result320

321
322

323
End Function324

325

关键部分我加了少量注释。总的来说,这段代码首先分解了传递的参数,通过反射从对象的类型中查找所需的方法。在调用的时候,它建立了一个VBBinder类的对象,VBBinder继承自System.Reflection.Binder类,是一个管理实参和型参结合的类。除了用VBBinder调用以外,我们还能看出,如果符合下列情况,将通过另一个方法LateBinding.FastCall来调用方法:
1、不是COM对象的方法
2、方法没有别名(没有重载)
3、没有使用参数数组和可选参数
4、没有按引用传递和接受返回参数的情况
FastCall运行速度可能要比VBBinder.InvokeMember快,它忽略别名和其它高级的用法。
关于VBBinder和LateBinding的具体实现,我们下次继续讨论。
以上为转载!没有经过作者的允许。特此声明!

浙公网安备 33010602011771号