Reducing code-bloat with anonymous methods

C# 2.0 introduced anonymous methods that allow programmers to create a callable block of code that uses parameters but is not declared as a method.  One of the places I find this to be most useful is with Invoke and BeginInvoke.  Often times, invoking a delegate is used internal to a class to perform asynchronous processing or to ensure code is executed on the GUI thread.

Events generally don’t have a particular thread their handlers are run on; it’s up to the code raising the event which thread is used.  Code that modifies WinForm control data must be run on the GUI thread.  If the code that modifies control data is in an event handler, you run the risk of causing hard-to-reproduce instability.  Rather than create a new delegate type, a method to be wrapped by your new delegate type, and code to instantiate the delegate when needed; you can use an anonymous method.  For example, if in the processing of an event you wanted to add an entry to a ListBox, to ensure the code didn’t cause any cross-threading exceptions or hard-to-reproduce instability you’d have to implement something like: 

    private delegate void StringToListBox(String item);

 

    private void AddStringToListBox(String item)

    {

        listBox1.Items.Add(item);

    }

 

    void myClass_MyEvent ( object sender, EventArgs e )

    {

        String item = DateTime.Now.ToString();

        listBox1.BeginInvoke(new StringToListBox(AddStringToListBox),

            new object[] { item });

    }

 


This lends itself to re-usability; but if you’re only using AddStringToListBox in the one event handler, that’s a lot of baggage.  Plus, you don’t get type safety passing an Object array to the delegate invocation.  Anonymous methods can reduce this down to a single call to BeginInvoke.  For example:

    void myClass_MyEvent ( object sender, EventArgs e )

    {

        String item = DateTime.Now.ToString();

        listBox1.BeginInvoke((MethodInvoker)delegate( )

        {

            listBox1.Items.Add(item);

        });

    }

Type safety is no longer an issue and you’re not polluting the class’ name space with a method that could be misused.  For WinForm control events this is not necessary because they are raised on the GUI thread.

Uses for yield Part One – Introduction

.NET has strong support for collections.  Even the built-in Array type implements ICollection.  The ICollection interface derives from the IEnumerable interface to allow easy enumeration of all elements in a collection.  In C#, the foreach statement uses the GetEnumerator member of IEnumerable (a class need not implement IEnumerable, it just needs a public GetEnumerator method that returns an IEnumerator object) to begin enumeration.

Previous to C# 2.0 foreach required the existence of the collection and all the elements contained therein, or a custom implementation of IEnumerator.MoveNext() and IEnumerator.Current.  Not an entirely unexpected demand, when you think of it.  If you wanted to provide the ability to enumerate all the elements of a particular set of data then that data would have to be calculated at the time of the set’s creation.  C# 2.0 introduced the yield keyword that meant you could implement an enumerable set that doesn’t require all its elements to exist at the beginning of the enumeration or creating an IEnumerator-derived class.

What does this do you for, the designer?  Well, you can employ lazy evaluation to distribute processing throughout several different points in time.  This is handy if you want to maintain a responsive UI, or you have to deal with high-latency resources like a web connection to a remote server or a connection to a remote database.  This is especially beneficial if you know your clients aren’t always going to enumerate all elements in your set (i.e. you don’t have to perform processing to create all elements in order to use the foreach pattern).  This effectively means the creation of your set can be O(1).

The documentation for yield provides a pragmatic example of creating an enumerable set of powers of a number up to a specific exponent.  A slight modification of the example shows that indeed the calculation of each power is in fact lazy, and distributed across time:

calculating 2^1
2^1 = 2
calculating 2^2
2^2 = 4
calculating 2^3
2^3 = 8
calculating 2^4
2^4 = 16
calculating 2^5
2^5 = 32
calculating 2^6
2^6 = 64
calculating 2^7
2^7 = 128
calculating 2^8
2^8 = 256

The yield statement has a couple of variations.  To provide the value at the current position, use yield return value.  Use yield break to signal the end of the set and to stop the enumeration loop.  For example, to implement an enumerator of a set whose limit is not known at the start of an enumeration:

    /// <summary>

    /// Get an enumerator for all the keys pressed until Esc

    /// </summary>

    /// <returns>IEnumerator object.</returns>

    public static System.Collections.IEnumerable GetPressedKeysUntilEscape()

    {

        do

        {

            ConsoleKeyInfo character = Console.ReadKey(true);

            if (character.Key == ConsoleKey.Escape)

            {

                yield break;

            }

            yield return character.Key;

        }

        while (true);

    }

    // …

    foreach (ConsoleKey key in GetPressedKeysUntilEscape())

    {

        Debug.WriteLine(key.ToString());

    }

Sudden "…you must have Terminal Server User Access permissions on this computer." Error.

I have a Small Business Server 2003 R2 Server running Team Foundation Server tucked out of the way to conserve desk space (three servers, two clients, two desks: not much space).  I don’t have it hooked up to a monitor (one: don’t have that many monitors, and two: desk space).  So, I’ve been merrily using Remote Desktop Connection (RDC) in Windows XP to connect to this server to perform my various administration tasks (like install service packs, hot fixes, etc.).


Well, I finally had a couple of cycles to install some hotfixes for the new daylight savings time changes to various components, so I sparked up RDC to get the ball rolling on my server–as I have done many times before.  I was greeted with a message box as I logged in:


To log on to this remote computer, you must have Terminal Server User Access Permissions on this computer. By default, members of the Remote Desktop group have these permissions. If you are not a member of the Remote Desktop Users group or another group that has these permissions, or if the Remote Desktop User group does not have these permissions, you must be granted these permissions manually.


Needless to say I was dumbfounded–it worked fine yesterday.  After a bit of searching, it appears it was the 120 day anniversary of creating this server and Terminal Server (which is what is used for an application server in Small Business Server) had “expired” (i.e. its grace period for CALs had expired).  I was used to installing Windows Server and setting up Terminal Server for remote administration (there was a setting for that in Windows Server, I honestly don’t remember what Small Business Server asked me when I installed; it certainly wasn’t clear it was different the other Windows Server installation processes).  Apparently I missed the memo that remote administration is now called “Remote Desktop”.  Clearly a WTF moment.


As it turns out, the hoops to get back to the ability of remote administration aren’t clearly documented (I actually couldn’t find any documentation on the process, I actually inferred the process from various non-Microsoft sources–there could be documentation somewhere, I just didn’t find it).  The process requires that Terminal Server be uninstalled, the server rebooted, and Remote Desktop be re-enabled.  A point-list of the steps:


  1. Run Add/Remove Programs (run “appwiz.cpl”)
  2. Click Add/Remove Windows Components (Alt-W)
  3. Uncheck Terminal Server
  4. Press Next>.
  5. Follow instructions, including rebooting.
  6. Open System control panel applet (run “sysdm.cpl”)
  7. Click Remote tab.
  8. Check Enable Remote Desktop on this computer. (because removing Terminal Server disables this)
  9. Click Select Remote Users…
  10. Make sure administrators is in the list.
  11. Click OK.
  12. Click OK. for the next dialog.
  13. Wait a few minutes for things to get up and running and you’re no ready for remote administration again.

I hope this helps someone get back up and running faster than I did…