< Day Day Up > |
In both FoxPro and Visual Basic .NET, code is divided into modules called functions and procedures (subroutines in Visual Basic .NET). These can be modules, or they can be methods in classes. You use Name () to call a function or procedure, and obj.Name() to call a method of a class. When you define a class, whether in a PRG or in a VCX, you can add methods, which either return a value or don't. (In Visual FoxPro there is no distinction between function methods and procedure methods.) When you add a method to either a Visual Class Library (VCX) or a class in a PRG, you designate it as Public , Protected , or Hidden . This determines the method's visibility when instantiated . If you define the class and its methods in a PRG, that is, a procedure library, you precede method names with PROTECTED or HIDDEN (the unstated default is PUBLIC ):
When you create an object from a class definition stored in a VCX or PRG using CREATEOBJECT() or NEWOBJECT() , all of the base class properties and methods are exposed. There may be dozens of them. However, if you use a PRG that starts with the statement DEFINE CLASS <name> AS CUSTOM OLEPUBLIC and ends with ENDDEFINE and compile it as a single-threaded DLL (STDLL) or a multithreaded DLL (MTDLL), then when you instantiate an object from the resulting DLL, IntelliSense will only show you the PUBLIC properties and methods. It only takes two additional lines of code, and provides a cleaner way for others to use your class's methods. Function DeclarationsBut even the many variations on declaring variables discussed in the preceding section don't cause as much confusion as subroutine and function declarations. For one thing, functions and subroutines in FoxPro have always been a bit amorphous; either one can return or not return a value. Visual Basic .NET is very particular about this. Functions must return a value, and it must be of the type specified in the RETURN statement. To make matters worse , someone in Redmond (and I think I know who it was) once decided that assigning the function's value to its name was a good way to hide the RETURN statement, or save a line of code, or who knows what. So you see code like this: FUNCTION DollarsToCharacters (inputval as double) AS String DollarsToCharacters = CSTR(inputval,10,2) END FUNCTION Functions and Procedures in FoxProYou can define functions and classes either in a PROCEDURE library or a program. Procedure libraries are simply collections of FUNCTION and PROCEDURE blocks stored inline in a text file ending with the extension .prg . They are opened for use as procedure libraries using the statement SET PROCEDURE TO < filename > ADDITIVE Typically this is done early in the MAIN program, after which the contained functions and procedures can be referred to as if they were FoxPro functions, that is, with no object name preceding them. Functions are declared with the FUNCTION < name > ... ENDFUNC and PROCEDURE < name >... ENDPROC blocks. Methods in visual class libraries (VCX files) are not characterized as either functions or procedures, because fundamentally, FoxPro doesn't care whether you return a value or not. FoxPro traditionally uses the PARAMETERS statement to collect passed values. More recent versions of FoxPro permit the use of the Visual Basic syntax. So you can now write either FUNCTION DollarsToCharacters PARAMETERS InputVal RETURN STR(InputVal,10,2) ENDFUNC or FUNCTION DollarsToCharacters ( InputVal as Numeric ) AS String RETURN STR(InputVal,10,2) ENDFUNC In FoxPro, that's about all you have to know. Functions and Subroutines in Visual Basic .NETBrace yourself for the Visual Basic .NET equivalent. First, I'll show you the formal definitions. (Note that the only difference between functions and subroutines is the As type clause in the function declaration, which indicates what data type will be returned.) The following is the SUB declaration in Visual Basic .NET: [ < attrlist > ] [{ Overloads Overrides Overridable NotOverridable MustOverride Shadows Shared }] [{ Public Protected Friend Protected Friend Private }] Sub name [( arglist )] [ Implements interface.definedname ] [ statements ] [ Exit Sub ] [ statements ] End Sub The following is the FUNCTION declaration in Visual Basic .NET: [ < attrlist > ] [{ Overloads Overrides Overridable NotOverridable MustOverride Shadows Shared }] [{ Public Protected Friend Protected Friend Private }] Function name [( arglist )] [ As type ] [ Implements interface.definedname ] [ statements ] [ Exit Function ] [ statements ] End Function The first group of options in a function or subroutine declaration determine how the routine relates to any identically named function in the base class. An explanation of each of these options follows . However, you probably don't need any of this. I'll explain at the end of this section:
The second qualifier determine visibility , that is, where the function or procedure can be seen.
Constructors: Init vs. NewIn FoxPro, you can include a PARAMETERS or LPARAMETERS statement as the first line of code in a class's Init procedure, then pass parameters to it in the CreateObject() or NewObject() function call that instantiates an object based on the class. Init is called the class constructor. In .NET, the New() function is the constructor, and it can also be used to pass parameters while creating an instance of a class. However, it's common in the .NET Namespaces to have several overloads of the New method, so that you can instantiate an object and pass it several types of parameters. The only difference between different overloads is their parameter lists; for example, when creating a DataAdapter object, you can either pass it a SQL select statement and a connection string, like this: Dim da as New SQLDataAdapter("SELECT * FROM Customers", "server=(local);database=NorthWind , PWD=sa;") or a SQL select statement and an object reference to an open connection, like this: Dim cn as New SQLConnection("server=(local);database=NorthWind, PWD=sa;") Cn.Open() Dim da as New SQLDataAdapter("SELECT * FROM Customers", cn) So even if you don't ever overload any of your own methods, you'll use overloaded .NET methods every day. When you do so, you'll need to add the statement MyBase.New() as the first line of your overloaded method, to call the object's original constructor. Implementing InterfacesFinally, you can declare a function signature (a collection of properties and methods) known as an interface , and then include an Implements clause in your function or subroutine declaration that checks to see that you did it right. It's a sort of project control feature.
If you're asked to add a specific functionality to your class that's been implemented elsewhere, you can build a list of the properties and methods you want to expose and include them in an Interface declaration. You can then include the statement Implements ( interface name ) in another class, and the compiler will report an error if the class doesn't reference all of the properties and methods in the interface. For example, define a class called Class1 as shown in Listing 1.1. Listing 1.1. Declaring an InterfacePublic Class Class1 Public Interface Foo Property Banana() As String Sub One(ByVal a As String) Function Two(ByVal b As String) As String End Interface End Class Now, open another class named Class2 , and on line 2, type Implements Class1.Foo . (The "Foo" will appear thanks to IntelliSense when you press the period key.) Prototyped declarations for the property, the subroutine, and the function will automatically be inserted into your code (see Listing 1.2). Listing 1.2. Implementing an Interface in a ClassPublic Class Class2 Implements Class1.Foo Public Property Banana() As String Implements Class1.Foo.Banana Get End Get Set(ByVal Value As String) End Set End Property Public Sub One(ByVal a As String) Implements Class1.Foo.One End Sub Public Function Two(ByVal b As String) As String Implements Class1.Foo.Two End Function End Class It's pretty hard to screw this up if you use these declarations. And as an added bonus, if the Interface definition in Class1 is changed, its implementation in Class2 produces a compiler error. On the other hand, you have to be pretty sure what you want Class1 to do before you define the Foo interface. Defining interfaces and then using the Implements keyword to check your math is not a bad idea. You can do it in FoxPro as well (when working with Component Object Model [COM] objects), although I've never had occasion to do so. It's helpful, but it's not necessary. So unless someone tells you to use them, ignore Interface declarations and Implements clauses. In a team environment, they become more useful. Summary of Visual Basic DeclarationsThe following summary of function scope declarations describes the available options. After I describe them, I'll suggest a simple approach.
So What Should I Use?It's very, very simple. In FoxPro, put the initial assignment of variables in MAIN to default them to PUBLIC scope, and then declare variables as PRIVATE in all called routines that themselves might call other routines that might need the private values ”for example, a screen that set up titles for a report. Use LOCAL for all variables that are definitely used only in a single routine (for example, loop counters). You can completely ignore method scope and make them all PUBLIC , the default. And remember that in general, variables that are used only by methods of a class should be public properties of that class, not PUBLIC variables in MAIN. In Visual Basic, you can do the same thing. The greater variety of declarations in Visual Basic .NET implies that it's desirable to take advantage of every one of the options. However, as database developers, we don't need most of them. Make all methods of your classes Shared Public , and ignore all of the nuances . Don't use Overrides and Overloads . And if you find yourself using Shadows a lot, consider redesigning your base class or writing two different classes. A footnote on overloaded methods in .NETThe fact that you don't need to create multiple overloads of subs and functions doesn't mean, however, that you won't see them every day of your programming life. A great many of the .NET classes have overloaded methods. Let's say you want to create a connection to SQL Server. If you do this Dim cn As SQLClient.SQLConnection( when you press the left parenthesis, nothing happens. However, if you type this: Dim cn As New SQLClient.SQLConnection( you will be presented with a little tooltip box that informs you that you're about to use overload 2 (of 2 available overloads), which accepts a connection string. Multiple overloads of New() methods for .NET classes are ubiquitous, and rather helpful. I still don't think you need to overload your own methods, but you'll use overloaded methods every day. |
< Day Day Up > |