To help you understand .NET development from a VFP perspective, this article introduces you to operator overloading and shows you how to apply it for powerful programming in .NET.

Many object-oriented languages support a concept called “operator overloading.” This technique lets you specify how operators such as = or + are applied to objects. Visual FoxPro gets away without support for operator overloading, mainly because there isn't much need for it in VFP due to the way you build and use objects. In .NET, on the other hand, where object-oriented development is taken to new heights because everything is an object, operator overloading is a welcome addition to the developer's toolbox.

What exactly does operator overloading do? To answer this question, let's first take a look at a few familiar and seemingly simple scenarios that exist in Visual FoxPro. The first example is the simple definition of two numeric memory variables:

LOCAL lnOne AS Number
 LOCAL lnTwo AS Number
 lnOne = 10
 lnTwo = 5

These numbers are simple types. Visual FoxPro has knowledge about how to manage numbers in memory and how to apply operators (operations) to them. For example, you can simply add the two numbers and print the result:

 ? lnOne + lnTwo

Similarly, you can easily compare the two numbers:

 ? lnOne = lnTwo

It comes as little surprise that you can also do this with other types, as demonstrated in this simple string example:

 LOCAL lcOne AS String
 LOCAL lcTwo AS String
 lcOne = "Hello"
 lcTwo = "World!"
 ? lcOne + lcTwo
 ? lcOne = lcTwo

At this point you may be wondering why we'd write an article about something that seems so trivial. The reason is there are some interesting things happening here that developers generally take for granted. For one, the Visual FoxPro runtime and compiler have knowledge about how to handle these specific types. Therefore, Visual FoxPro knows that in order to handle a numeric variable, it has to retrieve the binary representation of the number from a certain location in memory. VFP also knows how to take two numeric values and compare them or add them together. For the string example, on the other hand, a whole different set of rules applies. Again, VFP knows how to handle binary in-memory representations of the string. It also knows that to apply the + operator to two strings, it can't just use the binary representation and add them together. Instead, the two strings have to be put in sequence one after the other. If VFP were to apply the same rules to strings as to numbers, it would simply take the numeric value for “H” (72) and the numeric value for “W” (87) and add them together, which would result in 159, which is some special character depending on your encoding. This is not at all what you'd want.

Luckily, VFP knows how to apply operators in a sensible fashion, so all the different variable and field types are handled as you'd expect. Note that there's one special type in VFP: the object.

VFP isn't very good at comparing two different objects. Consider this example:

 LOCAL loForm1 AS Form
 LOCAL loForm2 AS Form
 LOCAL loForm3 AS Form
 loForm1 = CreateObject("Form")
 loForm2 = loForm1
 loForm3 = CreateObject("Form")
 ? loForm1 = loForm2
 ? loForm3 = loForm2

In this example, we create three variables of type “object” that all point to forms. The first two point to the same exact object in memory, and the third variable creates its own instance of a form. Both instantiated forms are completely identical. However, even though loForm2 points to an object that's indistinguishable from loForm3, VFP tells you they aren't the same. On the other hand, loForm1 and loForm2 point to the same exact form object in memory and VFP says they're identical.

This also doesn't come as a surprise. When you create complex objects, such as forms, you generally expect the = operator to tell you whether the exact instance is the same. After all, there are two forms in memory and potentially two different windows showing on the screen.

Entering an all-object world

Things become a little more interesting when we enter a world where everything is an object. If you've read previous articles in our series on .NET development from a Visual FoxPro perspective, you probably already know that in .NET, there's no difference between “types” and “objects.” The two terms can be used completely interchangeably. Therefore, when you create a numeric variable in .NET (an “integer”), you're really creating an integer object. You can imagine an integer variable as an object with only a single property that holds the numeric value. Now consider this example in C#:

 int iOne = 10;
 int iTwo = 10;
 if (iOne == iTwo)
 {
     Console.WriteLine("They are equal!");
 }

In this example we create two different integer objects in memory that happen to have the same value. Then we compare the two variables to see if they're equal. Would you expect C# to consider the two objects to be identical, even though they're different object instances, similar to how the two VFP forms were different objects? Would you expect to see the message printed out?

Of course you would. But how exactly did it know? How could it take two object references and compare them and realize that there's a single property in these integer objects and if they happen to be the same, consider them equal? How does it know that unlike in the VFP form example above, it isn't supposed to compare references but look instead at the values?

