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

關鍵部分我加了少量注釋??偟膩碚f,這段代碼首先分解了傳遞的參數,通過反射從對象的類型中查找所需的方法。在調用的時候,它建立了一個VBBinder類的對象,VBBinder繼承自System.Reflection.Binder類,是一個管理實參和型參結合的類。除了用VBBinder調用以外,我們還能看出,如果符合下列情況,將通過另一個方法LateBinding.FastCall來調用方法:
1、不是COM對象的方法
2、方法沒有別名(沒有重載)
3、沒有使用參數數組和可選參數
4、沒有按引用傳遞和接受返回參數的情況
FastCall運行速度可能要比VBBinder.InvokeMember快,它忽略別名和其它高級的用法。
關于VBBinder和LateBinding的具體實現,我們下次繼續討論。
以上為轉載!沒有經過作者的允許。特此聲明!

浙公網安備 33010602011771號