Friday, July 27, 2007

Using an Object Property as a ByRef Parameter

The Problem

We had developed a class that contained an internal flag that would be set if a specific property on the class was set. The class would perform different logic based on whether or not the flag was set. In our testing we found that this internal flag was getting set in a scenario where it should not have been set. We verified that the code path executed in our test scenario would not explicitly set the property, but the flag was getting set.

Through a little investigation we determined that the Set property procedure for the property was being executed, even though we never explicitly set this property in our code. After a little more digging we discovered that this class’s property was being passed in to a procedure as the argument for a ByRef parameter.

What VB.NET Does

It appears that VB.NET basically does the following anytime an object property is used as an argument for a ByRef parameter:

1. VB.NET calls the Get property procedure for the object property that is being used as the argument for a ByRef parameter and stores it in a local variable. This is the same thing that happens when an object property is used as the argument for a ByVal parameter.

2. VB.NET will then execute the code in the procedure. Any Get or Set operations performed on the ByRef parameter will be executed against this local variable rather than the object property.

3. When the procedure has finished executing, VB.NET will set the object property’s value to the local variable by calling the Set property procedure for that object property. This will occur even if the local variable associated with the ByRef parameter was never set. If this were a ByVal parameter, this set operation would not occur.

This technique for handling object properties used as arguments for ByRef parameters seems reasonable, but if you are not aware that this is how VB.NET handles this situation, you can find yourself in trouble. In our case we had logic associated with the class’s property being set and we assumed the property would only be set if the ByRef parameter was set. Unfortunately, this is not how VB.NET was implemented by Microsoft, so we had to modify our code to accommodate the actual behavior of VB.NET.

What C# Does

Out of curiosity, I wanted to see if C# exhibited the same behavior as VB.NET, so I wrote a simple application to duplicate what we were doing in our VB.NET code. When I tried to compile the application, the following error occurred:
error CS0206: A property or indexer may not be passed as an out or ref parameter
It appears that the C# development team, when faced with the same technical issue, decided to inform the developer that he or she could not do what they were trying to do by raising a compiler error. In contrast, the VB.NET group provided the desired functionality, but implemented it in a way that is not necessarily intuitive.

Personal Opinion

I personally prefer the C# approach for handling this issue, but I understand why VB.NET works the way it does.

Lesson Learned

It is best to avoid using an object’s property as the argument for a ByRef parameter in VB.NET.