There are different answers to these questions. One answer is the compiler could have special knowledge of integer objects, so it knows to compare values rather than references. Similarly, the compiler could have special knowledge of other object types, such as strings, dates, etc. In fact, this is exactly what Visual Basic .NET does.

The problem with this approach is that the people creating the compiler have to take as many special objects/types into account as possible, but even if they have a large list of special cases, they can't possibly foresee the new objects you may want to create. Another way to go is to let the object's developer specify how certain operators apply to the class he created. This is exactly where operator overloading comes into play, and where, to be honest, C# currently has a leg up on VB.NET.

To reiterate the point, let's take a look at another type .NET supports natively: the GUID. A GUID is a globally unique ID. You often see GUIDs represented as strings, like this:

 9d35b850-967f-4b19-ac30-bb67eadeb19d

This isn't how GUIDs are stored in memory, however. Instead, GUIDs are a composition of a number of smaller numbers (simply referred to as components a through k). You can look at the .NET debugger to see what the breakdown is. For the above GUID, these components have the following values:

 a = -1657423792 (integer)
 b = -27009 (short)
 c = 19225 (byte)
 d = 172 (byte)
 e = 48 (byte)
 f = 187 (byte)
 g = 103 (byte)
 h = 234 (byte)
 i = 222 (byte)
 j = 177 (byte)
 k = 157 (byte)

You can instantiate GUIDs in a number of ways. The most intuitive way is to define the string-formatted value as the constructor parameter like this:

 Guid myFirstGuid = new Guid("9d35b850-967f-4b19-ac30-bb67eadeb19d")

Guid mySecondGuid = new Guid(“9d35b850-967f-4b19-ac30-bb67eadeb19d”)

As you can see, the two GUIDs are completely identical, but they're separate objects in memory. Again, the question is whether the following if-statement will evaluate to true and process the output line.

 if (myFirstGuid == mySecondGuid)
 {
     Console.WriteLine("They are equal!");
 }

Should they be considered equal? In fact, they are and C# knows, not because there's special code in the C# compiler that knows how to handle GUIDs, but because the GUID class defines the behavior for the equals (==) operator.

You can write a similar example in VB.NET:

 Dim myFirstGuid As New Guid("9d35b850-967f-4b19-ac30-bb67eadeb19d")
 Dim mySecondGuid As New Guid("9d35b850-967f-4b19-ac30-bb67eadeb19d")
 If myFirstGuid = mySecondGuid Then
     Console.WriteLine("They are equal.")
 End If

Would you still expect them to be the same? I would, but they aren't. This code snippet doesn't even compile. The VB.NET compiler simply states that it doesn't know how to process the = operator for GUIDs. Visual Basic .NET doesn't support operator overloading and therefore can't benefit from the rules defined by the GUID class as to how = operations should be handled. Too bad! The only way you can compare GUIDs in VB.NET is to first turn them into their string representation:

 If myFirstGuid.ToString() = mySecondGuid.ToString() Then
     Console.WriteLine("They are equal.")
 End If

Luckily, the ToString() method provides a version of the GUID that can be reliably compared to another string GUID because the returned string includes all the information encoded in the object. This is a lucky coincidence, however. Developers could have chosen to only include the “a” value in the output of the ToString() method, and this wouldn't have worked. Imagine doing something similar with a Customer object. ToString() might just return the last name, which wouldn't be reliable when comparing John Smith and Jack Smith. At that point, it would be up to the developer to inspect individual properties and decide whether the objects are considered equal. The Customer class has no way to define rules for comparison in the VB.NET world.

As you can imagine, due to the lack of support for the feature, you won't see much VB.NET code in the rest of this article.

Implementing operator overloading

Now that we discussed the usefulness of operator overloading, let's create a simple example. Normally, when people create operator-overloading examples, vector additions and multiplications are used to demonstrate the concept's power. There is a good reason for this, since it is very natural to allow addition of two vectors using the + operator among many other things. However, I also find that unless you are interested in subjects such as 3D graphics, a Vector is not likely to hold a special place in your heart. I have therefore chosen to implement a new type/object/class that can store birthdays.

