One of the nicer spots

Posted 3 months and 3 week(s) ago

No alternative to graphics, sorry

For a long time i have been wanting to do some posts on things i like or dislike about C# and the whole .net thingy, and since i'm using it everyday at work there is a remote possibility that i will hit on something interesting.

The first thing i will cover is the yield keyword. For python developers this is all old hat, but for C# developers it is a great improvement to the language.

Basically it rids you of implementing all sorts of "crazy" iterators to supply a generic interface to collections of internal data that you wish to expose.

Normally you try to write generic code, and make your objects as loosely coupled as makes sense, this introduces a problem when you are working with internal collections of objects.

For instance, consider a Department containing a collection of Student(s) - in the first implementation this could be implemented as a simple vector of Student(s) (a List in .net), and you might expose the internal data structure by providing a Students property like the following:

   IList<Student> Students
   {
      get
      {
         return m_students;
      }
   }

Ok, so far so good, the users of the Department class can now see which students belong to a particular Department, but the users of the class can still modify the collection, they believe they can index the list by integer index, and so on, and so on, the IList<> interface is _very_ broad. You could expose it as an ICollection<> but that would still expose the collection. What you really should do, is have the Students property return an IEnumerable<Student> instance.

This will make the internal way that a Department class represents students completely independent of the way users of the class access them. Luckily List<Student> already exposes the IEnumerable<> interface - so, easy!

   IEnumerable<Student> Students
   {
      get
      {
         return m_students;
      }
   }

What happens now is that someone comes along and says - hey! Finding out whether or not a student is in a particular Department just by traversing a list is too slow! So you decide to go with something like a Dictionary mapping from student id (SSN or something similar) to the Student object. But you still want users to be able to, simply, traverse the list of students. With a Dictionary that is not so easy, even though a Dictionary exposes the IEnumerable<> it does so for a special Dictionary.KeyValuePair type - which is no good for our purpose. This is where yield will help us for the first time - check out how i implement an IEnumerable<Student> class in 5 lines:

   IEnumerable<Student> Students
   {
      get
      {
         foreach ( KeyValuePair<int, Student> pair in m_students )
         {
            yield return pair.Value;
         }
      }
   }

Isn't that just almost pythonic'ly beautiful? Isn't that just the simplest way possible to implement that particular task?

--

An even nicer example.

Where it will really save your ass is if you need to traverse a tree structure sequentially, then you just yield your way on to beatiful code - no more note keeping while trying to traverse the tree correctly. Just implement something simple like the following:

class TreeNode
{
    List<TreeNode> m_children;
    ...
    IEnumerable<TreeNode> AllChildren
    {
        get
        {
            foreach ( TreeNode child in m_children )
            {
                yield return child;
                foreach ( TreeNode subChild in child.AllChildren )
                {
                    yield return subChild;
                }
            }
        }
    }
}

This is pretty much as simple as you are going to get, of course it would be nice with support for a call to yield to automatically yield every member of another IEnumerable<> object, such that the innermost foreach could be replaced with a simple yield child.AllChildren;. That would be _really_ awesome. Does anybody know where to report enhancements (and bugs for that matter) to mirosoft?

You still have the same cross-thread issues as with List<> and Dictionary<>'s implementations of IEnumerator<> but hey, you can't have it all!

Cheers!


-- A microsoft fanboy!

P.S. I haven't even scrached the surface of what the yield keyword can do for you - there is no requirement that you can only expose data throught yield, how about an implementation of python's xrange(from,to,increment) in C#:

   IEnumerable<int> Range(int _from, int _to, int _increment)
   {
      for ( int i = _from; i < _to, i += _increment ) return yield i;
   }

Strangely pleasing! (bonus points for writing a generic version)

Or perhaps a way to filter the output:

   IEnumerable<Student> Seniors
   {
      get
      {
         foreach ( Student student in m_students )
         {
            if ( student.TimeOnCampus > 3 ) yield return student;
         }
      }
   }

Which of course is horribly specific, why not generic' it up a bit:

   IEnumerable<Student> FilterStudents ( Predicate<Student> _filter )
   {
      foreach ( Student student in m_students )
      {
         if ( _filter(student) ) yield return student;
      }
   }

Not hard to see why lambda expressions is part of the new C#, and LINQ also seems closely related to this.

It's not python, but it's is pretty close for a statically linked language without template (as in C++) support.


Hey, i actually said something positive about something microsoft did! I _must_ be getting old, maybe i should pick up smoking again.


Comments:


Post comment:

Name:
Email (will not be exposed):
Website:
Comment:

To avoid having bots and other bastards (read: spammers) post to this forum, you're post will not show up until you click a link in a mail message sent to your email address.