Anonymous delegates rock

I’m finding I really like C#’s anonymous delegates. I’ve used them several times now in various ways. Normal delegates are already a Good Thing, however basically they are C(++) function pointers with a much nicer syntax. Anonymous delegates do clever things like having the ability to access local variables.  This just provides some syntactic sugar and the compiler under the hood creates classes to hold the variables and puts the anonymous method on that class.  Take a look with a decompiler and you can see what gets generated.

One “obvious” pattern I’ve seen people here come up with independently is to invoke something on a worker thread from a UI thread, or the other way round (and sometimes both by nesting one delegate inside another).

However another use I like is what I want to share today.

I’ve been using WMI a bit recently. WMI can be used (amongst other things) to get information about remote computers.  I won’t go into the detail here – there’s plenty on the web about it (http://en.wikipedia.org/wiki/Windows_Management_Instrumentation).  I may even do a blog myself on it one day.

To access WMI via .NET you use the System.Management namespace.  There are various ways to call and get the information, but a common example is to run a query in a SQL-esque way – e.g. “select TotalPhysicalMemory  from Win32_ComputerSystem” will get you the total physical memory on a machine.  However to run a query like this requires quite verbose code, not least because a lot of objects you create need Dispose()ing of afterwards.


ManagementScope scope = new ManagementScope( "\\mymachinename\root\cimv2" );
scope.Connect();
objQry = new ObjectQuery( "select TotalPhysicalMemory from Win32_ComputerSystem" );
UInt64 totalMemory = 0;
using( ManagementObjectSearcher searcher = new ManagementObjectSearcher( scope, 
{
    using( ManagementObjectCollection collection = searcher.Get() )
    {
        foreach( ManagementObject obj in collection )
        {
            totalMemory = (UInt64)obj["TotalMemory"];
            obj.Dispose();
        }
    }
}

So great – i’ve got that out, but I want to find out how much free disk it has as well.  This query is “select Name,FreeSpace from Win32_LogicalDisk where DriveType=3” and could well return me more than one disk. (DriveType=3 means physical disks on this machine as opposed to mapped network drives etc.)

I can reuse the scope so the extra code is…

objQry = new ObjectQuery( "select Name,FreeSpace from Win32_LogicalDisk where DriveType=3" );
Dictionary<string,UInt64> drives = new Dictionary<string,UInt64>();
using( ManagementObjectSearcher searcher = new ManagementObjectSearcher( scope, 
{
    using( ManagementObjectCollection collection = searcher.Get() )
    {
        foreach( ManagementObject obj in collection )
        {
            drives.add( (string)obj["Name"], (UInt64)obj["FreeSpace"] );
            obj.Dispose();
        }
    }
}

But that instantly offends me – I have 13 lines of code, of which 10 are the same as above.  That’s 10 lines repeated whenever I want to run another query.

Instead I can define a method like this:

private delegate void UseManagementObject( ManagementObject obj );

private static void DoWmiQuery( ManagementScope scope, string qry, UseManagementObject callback )
{
    ObjectQuery objQry = new ObjectQuery( qry );            
    using( ManagementObjectSearcher searcher = new ManagementObjectSearcher( scope, objQry ) )
    {
        using( ManagementObjectCollection collection = searcher.Get() )
        {
            foreach( ManagementObject obj in collection )
            {
                callback( obj );
                obj.Dispose();
            }
        }
    }
}

Now I can reduce my 2 previous pieces to this:

ManagementScope scope = new ManagementScope( "\\mymachinename\root\cimv2" );
scope.Connect();
UInt64 totalMemory = 0;
DoWMiQuery( scope, "select TotalPhysicalMemory from Win32_ComputerSystem", delegate( ManagementObject obj )
                   {
                       totalMemory = (UInt64)obj["TotalMemory"];
                   } );

Dictionary<string,UInt64> drives = new Dictionary<string,UInt64>();
DoWMiQuery( scope, "select Name,FreeSpace from Win32_LogicalDisk where DriveType=3", delegate( ManagementObject obj )
                   {
                       drives.add( (string)obj["Name"], (UInt64)obj["FreeSpace"] );
                   } );

Jobs a good’un 🙂