Compare Constructors and Destructors in VFP and .NET
Unlike VFP, .NET forces you to give up control over the destruction of objects, but you get some benefits in return.
For Visual FoxPro developers, constructors and destructors are a simple affair: They're events that fire when an object is created, and when it's destroyed. These things happen in predictable patterns. The way constructors and destructors work in .NET, however, is slightly different-so different, in fact, that it warrants an entire article.
What can be so different about constructors and destructors? Constructors are straightforward: They fire whenever an object is instantiated. On the other hand, destructors are far less predictable. You don't have control over when an object is destroyed because the .NET "Garbage Collector" handles object life time and "finalization." It decides when objects can be removed from memory. There's no such thing in Visual FoxPro. An object dies when the last user of the object has let go of it. This is a significant difference, and what this article is all about.
Every object is an instance of a class. Whenever a class is instantiated, the code in its constructor executes. The constructor usually contains code that sets the state for a brand new object (that is, the code sets values of whatever member variables the object might require after it instantiates).
In VFP, every time an object is created, an Init event fires, and that event calls a method also called Init. Because of this, the Init method is used as a constructor for the class. However, the Init method isn't a real constructor. Constructors are meant to be executed only when the object is created, and never again throughout its lifetime. VFP violates that rule by letting you call the Init method at any time. Programmers often write code similar to this:
| Define Class Foo As Custom|
| *-- Set up some initial values.|
| *-- Perform some tasks...|
The idea here is that after performing some actions within the object, you decide some cleanup is needed and call the Init() again, in an attempt to reset the object. Presumably, this ensures the object is ready to be used in its original state. Calling the Init method again is strange because, conceptually, it seems to indicate you're trying to create the object again, but that isn't true because the object is already there. In reality, the code lacks proper abstraction, and you could break out the setting of the initial object state into a new method similar to this:
| Define Class Foo As Custom|
| *-- Perform some tasks...|
This code creates a new method (called CleanUp) that takes care of all the necessary housekeeping to configure the object's original state. You can now call that method from the constructor as well as any other place within the object where clean-up might be needed.
- Another issue with VFP's OOP implementation is you aren't able to create real abstract classes (classes that can't be instantiated). As a workaround, you could insert a RETURN .F. in the Init method of a class, which would prevent the object from instantiating. There are some flaws to this approach, though:
- The object is created anyway, even if just for milliseconds. This can lead to performance issues. For example, the object might contain other objects through composition, which means all those objects are created before the main object is destroyed based on its "failed" Init event.
The compiler wouldn't catch attempts to instantiate an "abstract" class. Therefore, developers trying to instantiate such classes wouldn't be warned about the potential problem until the application failed during execution.
How are these things handled in .NET?
.NET's implementation of constructors makes more sense, because it enforces the aspect of only running that code when an object is created, and not letting you call that method from any place else. Here's how to implement a constructor in C#:
| // Whatever code for when the object gets created.|
In C#, the name of the constructor method is always identical to the name of the class (keep in mind C# is case-sensitive). In VB.NET, it's slightly different:
| ' Whatever code for when the object gets created.|
The main difference is that in VB.NET, constructors have a standardized name of "New" (indicating that when a new object is created, this is the code that should run).
If you have to run some clean up code after creating the object, you can use the same technique mentioned earlier for the VFP code, which is the creation of a CleanUp method (the name is up to you). Then call that method from the object's constructor and whatever else that's required.
You might have noticed that in both examples we declared the constructors as public. If the constructor isn't public, it isn't accessible to the outside world and it can't instantiate the class. The one scenario in which you'd want that behavior is when you have a class with static (C#) or shared (VB.NET) members you don't want to instantiate, but you want to use those members. We won't delve into that here, however, because it's the topic of a future article in our series on comparing OOP in Visual FoxPro and .NET.
If you're interested in additional details about constructors, refer to our article "Compare Inheritance in VFP and VS.NET" in the January 2004 issue, where you can find more information about overloaded constructors and inheritance.
Where objects go to die
Whereas an object's constructor has code that executes when an object is instantiated, destructors are exactly the opposite. They fire when an object is destroyed. In VFP, when an object is destroyed, the Destroy event fires, calling a Destroy method. The Destroy method acts as the destructor. Notice that the Destroy method presents the same problem as the Init method: It's available for any process to call it at any time. Therefore, this example is possible:
| oFoo = CreateObject("Foo")|
Even though this is possible, it doesn't make much sense. Calling the Destroy method won't destroy the object. The only thing it does is run whatever code that method has. You can probably envision a better way to go. Create a second method you can call from within the destructor and wherever else it might be useful.
Not surprisingly, .NET languages also provide destructors for classes. Again, unlike in VFP, you can't call a destructor directly. Code inside a destructor only runs when the object is destroyed from memory.
This is how you define a class destructor in C#:
| // Whatever code for when the object is destroyed.|
This is similar to creating a constructor, except this time the name of the method is prefixed by a tilde (~). The VB.NET version is slightly different:
| Protected Override Sub Finalize()|
| 'Whatever code for when the object is destroyed.|
The difference here is that in VB.NET a destructor is implemented by overriding the Finalize method defined in the Object class.
So far, so good. Things seem to be similar in both worlds -- or are they?
I'm not dead yet!
How hard it is to destroy an object in VFP? Usually it doesn't take more than this:
This line of code explicitly removes the object from memory. Alternatively, you can simply indicate you don't need the object anymore by assigning a value other than the object to the variable that represents the object. You can do this by assigning "null":
You aren't required to set the reference to null. In fact, if the variable referencing the object went out of scope, you'd see the same result.
This doesn't necessarily kill the object. Visual FoxPro uses a mechanism called "reference counting" to determine whether an object must be kept in reference. Consider this example:
| oTest = CreateObject("Custom")|
This creates a custom object in memory, then assigns a reference to the object to a second variable. You now have two references to the same object. In other words: The reference count is now 2. What happens when you set oTest to null? Not much. The reference count is reduced to 1. However, you still have a second reference to the same object called oTest2. As long as that variable references the object, the object remains in memory. Only when that reference is gone, and the reference count is reduced to 0, is the object removed from memory.
This is a fairly standard technique. Unfortunately, it isn't entirely reliable in scenarios where objects have references to each other. In that case, objects may be "stuck" in memory, despite not having any external references.
Let's take this example further and look at a VFP form example:
| oForm = Createobject("MyForm")|
| Local loTextbox As TextBox|
| loTextbox = oForm.txtTest|
| Define Class MyForm As Form |
| Add Object txtTest as textbox|
| txtTest.Value = "Some text..."|
| MessageBox("Destroying form...")|
At the bottom of this example there's a simple class with a contained textbox and a MessageBox in its Destroy method, so you can see when the object is destroyed.
You first instantiate the form and assign it to a variable called oForm. Subsequently, a reference to the textbox inside that form is assigned to another local variable named loTextbox. Immediately afterwards, the oForm variable is set to null in an effort to destroy the form, which triggers the messagebox in the Destroy() method (think of it as the destructor). Nevertheless, the Form doesn't disappear, which means the form is still in memory. The form can't be released from memory because, even though the form has by now let go of its reference to the textbox, the second local variable hasn't. Therefore, the reference count is still at 1, and the textbox can't be released from memory. This also keeps the form alive. As as result, the call to MessageBox(loTextbox.Value) works just fine, showing the text from the form's textbox.
As you can see, this scenario is confusing. Even though the Form's Destroy event fired, the Form wasn't released. What if the Destroy method was cleaning up resources needed by the other variable that was holding a reference to the form's textbox? Unexpected errors would occur.
Also, after you've assigned a null to the variable, VFP's runtime doesn't know about the other variables referencing that first variable, and it tries to assign other things to that "free" space in memory. But wait! The object (the form in this example) is still there; wouldn't that be a problem? Have you ever seen C5 errors? Dangled references in memory can cause many of them. In a small scenario like this, you can figure it out relatively easily, but in a real-world application, things are hardly ever this simple.
Killing .NET objects
C#'s syntax for clearing object references isn't all that different. You can remove the reference by setting it to null:
Here's the example in VB.NET:
This is different from setting the reference to null in VFP. Unlike VFP, .NET doesn't count references. Instead, it employs a "Garbage Collector," a process whose sole purpose is to figure out whether you should keep objects in memory. When you set an object reference to null in .NET, you're only telling the .NET Garbage Collector you're done using the object. But, and this is the big surprise, doing that doesn't mean it removes the object from memory precisely at that moment. Instead, it stays in memory until the Garbage Collector runs. And even when that happens, there's no guarantee the object will be removed from memory. The Garbage Collector decides whether objects should be disposed of to free up resources. This could be a long time after you destroy the object. In fact, if the system isn't busy, this could be a matter of hours.
Disposing of objects
You just learned that in .NET you don't have control over when an object is destroyed. This can be a problem when objects make use of system resources such as connections to a database. Previously (and in VFP), a common technique was to release such resources in the object's destructor. Normally, it isn't a good idea to hold a database connection longer than necessary. Because the destructor in .NET may not fire for hours, this clearly isn't a good way to go. But, what can you do if there's no control over when the object is destroyed? The answer lies in implementing the IDisposable interface. (If you aren't familiar with interfaces, refer to our article, "Compare Interfaces and Polymorphism in VFP and VS.NET," in the May 2004 issue.)
The IDisposable interface has one method: Dispose. Every class that uses resources that should be disposed of when you're done using the object should implement this interface. It's up to you to call the Dispose method when necessary.
Here's a simple example in C# of a class implementing the IDisposable interface:
| // Whatever code that cleans up resources.|
Here's the example in VB.NET:
| Public Sub Dispose() Implements System.IDisposable.Dispose|
| 'Whatever code that cleans up resources.|
Now you have a method that can contain cleanup code. However, this method won't be called automatically. Instead, it's up to whoever's using that object to call the Dispose method when he's done. This doesn't remove the object from memory. It simply instructs the object to clean up whatever must be cleaned up. The object will remain in memory afterwards, although likely in a non-functional version.
Subsequently, you can let go of all the references to the object, or set object references to null. This makes the object available for garbage collection and the object will eventually be cleared from memory. This time, however, you don't care how much time passes until that happens because the object doesn't hold onto valuable resources any longer.
However, this scenario only works if the developer remembers to call Dispose() when the object is no longer needed. Otherwise, valuable resources remain in use, causing
problems such as database connection leaks. For this reason, clean up code should not only reside in the Dispose() method, but also in the finalize method just in case. This way, resources may remain in use much longer than desired (which can have serious side effects), but at least it won't be catastrophic.
It's also important to add a call to the GC.SuppressFinalize method from within the Dispose method. It's time-consuming for the garbage collector to finalize an object (which, in turn, calls the method's Finalize method). If the object was already disposed of by calling the Dispose method, it's possible to suppress that step by using the GC.SuppressFinalize method.
Note: There's a good description of .NET's garbage collector in the MSDN Help, which we strongly suggest every .NET developer reads. Search for the topic "Programming for Garbage Collection."
One difference in C# is that it provides a using block (which has a different purpose than the using statement that imports namespaces). The using block automatically calls -- at the end of the block -- the Dispose method of the object being used. Here's an example:
| using (Font MyFont = new Font("Arial", 10.0f))|
| // Whatever code that uses the MyFont object.|
| } // compiler will call Dispose on MyFont|
In this article, we discussed the differences between constructors (code that runs when an object is instantiated) and destructors (code that runs when an object is destroyed) in VFP and .NET, and information about how .NET handles garbage collection. The garbage collector helps avoid potential problems you usually have to take care of yourself in VFP. These problems are often hard to detect in VFP, and many developers aren't even aware their applications slowly keep allocating more and more memory to these unneeded objects. In .NET, you pay for this added memory management quality with some potentially unexpected headaches in object lifetime and destruction. However, the trade-off is a good one: You give up control over the destruction of objects and have to get used to calling Dispose(), but you get rid of the frequent memory leaks, which pays off in any serious development effort.
By Claudio Lassala and Markus Egger