Friday, December 28, 2007

Foreach in C# uses ducktype

This came to me as little surprise. May be for others too.. including my manager with whom I had discussion about this once.

So far I was under the impression that for any class to be used inside foreach, it must implement IEnumrable interface. I remember myself asking this question in interview to many guys and even answered same question in the interview when I was on other side of the interview table.

Today I am going to proove that its not true anymore.... (what??????)

Well, all you need for your class to be used inside foreach is
  • have GetEnumrator method with no input params and return type object which has two method MoveNext (returns bool) and property Current with getter (returns object)
Still not want to believe me? well here is the code snippet
using System;

namespace ForEachDemo
{
public class Program
{
private static void Main(string[] args)
{
// the following complies just fine:

Person[] peopleArray = new Person[3]
{
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};

People peopleList = new People(peopleArray);
Console.WriteLine("This program demostrate usage of foreach without implementing IEnumerable interface.");
foreach (Person p in peopleList)
Console.WriteLine(p.firstName + " " + p.lastName);

Console.WriteLine("Press return key to exit...");
Console.ReadLine();
}
}

public class People
{
private Person[] _people;

public People(Person[] persons)
{
_people = persons;
}

public PeopleEnum GetEnumerator()
{
return new PeopleEnum(_people);
}
}

public class PeopleEnum
{
private Person[] _people;

// Enumerators are positioned before the first element
// until the first MoveNext() call.
private int position = -1;

public PeopleEnum(Person[] list)
{
_people = list;
}

public bool MoveNext()
{
position++;
return (position < _people.Length);
}

public void Reset()
{
position = -1;
}

public object Current
{
get
{
try
{
return _people[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
}

public class Person
{
public Person(string fName, string lName)
{
firstName = fName;
lastName = lName;
}

public string firstName;
public string lastName;
}
}

just copy paste this in empty console app and look at the result. You will be more than surprised to see what is happening....

Obvious question you might want to ask here is what the F**k is going on here. The answer to this is foreach in C# (may be even VB.NET, I didn't tried my self) uses ducktype.

Develop smartly:)

NUnit 2.4 is here with support to VS2008 and RowTest

This is really cool. So far NUnit was legging one cool feature of MBUnit - RowTest

Not any more. NUnit 2.4 is here to kick that legging :)

IDE support is included as extension in Test Driven .NET 2.11. you can download from here. Row extension binary is here

Click on one for 2.4.5 and the zip file will have two dlls (NUnitExtension.RowTest.AddIn.dll and NUnitExtension.RowTest.dll )

After you install Test Driven .NET 2.11, navigate to '%ProgramFiles%\TestDriven.NET 2.0\NUnit\2.4' and create subdirectory called addins. Then copy NUnitExtension.RowTest.AddIn.dll inside addins. Its is required that you don't keep any other non dll file in this folder. Click here to know why?

Finally use NUnitExtension.RowTest.dll as your BinRef in the UnitTest or IntegrationTest Project.

Now writing test is very simple. Here is the snippet

using NUnitExtension.RowTest;

namespace Mahendra
{
[TestFixture]
public class RowTestDemoTester
{
[RowTest]
[Row(100,20,120)]
[Row(10,11,21)]
[Row(20,30,51)]
public void Simple_Addition_Test(int a, int b, int sum)
{
Assert.AreEqual(sum, a+b);
}
}
}

What is even cool is, once you install TestDriven.net 2.11, right click on your test project and you will see under Test With submenu -> all your previous installation of NUnit as well. i.e. in my case it is NUnit 2.2 and NUnit 2.4

If you choose to test using NUnit 2.2 (any one prior to NUnit2.4) all the RowTest will be simply ignored without giving any compilation error or failing test.

Develop smartly :)

Thursday, December 27, 2007

T-SQL hacks, getting Nth highest, top N and paging

Its time for T-SQL

Some of the common scenario for data retrieval could be (without using TOP keyword remember oracle doesn't have TOP)
  • Getting Nth highest/lowest value column
  • Getting top Nth highest/lowest value columns
  • For paging purpose, getting X row for page n i.e. getting column 11 to 20)
Here is some of my hecks for obtaining above results. I have used NorthWind orders table for this purpose.

For bullet point 1: Getting Nth highest, here is the t-sql
select * from orders a
where N-1 =
(select count(orderID) from orders b where b.orderid > a.orderid)

trick here is self join. Notice closely here what I am doing.
  1. For each row of inner table (alias b) get count of columns from the same table i.e. self join, who has value higher than current row of outer table.
  2. Use common math like this: for any column, in outer table, to be Nth highest, it must be lower than N-1 columns of inner table.
  3. Select that row. It is your Nth highest row
Apply same logistic and you get N highest/lowest row like this
--TOP N highest
select * from orders a where 10 >
(select count(orderID) from orders b where b.orderid > a.orderid) order by orderid desc

--TOP N lowest
select * from orders a where 10 >
(select count(orderID) from orders b where b.orderid < a.orderid)

The only difference between last two queries are inner query condition. By same methodology mentioned above, first query is looking for number of columns having higher value, second one looks for number of column having lower value.

Well well well....
so what is the practical use of all this crap then? Hmmmm....
May be the answer is bullet point number 3 : For paging purpose, getting X row for page n i.e. getting column 11 to 20)

Now I am applying above logic in two fold to get rows for paging .

My input here would be page size and index of the page (1,2,3 base).
Logic here is to get 11 to 20 row, first get 1 to 20 and then filter out 1 to 10

Here is the query
begin
declare @page_size int, @page_number int

set @page_size = 10
set @page_number = 3

select * from orders a
where @page_size*@page_number >
(select count(orderID) from orders b where b.orderid > a.orderid)
and not exists
(select c.orderid from orders c
where @page_size*(@page_number-1) >
(select count(orderID) from orders d where c.orderid < d.orderid)
and a.orderid = c.orderid
)
order by orderid desc
end

*********************************************************************
Again, I know some of the results above can be obtained easily with sql 2k5 (i haven't spoiled my hand with this yet though) and up,this exercise is to strengthen understanding of how select gets executed. The whole logic is to closely understand execution of query and thereby manipulating it to get desired result. Alternatively it can be helpful as interview question for either side of the table :)

Setting up wireless router

This time nothing my own creation. I was setting up my wireless router for the first time and was concern about security stuffs as wireless is more vulnerable to hacking. I can to this page. Sounds interesting reference to me. May be it can help somebody else too.