My expectations for a BirthDay type are pretty simple. I want the type to store information such as the year, month, and day (for simplicities sake I will forfeit any code that verifies that the date is valid). I also want dates to be comparable the way birthdays are generally compared. In other words: If two birthdays fall on the same month and day, I would like to consider the two birthdays to be equal, even if one is 59 years older than the other. (I always like to think of myself as having the same birthday as my grandmother, even though she is 59 years older than I am.)

Here's the basic birthday class:

 public class BirthDay
 {
    private int iYear;
    private int iMonth;
    private int iDay;
    private bool bInitialized = false;
    public BirthDay(int year, int month, int day)
    {
       this.iYear = year;
       this.iMonth = month;
       this.iDay = day;
             this.bInitialized = true;
    }
    public override string ToString()
    {
       return this.iMonth.ToString()+"/"+
          this.iDay.ToString()+"/"+
          this.iYear.ToString();
    }
 }

As you can see, the class is very simple. One could opt to add a lot more functionality, expose the value through means other than ToString(), and a lot more. For this example however, the current implementation will be sufficient.

We can now instantiate my own as well as my grandmother's birthday in the following fashion:

 BirthDay birthDayMarkus = new BirthDay(1974,7,31);
 BirthDay birthDayHildegard = new BirthDay(1915,7,31);

The next step is the comparison of the two birthdays. I would envision that I can do it like so:

 if (birthDayMarkus == birthDayHildegard)
 {
    Console.WriteLine("They share a birthday!!!");
 }

But it's no surprise this doesn't work out of the box. .NET can't know how to compare these two objects. It doesn't know our 3 integers hold actual data, while the Boolean is a simple “housekeeping” variable that makes no sense for comparison in any scenario. Beyond that, it certainly can not know that during comparison operations, only the month and the day are of interest. All .NET can do is check whether the two object references point to the same exact object in memory. Since they do not, the expression will always evaluate to false.

Let's fix that! We can do so by defining the operator on our class:

 public static bool operator== (BirthDay day1, BirthDay day2)
 {
    if (day1.iDay == day2.iDay && day1.iMonth == day2.iMonth)
    {
       return true;
    }
    else
    {
       return false;
    }
 }
 public static bool operator!= (BirthDay day1, BirthDay day2)
 {
    if (day1.iDay == day2.iDay && day1.iMonth == day2.iMonth)
    {
       return false;
    }
    else
    {
       return true;
    }
 }

Voila! Now our if-statement from above behaves as we would expect it to.

As you can see, operators are very similar to common methods. The main difference is that instead of the method name, we specify the “operator” keyword, followed by the operator we want to define (== in this case). Also, note that operators need to be flagged as “static” (see our previous article on static members if you are not familiar with the concept).

