Navigation


VFPConversion Blog

This blog is dedicate to the adoption of technologies such as Microsoft .NET or SQL Server in addition to Visual FoxPro. Expect posts on this blog to be fairly technical, as this blog is geared towards developers, testers, and technical decision makers.

Content Area Footer

Saturday, April 22, 2006
A Nice Use of Generics

The .NET Runtime 2.0 has a very nice new feature called "Generics". Generics have a lot to do with strong-typing. As you probably know, everything one does in .NET is supposed to be strongly types, since it has such a positive impact on code quality (among other things). However, there are some areas where strong typing had to be violated in .NET up to version 1.1, or - as an alternative - the developer had to do an insane amount of work to get the advantages of strong typing.

One example are lists of objects stored in collections. For this purpose, .NET offers various classes, such as the ArrayList class. An array list can store any object type. For instance, an array list can store form classes:

ArrayList myForms = new ArrayList();
myForms.Add(new Form());
myForms.Add(new Form());
myForms.Add(new CustomerEditForm());

This is C# code. If there was an ArrayList class in VFP (or if you created one yourself), it would be used in this fashion:

LOCAL myForms
myForms = CreateObject("ArrayList")
myForms.Add(CreateObject("Form"))
myForms.Add(CreateObject("Form"))
myForms.Add(CreateObject("CustomerEditForm"))

Having collections of objects is very useful in many ways. For instance, we can now iterate over these forms to show them all on screen:

foreach (Form frm in myForms)
{
    frm.Show();
}

Now here is the problem: What if someone stored something other than a Form instance into that collection? An ArrayList can store any object, not just forms. So this would be a disaster:

ArrayList myForms = new ArrayList();
myForms.Add(new Form());
myForms.Add(new Form());
myForms.Add(new Button());
foreach (Form frm in myForms)
{
    frm.Show();
}

This would fail at runtime, since there is no way for the ArrayList class to control that only a certain type of object can be stored in that particular instance.

So how can we solve this problem? In .NET 1.1, this problem is solved by subclassing the ArrayList class and changing a number of members (such as the Add() method) to accept only Form classes. The problem with this is that it is very time consuming to do this. One has to create a subclass for forms, another for buttons, another for DataSets, and so on. (Keep in mind that there are thousands of different object types in the .NET Framework).

This is why .NET 2.0 introduces a the "Generics" feature. It allows for the generic definition of classes (such as an ArrayList-like class), but then when it is used, define a more specific purpose. This example shows the new List class, which is a generics-enabled version of the ArrayList:

List<Form> myForms = new List<Form>();
myForms.Add(new Form());
myForms.Add(new Form());
myForms.Add(new Button()); // Compiler Error!!!

In this case, a new List object is instantiated, but it is stated that in this instance, only Form objects can be stored in it. This is defined in the angle brackets. Instead of saying "we instantiate an ArrayList", we would now say "we instantiate a List of Form".

This is very cool, because is is great for code quality as well as productivity.

Now here is a very cool example for the use of generics in a non-collection example: I recently helped a customer to create business rules. The customer uses our Milos Solution Platform (framework) to create their application, and Milos (like many other frameworks) allows for the definition of business rules as classes (this is only one of the ways of doing this... but exact workings of Milos are not the subject of this post). The customer wanted to create a generic rule that allowed them to compare any data field to a value and say "this is only valid of the value is greater than X".

In .NET 1.1, this sort of rule could be implemented through objects that "implement IComparable". Many objects, such as integers, decimals, strings,... implement IComparable. This way, it is possible to say "if (x > 0)", which compares x to 0 and evaluates to true if x is indeed greater than 0. It also allows us to say 'if ("B" > "A")', which evaluates to true, since A is before B in the alphabet. So this works with every object that implements IComparable. Therefore, a method that compares to values could be implemented like this in C#:

public class MyRule
    public bool
IsGreater(IComparable val1, IComparable val2)
    {
        if (val1.CompareTo(val2) > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

We can now call this method passing two integers (numbers), since integers implement IComparable. So the following is perfectly valid:

MyRule x = new MyRule();
x.IsGreater(10,5);

The following is also valid, since strings implement IComparable:

MyRule x = new MyRule();
x.IsGreater("Athens","Rome");

So far so good. The only gotcha here is that both passed parameters need to be not just of a type that implements IComparable, but they in fact need to be the SAME type. In other words: This would not work despite both parameters being IComparable:

MyRule x = new MyRule();
x.IsGreater(10,"Rome");

And this can be a bummer, because sometimes this gets confusing. A typical failure scenario would be one where two different numeric types (decimal and integer for instance) are passed. What we need is a way to implement this as generically as demonstrated here, but when the method is used, it needs to be restricted to a specific type. And once again, this can be done when the same method is implemented through generics:

MyRule<int> x = new MyRule<int>();
x.IsGreater(10,20);
x.IsGreater(10,"Rome"); // Compiler Error!!!

I thought this was a very nice use of Generics. It saved us from having to create potentially hundreds of different strongly-typed rules and still get full compiler quality checking.

BTW: A little while back I blogged about the creation of a Length data type. At the time, some readers discussed whether there was a real advantage over creating a new data type, or whether the same advantages could be achieved through methods and objects implemented in VFP. This business rule example is a good demonstration of why it is advantageous to have custom types. The Length type I created in this example can be used with this same business rule class like so:

MyRule<Length> x = new MyRule<Length>();
x.IsGreater(new Length("5 foot 6 inch"),new Length("1m 25cm"));

There is no difference between the usage of this custom type and the intrinsic types. As long as the custom type implements IComparable, it will work in all the scenarios all the other types work.

BTW: If you are interested in more info about generics, check out the following CoDe Magazine search link:
http://www.code-magazine.com/SearchResults.aspx?search=Generics

 



Posted @ 6:06 PM by Egger, Markus (markus@code-magazine.com) -
Comments


 

 

 

 

 

 

 

Syndication RSS 2.0 RSS 2.0

All My Blogs:
My personal blogs:
Dev and Publishing Dev and Publishing
Travel and Internat. Living Travel and Internat. Living
Other blogs I contribute to:
Milos Blog (US) Milos Blog (US)
VFPConv. Dev Blog (US) VFPConv. Dev Blog (US)
VFPConv. Dev Blog (DE) VFPConv. Dev Blog (DE)

 

Blog Archives
All Blog Posts

2009
    February (1)
2008
    November (1)
    October (2)
    April (1)
    March (1)
2007
    August (1)
    July (1)
    June (2)
    March (1)
    January (1)
2006
    December (1)
    November (2)
    October (1)
    September (1)
    August (1)
    April (3)
    March (2)
    February (1)
    January (1)
2005
    December (2)
    November (3)
    October (2)
    September (7)
    August (3)
    July (4)
    June (12)

 

 

 

This Blog is powered by MilosTM Collaboration Components.