Thursday, March 1, 2007

Why IEquatable ?

In the .net framework 2.0, a new interface IEquatable<T> has been introduced. Implementing it in your classes would give you a type safe way of equating your class instances.

The default .equals of the System.Object does a reference check and is not particularly useful when you want to equate instances of your class based on say certain fields. The workaround in .net 1.1 was to override the .equals(object o) , but this meant that additional type checking had to be done. Now with Generics,
IEquatable<T> gives you a type safe method to implement your class instance equality check.

I wrote some simple code to figure it all out:

First, A class which doesn't implement IEquatable ....


using System;

namespace test
{
class RegularAnimal :Animal
{



public RegularAnimal(int legs, string sound, string species)
{
this.legs = legs;
this.sound = sound;
this.species = species;

}

public void Introduce()
{
Console.WriteLine("I am a regular " + species + " I have " + legs.ToString() + " legs and I " + sound);
}


}
}


Now the same class that implements IEquatable<T>


using System;


namespace test
{

class EquatableAnimal : Animal, IEquatable<EquatableAnimal>
{

public EquatableAnimal(int legs, string sound, string species)
{
this.legs = legs;
this.sound = sound;
this.species = species;

}

public void Introduce()
{
Console.WriteLine("I am an equatable " + species + " I have " + legs.ToString() + " legs and I " + sound);
}


public bool Equals(EquatableAnimal other)
{
return (this != null && this.sound == other.sound);
}


}

}

Now some code to put the classes to test:


static void Main(string[] args)
{


RegularAnimal regular_cat = new RegularAnimal(4, "meow", "cat");
RegularAnimal regular_dog = new RegularAnimal(4, "bark", "dog");
RegularAnimal regular_siameseCat = new RegularAnimal(4, "meow", "siamese cat");

regular_cat.Introduce();
regular_dog.Introduce();
regular_siameseCat.Introduce();
IsEqual(regular_cat, regular_dog);
IsEqual(regular_cat, regular_siameseCat);

EquatableAnimal cat = new EquatableAnimal(4, "meow", "cat");
EquatableAnimal dog = new EquatableAnimal(4, "bark", "dog");
EquatableAnimal siameseCat = new EquatableAnimal(4, "meow", "siamese cat");
cat.Introduce();
dog.Introduce();
siameseCat.Introduce();
IsEqual(cat, dog);
IsEqual(cat, siameseCat);



}

public static void IsEqual(EquatableAnimal animal1, EquatableAnimal animal2)
{
if (animal1.Equals(animal2))
{
Console.WriteLine("We are the same: " + animal1.species + " and " + animal2.species);
}
else
{
Console.WriteLine("We are not the same: " + animal1.species + " and " + animal2.species);
}
}
public static void IsEqual(RegularAnimal animal1, RegularAnimal animal2)
{
if (animal1.Equals(animal2))
{
Console.WriteLine("We are the same: " + animal1.species + " and " + animal2.species);
}
else
{
Console.WriteLine("We are not the same: " + animal1.species + " and " + animal2.species);
}
}



The results(click on the picture to enlarge):


I am a regular cat I have 4 legs and I meow
I am a regular dog I have 4 legs and I bark
I am a regular siamese cat I have 4 legs and I meow
We are not the same: cat and dog
We are not the same: cat and siamese cat
I am an equatable cat I have 4 legs and I meow
I am an equatable dog I have 4 legs and I bark
I am an equatable siamese cat I have 4 legs and I meow
We are not the same: cat and dog
We are the same: cat and siamese cat
Press any key to continue . . .







The 'EquatableAnimal' compares the 'sound' field to identify animals of the same species while the RegularAnimal equality check fails since we are not overriding the Object.equals(Object o).If we had gone that route instead of implementing the IEquatable interface, we'd need to introduce this code in the RegularAnimal class:


public override bool Equals(object obj)
{
return ((obj != null) && (obj.GetType() == this.GetType()) && ((RegularAnimal)obj).sound == this.sound);


}





Useful links:
http://www.microsoft.com/mspress/books/sampchap/5353.aspx
http://msdn2.microsoft.com/en-us/library/ms131187.aspx
http://msdn2.microsoft.com/en-us/library/ms379564(vs.80).aspx

Thursday, February 1, 2007

Enum and DropDowns

So I need to get the name and value of an Enum to tie sensibly to a DropDownList. This is how you do it:

Enum definition:

public enum Categories
{
Wildlife,
Resorts,
Beer,
Other
}



To tie to DropDownList called ddCategory:

ddCategory.DataSource = Enum.GetNames(typeof(Categories));
ddCategory.DataBind();

To set the selected value:


ddCategory.SelectedValue = Enum.GetName(typeof(grr.Categories),1) // or just "Wildlife"

Tuesday, January 30, 2007

Object to DataTable

When I lay my hands on ASP.NET 2.0, I was thrilled to use Generics in the form of List( of T) and used it to create collections of my business objects. I was even more excited to use the ObjectDataSource control and bind it to a GridView. But when I tried sorting the GridView, I got a runtime error which said that sorting of List (of T) is not supported and suggested using a DataTable or DataSet! Googling for a few minutes, I learned that to enable sorting for my collection class (in this case a Generic List), I need to first create a custom collection and then implement the IComparable interface. And then, I had to setThe ObjectDataSource's SortExpression.

I was too lazy to do all this and also didn't want my code scattered in multiple events so I decided to write a bit of 'reflective' code to convert my List(of T) to a DataTable. All I had to do then is Databind the DataTable and let GridView handle sorting.

The class cList2Table has a method to convert your System.Generics.List(of T) to a DataTable. cList2Table takes a generic list as its constructor parameter and returns a DataTable when you call the GetTable() method.



Imports System.Reflection
Public Class cObjectToTable(Of T)
Dim objectCollection As List(Of T)
Public Sub New(ByVal objectCollection As List(Of T))
Me.objectCollection = objectCollection
End Sub
Public Function GetTable() As DataTable
Dim table As New DataTable
Dim objectType As Type = GetType(T)
Dim objectProperties As PropertyInfo() = objectType.GetProperties
'create a column for each property in the class
For Each propertyItem As PropertyInfo In objectProperties table.Columns.Add(New DataColumn(propertyItem.Name, propertyItem.PropertyType))
Next
For Each item As T In objectCollection
'create a new row based on the table structure we just created Dim row As DataRow = table.NewRow()
'copy object data to the datarow
For Each propertyItem As PropertyInfo In objectProperties
row(propertyItem.Name) = propertyItem.GetValue(item, Nothing)
Next
'add row to the table
table.Rows.Add(row)
Next
Return table
End Function
End Class

Now I usually like to see schema in the GridView during design time,So I filled my typed DataSet with the following snippet of code.

Dim loTable As New dsSection.dtSectionDataTable
loTable.Load(loObject2Table.GetTable().CreateDataReader)