In this case, we also picked a somewhat special scenario. Whenever we check whether an object is “equal” (== in C#), the compiler forces us to create a “not equal” (!=) operator. This is a logical requirement since people who expect to use the == operator on your object would probably be very surprised if the != operator would fail. Similarly, each .NET object features an Equals() method. Whenever the == operator is overloaded, it makes logical sense to also override the Equals() method. You will get a compiler warning if you don't, and I encourage you to go ahead and add the functionality, even though I leave it out of this example to keep it simple.

Note that our example is very specific. It defines how two BirthDay objects can be compared to each other using == and != operators. It is also possible to create additional “overloads for overloaded operators” (things are getting a tad confusing here), where one of the two values is of a different type. For instance, we may want to compare the BirthDay object to a standard DateTime object. This can be accomplished with the following code:

 public static bool operator== (BirthDay day1, DateTime day2)
 {
    if (day1.iDay == day2.Day && day1.iMonth == day2.Month)
    {
       return true;
    }
    else
    {
       return false;
    }
 }
 public static bool operator!= (BirthDay day1, DateTime day2)
 {
    if (day1.iDay == day2.Day && day1.iMonth == day2.Month)
    {
       return false;
    }
    else
    {
       return true;
    }
 }

Note that in this case, the second parameter is of type DateTime and not BirthDay.

We can now find out very easily, whether today is my birthday:

 if (birthDayMarkus == DateTime.Today)
 {
    Console.WriteLine("Time to party!!!");
 }

Sadly, at the time of writing, this happens to evaluate to false, and short of a pretty freaky coincidence, this will likely evaluate to false when you read the article. But you get the idea.

Operator overloading is not limited to == and != operators either. For instance, we can overload the + operator for people who want to add up two BirthDay objects. The most difficult task of this step is to decide what is actually supposed to happen when someone attempts that! Perhaps we could simply return the total number of days my grandmother and I have lived combined:

 public static int operator+ (BirthDay day1, BirthDay day2)
 {
    DateTime dat1 = new DateTime(day1.iYear,day1.iMonth,day1.iDay);
    DateTime dat2 = new DateTime(day2.iYear,day2.iMonth,day2.iDay);
    TimeSpan span1 = DateTime.Today - dat1;
    TimeSpan span2 = DateTime.Today - dat2;
    return span1.Days + span2.Days;
 }

Very nice! Now we can calculate that at the time I am writing this, we have lived a total of 43,738 days.

Note that in order to figure this out, I temporarily create two DateTime objects. I then subtract each date from the current date. This takes advantage of the overloaded “-” operator on the DateTime class, which returns a TimeSpan. This is very handy. As you probably guessed, VB.NET can not do this.

Design Considerations

From a designer's point of view, operator overloading can be a mixed bag. It is a fantastic feature for a number of uses (consider the GUID example), but it can also make code unreadable and non-maintainable. Personally, I find overloading the equals-operator very useful on a large number of objects. It makes a lot of sense to me that I can apply the == operator to two customer objects, and I would expect the two objects to be considered equal if their customer number is the same (or whatever the business logic might be). Adding a + operator to a customer object, on the other hand, would not come with any obvious behavior. What would we expect to happen? Would the two customers merge? Would their orders get combined? One can not tell easily. It is therefore best not to provide such an ambiguous operator.

Consider these examples:

 Percentage num1 = new Percentage("5%");
 Percentage num2 = new Percentage("12%");
 Percentage num3 = num1 + num2;
 float total = 250 - num3;
 Matrix m1 = Matrix.RotateX(90);
 Matrix m2 = Matrix.Resize(0.5f);
 Matrix m3 = m1 * m2;
 DataRow oRow = BusinessObject.GetRow("x");
 oRow++;
 Customer oCustomer = BusinessObject.GetCustomer("ALFKI");
 oCustomer += 10000;

Can you tell easily what these examples do?

In the first three lines, we instantiate percentage objects and add them up. It would be reasonable to expect that 5% + 12% is 17%. It would also be reasonable to subtract a percentage from a number. This as a great use of operator overloading.

In the next three lines, we create different transformation matrixes and multiply them. This may not be terribly obvious to people not familiar with matrix operations, but for someone who is familiar with the subject matter, multiplying matrixes (although different from standard multiplication) is a natural and expected feature. Once again, I would consider this a good use of operator overloading. In fact, many objects that deal with matrixes, such as the managed DirectX SDK, support this very overload.

Things get a bit trickier in the DataRow example. What exactly happens when we add 1 to a row (++)? Perhaps we move to the next row. Perhaps we increase one of the field values within the row. The situation is unclear and creating this operator is not a good idea!

The same is true for the customer example. What happens when we add 10,000 to the customer? Perhaps we increase the customer's spending limit. Perhaps we just logged a payment. Perhaps something entirely different was intended. Creating this sort of operator is unintuitive and a good recipe for disaster.

So, in hindsight, was it good to create an == operator on the BirthDay object? It probably depends on the exact business scenario, but I would not argue against it. Was it good to create the + operator? Maybe not. Is it good that the DateTime object has an overloaded - operator? Certainly! It just all depends on the scenario at hand. Ask yourself whether the average user of the object would find the operator you create intuitive. Sometimes there is no clear answer, but at least you won't be way off!

Based on personal experience, I find a lot of use for overloaded == operators in a great variety of objects. There really are relatively few objects where I actually want to compare object references rather than data stored within the objects. Operators such as + and - are mostly useful for math and number-related objects. It may not make sense to have them on customer objects, but they are probably useful on a credit-limit object where one generally focuses on a single number. Operators such as < and > are a little less clear cut. They may not make sense on a customer object, but they may make sense on an “athlete” object that keeps track of the athlete's time at the 100 yard dash.

Like to so many things in programming, common sense needs to be applied to operator overloading. However, when applied correctly, operator overloading is extremely powerful, useful, and intuitive.

** By Markus Egger and Claudio Lassala **