gmplCallByNameStatement
- Mark Juras
Owned by Mark Juras
CallByName Statement Summary
CallByName is a terminal, refactoring statement that occurs only within a Refactor statement. The refactoring done by this statement takes a different view of refactoring, since it is not symbols that are being refactored but rather symbol-related code events that must be trapped and then refactored. In particular, before the compiler generates a late-binding callbyname operation on a symbol it checks if that symbol has a CallByName refactoring boxing type associated with it. If so, it boxes the symbol to that type and attempts to perform a normal early-bound call. Removing CallByName is performed by the compiler and not by the analyser. As discussed in the "Anatomy of CallByName" the alternative way of removing late-binding is to strengthen the type of the host symbol or to use the newer .NET type Dynamic. CallByName refactoring is used when changing the type of a symbol is not workable or desirable. The attributes of the CallByName statement are as follows:Attribute | Description |
Source | This optional attribute specifies the component whose code is generating late-bound calls by name. If the containing Refactor statement had a FileFilter specified then this identifier should be specified relative to it; else it should be specified relative to the root of the symbol table. If this attribute is omitted, then the code source is assumed to be the parent of the host symbol. |
Host | This required attribute specifies the host symbol, the actual object instance that is exposing the method or property being called. If the containing Refactor statement had a FileFilter specified then this identifier should be specified relative to it; else it should be specified relative to the root of the symbol table. There are three special identifiers that can be used at the start of this attribute: *Project, *Item, and *Dim.identifier. |
BoxType | This required attribute contains a comma-delimited list of the boxing types that may be used to resolve the late binding. gmBasic checks each type entry to see if it has a component that matches the following method or property name. If none exists, it does a box to the last type in the list regardless of whether it still generates a late-binding. There are three special identifiers that can be used with the type entries: *None, *Project, and *Self. |
Context | This optional attribute specifies the context in which the late call can occur: LateCall, LateCollection, and LateDefault. |
The special identifier *Project says to start the symbol search with the parent of the FileFilter. This will be the project file containing file whose symbols are being refactored. There are many different types of collections, the *Item identifier specifies any collection class Item method. The *Dim.identifier is necessary because these specifications are processed before any procedural code has been processed; therefore, undeclared variables are not yet in the symbol table. The *Dim simply specifies that the identifier needs to be declared. The *None type is used while these specifications are being developed. Where late calls are being generated is easy to see, but how to box them can be difficult to determine, especially if an interface has to be developed. The *None tells gmBasic to simply ignore the specification. The *Self type simply refers to the class being defined by the FileFilter code. The default context is LateCall. Before the compiler produces a late-binding for a weakly-typed host it checks for a CallByName entry and attempts to resolve the LateCall via a boxing operation. The LateCollection context occurs when nested Item().Item() references occur. For example consider the following situation
Collection PeriodsCollection; PeriodInfo = new Scripting.Dictionary(); PeriodsCollection.Add(PeriodInfo); StartUpString = PeriodCollections(index)("StartUp")
<CallByName source="methodname" host="methodname.PeriodsCollection" context="LateCollection" boxType="Scripting.Dictionary" />
recordSets.Add("recordset1",myRecordSet)
recordSets.Add("recordset1",myRecordSet.Fields)
<CallByName source="recordSets" host="Scripting.IDictionary.Add.Item" boxType="ADODB.Recordset" context="LateDefault" />
The Anatomy of CallByName
Both .NET and VB6 support a CallByName method which is used at runtime to get a property, set a property, or invoke a method. In .NET its parameters are as follows:Parameter | Description |
--------- | ------------ |
ObjectRef | A System.Object that is the host of the call. It is the instance of an object exposing the named property or method |
ProcName | A string expression containing the name of the property or method on the object that is being referenced. |
UseCallType | A CallType enumeration entry that specifies the type of procedure being called: Method, Get, or Set |
Args() | An optional System.Object array, ParamArray, containing the arguments to be passed to the property or method being called. |
The VB6 form of this method is almost identical except for the UseCallType parameter which in VB6 is: VbLet, VbGet, or VbMethod. The following three Vb6 CallByNames
CallByName Text1, "MousePointer", VbLet, vbCrosshair Result = CallByName(Text1, "MousePointer", VbGet) CallByName Text1, "Move", VbMethod, 4000, 4000
using VBNET = Microsoft.VisualBasic; ... VBNET.Interaction.CallByName(Text1,"MousePointer",VBNET.CallType.Set, new object[]{System.Windows.Forms.Cursors.Cross}); Result = VBNET.Interaction.CallByName(Text1,"MousePointer",VBNET.CallType.Get, new object[]{}); VBNET.Interaction.CallByName(Text1,"Move",VBNET.CallType.Method, new object[]{4000,4000});
Dim Text2 As Variant Text2.MousePointer = vbCrosshair Result = Text2.MousePointer Text2.Move(4000,4000)
object Text2 = null; VBNET.Interaction.CallByName(Text2,"MousePointer",VBNET.CallType.Set, new object[]{System.Windows.Forms.Cursors.Cross}); Result = VBNET.Interaction.CallByName(Text2,"MousePointer",VBNET.CallType.Get,null); VBNET.Interaction.CallByName(Text2,"Move",VBNET.CallType.Method, new object[]{4000,4000});
<Compile Project="C:\gmSrc\GMTest\vb6test\VB0005\vb6\VB0005.vbp" > <Refactor FileFilter="C:\gmSrc\GMTest\vb6test\VB0005\vb6\VB0005.frm"> <CallByName source="Command1_Click" Host="Command1_Click.Text2" BoxType="TextBox" /> </Refactor> </Compile>
((System.Windows.Forms.TextBox)(Text2)).Cursor = System.Windows.Forms.Cursors.Cross; Result = ((System.Windows.Forms.TextBox)(Text2)).Cursor; ((System.Windows.Forms.TextBox)(Text2)).SetBounds(4000,4000,0,0);
frm.txtFieldData(i).Text = vbNullString where frm is Variant VBNET.Interaction.CallByName(frm,"txtFieldData(i).Text",VBNET.CallType.Set, new object[]{VBNET.Constants.vbNullString});
Table of Contents