Python philosophy holds that "readability counts" and so after giving up the world of C++, templates, and STL one finds a calm bliss in the "obviousness" of most python code. C# made big strides in readability and the IDE-supported intellisense and refactoring made a big difference, but with the new features of C# 3.0 the difference is dramatic. At times it almost feels like "Python with braces". (Pythonistas: I make no claim whether Python with braces is a desirable thing.)

Let's take a look at real code. I translated some python code to C# to illustrate my point. The original IronPython code comes from here http://www.ironpython.info/index.php/A_Notify_Icon (the author is Michael Foord). I just copied it into VS 2008 and started translating.

What the sample does: creates a tray icon. Watches a folder. When files are modified in the folder, the tray icon shows a balloon.

image

On the left, python (IronPython of course). On the right C# 3.0 via Visual Studio 2008 Beta 2. 

Iron Python code C# code
# IronPython
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import (
    Form, FormWindowState,
    NotifyIcon, ToolTipIcon
)

from System.IO import FileSystemWatcher

from System.Drawing import Icon

from System.Threading import (
    ApartmentState, Thread, ThreadStart
)


path = r"c:\path_to_watch"

def thread_proc():
    f = Form()
    f.ShowInTaskbar = False
    f.WindowState = FormWindowState.Minimized
    
    n = NotifyIcon()
    n.Icon = Icon(r"C:\path_to_icon\icon.ico")
    n.Text = "Watching" + path
    
    w = FileSystemWatcher()
    w.Path = path
    def handle(w, a): 
        n.ShowBalloonTip(10, str(a.ChangeType), 
                         a.FullPath, ToolTipIcon.Warning)
    w.Changed += handle
    w.Created += handle
    w.Deleted += handle
    w.EnableRaisingEvents = True
    
    n.Visible = True
    def handle(*args): 
        f.Activate()
    f.Shown += handle
    f.ShowDialog()


t = Thread(ThreadStart(thread_proc))
t.ApartmentState = ApartmentState.STA
t.Start() 

# Sleep for 90 seconds so that you can 
# see the notify icon in action
Thread.Sleep(90000)

# Then die...
t.Abort()

// C# 3.0
using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace ConsoleApplication1
{
    class Program
    {
        static string path = @"e:\foo";

        static void thread_proc()
        {
            var f = new Form();
            f.ShowInTaskbar = true;
            f.WindowState = FormWindowState.Minimized;

            var n = new NotifyIcon();
            n.Icon = new Icon(@"e:\foo\icon.ico");
            n.Text = "Watching Path";

            var w = new FileSystemWatcher();
            w.Path = path;

            FileSystemEventHandler handle1 = (sender, e) =>
            {
                n.ShowBalloonTip(10, e.ChangeType.ToString(),
                e.FullPath, ToolTipIcon.Warning);
            };

            w.Changed += handle1;
            w.Created += handle1;
            w.Deleted += handle1;
            w.EnableRaisingEvents = true;

            n.Visible = true;
            EventHandler handle2 = (sender, e) =>
            {
                f.Activate();
            };
            f.Shown += handle2;
            f.ShowDialog();

        }

        static void Main(string[] args)
        {
            var t = new Thread(new ThreadStart(thread_proc));
            t.ApartmentState = ApartmentState.STA;
            t.Start();

            //# Sleep for 90 seconds so that you can 
            //# see the notify icon in action
            Thread.Sleep(90000);

            //# Then die...
            t.Abort();
        }
    }
}

Notes on the translation

  • overall very straightforward
  • easy parts were adding the "var" keyword, semicolons, replacing the string literals
  • trickier was remembering how to setting up the lambda expressions for the handlers (ones need to find the strongly-typed delegate in order to create the lambda expression)
  • There are still python features that don't map as cleanly, but the basics look good.
  • Note that I did not even leverage the power of C# 3.0 extension methods which provide another capability that maps nicely to a python feature