Monday, July 2, 2012

Why I've Grown To Love C#

I admit, I'm somewhat of a Linux programming snob. Why? Mostly ease, somewhat because it was how I was taught in school, and partially because I love the idea of FOSS.

But times change quickly, and I was thrown onto a C# project with a bunch of folks who were quite used to a Windows environment. Despite the obvious hassle of having to use Windows and deal with Visual Studio, .NET has some pretty cool things to offer.

Take the example where I have a class of students; my goal is to find their averages and get the student with the highest grade so I can single them out to the rest of the class. Let's first do this in vanilla C++:

Vector<Student> Students; // contains a short int array of test scores called Tests
...
short highestGrade = -1;
Student teachersPet;

for (int i=0; i<Students.Size(); i++)
{
    unsigned int sum = 0;
    for (int j=0; j<Students[i].Tests.Size(); j++)
    {
        sum += Students[i].Tests[j];
    }
    sum /= Students[i].Tests.Size();
    if (sum > highestGrade)
    {
        highestGrade = sum;
        teachersPet = Students[i];
    }
}

That's a mouthful. Not including intialization code, there are 7 lines of "useful" code, 5 auxiliary variables, 1 division, and a bit of array indexing. Let's perform a literal translation to C#:

List<Student> Students;
...

Student teachersPet;
short highestGrade = -1;
foreach (student in Students)
{
    int avg = 0;
    foreach (score in student.Tests)
    {
        avg += score;
    }
    avg /= student.Tests.Count();
    if (avg > highestGrade)
    {
        highestGrade = avg;
        teachersPet = student;
    }
}
</code></pre>

Pretty much the same code. We are utilizing the foreach operator, which lets us get away without any array indexing. Still 7 lines of "useful" code, still 3 auxiliary variables. Assuming the Tests property in the Student class is a array of ints, we can find the average in a much more succinct fashion:

foreach (student in Students)
{
    if (student.Tests.Average() > highestGrade)
    {
        teachersPet = student;
        highestGrade = student.Tests.Average();
    }
}
Four lines of functional code, two auxiliary variables. Why stop there? Using a LINQ-based approach and adding an Average property to the Student class, we can get rid of all auxiliary variables and find the max using the average in one line of code:

var teachersPet = Students.ForEach(x => x.Average = x.Tests.Average()).MaxBy(x => x.Average);

The same functionality in a single contiguous line of code! Not to mention these Average, ForEach, and Max functions can be better optimized by our compiler than what we had before. But is that as far down as we can squish it? What if we dropped the entire ForEach statement and just compared the averages:

var teachersPet = Students.MaxBy(x => x.Tests.Average());

And that's why I've grown attached to C#. LINQ is probably my favorite thing to ever come out of Microsoft

No comments:

Post a Comment