魔力随笔.net

随心开发,自为满意

博客园 首页 新随笔 联系 订阅 管理

Visual Basic一大吸引人的地方是它支持直接的后期绑定。就是说可以直接在代码里访问那些要到运行时才能确定的对象成员。比如这样一段代码:

 

1Dim f As Object = New Form2()
2f.Show()

 

Object是没有Show方法的,但是这段代码可以正确运行。这个功能让VB有了很强的灵活性,它能够直接操作没有类型库的COM对象,而C#则很麻烦。在COM时代,VB的后期绑定是通过IDispatch实现的,而.NET时代,它是由Reflection实现的。为了揭开VB后期绑定的秘密,后面几篇就来讨论一下后期绑定的实现原理。

首先,对方法的调用是后期绑定最重要的一个环节,因为实现了后期绑定的方法调用,就能实现对属性的访问和对事件的操作,这基本上就是对象操作的全部内容。VB的编译器在发现后期绑定的调用后,会用Microsoft.VisualBasic.CompilerServices中的相关操作类实现后期绑定的方法调用。其中,LateBinding.InternalLateCall是这个操作的桥梁。我们来看看这个方法的实现。

这段代码是我从IL手工翻译来的,因为流行的反编译器都不能正确反编译VB的When语句。这段代码很可能有错,凑合着看吧:


 

 1<DebuggerStepThrough(), _
 2
 3DebuggerHidden()> _
 4
 5Friend 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
21As 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 = 0Then
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, NothingNothing, 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
323End Function
324
325

关键部分我加了少量注释。总的来说,这段代码首先分解了传递的参数,通过反射从对象的类型中查找所需的方法。在调用的时候,它建立了一个VBBinder类的对象,VBBinder继承自System.Reflection.Binder类,是一个管理实参和型参结合的类。除了用VBBinder调用以外,我们还能看出,如果符合下列情况,将通过另一个方法LateBinding.FastCall来调用方法:

1、不是COM对象的方法

2、方法没有别名(没有重载)

3、没有使用参数数组和可选参数

4、没有按引用传递和接受返回参数的情况

FastCall运行速度可能要比VBBinder.InvokeMember快,它忽略别名和其它高级的用法。

关于VBBinder和LateBinding的具体实现,我们下次继续讨论。


以上为转载!没有经过作者的允许。特此声明!

posted on 2005-08-14 22:25  魔力随笔.net  阅读(1977)  评论(0)    收藏  举报