Mad Props!

Omniscience is just a Google-search away.

Login

You're reading Mabsterama, the weblog of Matt Hamilton. Enjoy your stay.

LINQ with Reflection

I was toying last night with my work-in-progress WPF version of Comicster, specifically looking at file formats. I want this version to easily support opening and saving collections to multiple file formats, and the logical way to do that was via some sort of plug-in system.

Eventually (and by that I mean probably when .NET 4.0 is released) I'll look at using the Managed Extensibility Framework to power the file-format plug-in system. I found a great post by Brad Abrams which walks through a simple MEF tutorial, and it looks trivially easy to, say, scan a folder for plug-in assemblies and grab the relevant types from them.

In the meantime, though, I've settled on defining my various "core" file formats in a single assembly and using reflection to load them on demand. The idea is that I've created two simple interfaces: ICollectionReader and ICollectionWriter. I've separated the read and write functionality because some file formats will only support opening and not saving. Then in the same assembly I've defined a few classes that implement those interfaces. For example, XmlCollectionReader (for XML files - duh) and PackageCollectionReader (for CMXX files, see this post for more info).

Once those classes were defined, I needed a way, when the user clicks the "Open" button, to find which file formats were available to read. I settled upon this nifty LINQ statement:

var readers = (
       from t in Assembly.GetAssembly(typeof(ICollectionReader)).GetExportedTypes()
       where t.IsClass
           && typeof(ICollectionReader).IsAssignableFrom(t)
           && t.GetConstructor(new Type[0]) != null
       select Activator.CreateInstance(t) as ICollectionReader
   ).ToList();

So what's happening here? I'm asking for a list of all the public classes that live in the same assembly as ICollectionReader and implement that interface, and I make sure that they have a constructor which takes no parameters, because otherwise the call to Activator.CreateInstance would throw an exception. I end up with a list of instances of these "reader" objects.

Each reader object has two properties: "Extension" which is the file extension for the file format supported by that reader (eg ".xml") and "Filter" which is the friendly name for that file format to display in a file dialog (eg "XML Files (*.xml)|*.xml"). Using those properties I can construct an OpenFileDialog based on all of the known reader objects:

var ofd = new OpenFileDialog
{
    Title = "Open Collection",
    Filter = string.Join("|", readers.Select(r => r.Filter).ToArray()),
    DefaultExt = readers[0].Extension,
};

Once I show that dialog I can check that the filename the user selected has a supported reader:

var reader = readers.FirstOrDefault(r => string.Compare(r.Extension,
    Path.GetExtension(ofd.FileName), StringComparison.OrdinalIgnoreCase) == 0);
if (reader == null) { MessageBox.Show("No readers available for this file extension"); return; }

And lastly, use the ICollectionReader's "ReadCollection" method to open the file:

this.DataContext = reader.ReadCollection(ofd.OpenFile());

There's a little bit more code in my "Open" command's Execute handler to do some sanity checking, but this is the meat-and-potatoes of it. The thing I'm most proud of is the initial LINQ statement, but ironically that'd be the first thing to go if I were to move to MEF. Ah well - if you want to make a MEF omelette you have to break a few Reflection eggs.

For vs Foreach Performance

A comment on a recent post of mine on Stack Overflow suggested that I replace my "for" loop with a more "modern" version using "foreach" and Enumerable.Range. In other words:

// replace this
for (int i = 0; i < 1000; i++)
{
}

// with this
foreach (var i in Enumerable.Range(0, 1000))
{
}

 

I can't really say that I prefer one over the other, although the second approach does look kinda cool. It'd be even nicer if there were some native support for ranges in C# like there was in Pascal:

// borrowing Pascal's range syntax
foreach (var i in [0..1000])
{
}

... but I digress. What I wanted to talk about in this post is my findings on the performance difference between a simple "for" loop and a "foreach" over Enumerable.Range. I timed two long loops using a Stopwatch:

const int count = 100000000;
var sw = new Stopwatch();

sw.Reset();
sw.Start();
for (int i = 0; i < count; i++)
{
}
sw.Stop();
var forTime = sw.ElapsedTicks;

sw.Reset();
sw.Start();
foreach (var i in Enumerable.Range(0, count))
{
}
sw.Stop();
var foreachTime = sw.ElapsedTicks;

Console.WriteLine(forTime);
Console.WriteLine(foreachTime);
Console.WriteLine((float)foreachTime / forTime);

 

The result:

4869915
14286932
2.933713

So the Enumerable.Range approach is three times slower than a simple for loop.

I guess in a real-world situation where the body of the loop is the thing taking the bulk of the time, there wouldn't be that big a difference, but it's interesting that the two approaches differ in performance by so much. Something to be aware of if you're very performance-oriented in your code, anyway.

Identity is Hard

I just finished listening to a recent episode of .NET Rocks! featuring Michele Leroux Bustamante in which she talks about claims-based identity and, more specifically, Windows CardSpace.

Michelle is obviously very knowledgeable about the subject and is really evangelising the technology, but as I was listening to the episode, glassy-eyed feeling the whoosh as the words flew over my head, it struck me that I'd heard this episode before. So upon arriving at work this morning I had a browse back through the past episodes of DNR.

Ah! Here we are! Episode #173 - Kim Cameron on Digital Identity. Dated 24/4/2006. For those keeping score at home, that's two and a half years ago. What else were we talking about two and a half years ago? Well, let's look at some of the topics in shows around that time. AJAX. Vista. LINQ was just starting to generate buzz.

What this tells me is that in two and a half years we've seen almost no development around Identity Cards. Hardly anyone seems to be using it. Certainly none of the "big" identity providers (Yahoo, Google etc) have bothered becoming a card provider. Windows Live ID has tinkered with it - if you can find the right URL you can sometimes see, buried in the fine print, a link to associate your Live ID with a self-generated card. Microsoft themselves aren't an identity card provider though.

Is it really that hard? Has it failed that badly?

Stack Overflow has proven to me that OpenID is a viable way to "federate" identity. It's complicated, sure, but if a Yahoo or Google user can use their own credentials to set up a user at Stack Overflow and sign in, that's a win.

Just for the record, myopenid.com is supporting Identity Cards in some way. I managed to associate my own OpenID with a self-generated card on my laptop. Confusingly, though, when I attempted to use that same card to sign in only a month or so later, OpenID told me that it didn't recognize it. I re-associated it and found that now two cards were associated with my ID. WTF? If my Identity Card can just ... I don't know ... change like that then how is it worth anything?

I had big hopes for Windows CardSpace when Microsoft first started talking about it pre-Vista. I still do have big hopes for it. But if it hasn't gained any traction in over two years (compared with other technologies like LINQ and AJAX) then I wonder how long it'll be before my mum and dad are signing into their Internet Banking application with an Identity Card.

Time For Your Wake-Up Call

Not a lot going on in my life right now, which you’ve probably discerned from my lack of updates. However something interesting happened last night.

At about 4am we were awoken by a sound from outside the house. It was almost the sound of the security screen door banging in the wind, except that the screen door is closed and locked, and there was no wind. Instead, it was the sound of someone trying to open the screen door. At four in the morning. Uninvited.

We leapt out of bed and turned on the light. I went to open the door but Sal urged me not to. By the time we were up the noise had stopped, so whoever it was trying to break in must have given up. Sal called the police to let them know (we didn’t exactly expect them to come sirens blaring) and they suggested that they’d had a few reports from the region of drunk people trying doors, perhaps not realising that they were at the wrong house. In hindsight that sounds unrealistic but at 4am it was quite plausible.

We’ll see if our mystery caller visits again tonight. This time I might actually answer the door wielding something heavy.

In other news, my brother called me tonight to inform me that he has dinner plans on Thursday night with Lance Henriksen. I kid you not. Jealous.

USA: Meet the Parents

Here are some random thoughts about our trip to the US with both sets of parents.

The flight to New Zealand was over fairly quickly, but we had a four hour wait in New Zealand airport before our next flight to LA. I was looking forward to that, thinking that there’d be plenty of shops and a few places to eat, but it turned out to be about a dozen shops at most, and a Burger King. So we sat around for four hours talking and/or sleeping.

Air New Zealand (and presumably the other major airlines) all have monitors in the back of the seats and a remote control built into the armrest so you can watch movies, TV shows and music videos. So instead of being limited to the movie they decide to show you, you get to choose. Surprisingly, I didn’t watch a movie in the entire twelve hours. Towards the end I watched a few episodes of The Simpsons and one of The Mighty Boosh, but in general I slept and listened to music on my MP3 player.

Disneyland was as remarkable as I remember. When we walked over to Downtown Disney (the freely-accessible area with shops and restaurants) the night we arrived, dad asked me what was on the other side of the entry gates that was so amazing. I didn’t bother trying to explain – his reaction the next morning said it all. In fact, dad’s reaction to pretty much every part of Disneyland was the highlight of the Los Angeles leg of our trip. I also scored a souvenir photo of him and me on Space Mountain which made for a great father’s day present.

On the second day of the LA leg we booked a tour of Hollywood, Beverly Hills and Venice Beach. That was fun, but I have to say that Venice Beach is nowhere near as glamorous as it looks on TV. In fact, it’s downright seedy. We had nearly an hour to kill there and I was glad when it was over. I think everybody enjoyed the tour though, if only to learn why we only stayed in LA long enough to do Disneyland.

The coach trip from LA to Las Vegas was really interesting. We crossed the Mojave desert, which sounds boring but provided some really interesting landscape. All in all it took about the same amount of time as it would to get to the airport, check in, fly to Vegas and then catch a shuttle to the hotel, so I think it was worth it.

Vegas was just as we remembered – overwhelming. Big, hot, bright, noisy … fantastic. The parents were simply dumbstruck. The only thing we didn’t like about Vegas (and I don’t remember it being as big a thing last trip) was the smoke. Since Australia banned the smoking of cigarettes in pubs and clubs a year or two ago, it was a different experience for us to walk into a casino and have to breathe in second-hand smoke. I hope the US catches up to us in this regard soon.

I’d forgotten about the people standing on the Las Vegas strip handing out cards featuring scantily-clad women promoting local strip-joints and the like. It really does detract from the experience. For some reason all of these people looked alike. They were short and dark-skinned. Perhaps they were munchkins. Porn munchkins.

While in Vegas we took a helicopter tour to the Grand Canyon, and that was incredible. We actually got to land in the canyon itself and have some drinks. It’s an impressive sight from above and from within.

After Vegas we flew to San Francisco, and I think everybody got a bit culture-shocked coming from the tourist-heavy areas to a “real” city. Our hotel, only a few blocks from Union Square on Geary Street, was right in the thick of the dirty, homeless-ridden streets. It wasn’t until we’d slept that night and we were able to take the parents over to Fisherman’s Wharf the next morning that everybody realised why we liked SF so much.

We took the ferry over to Alcatraz the next day, and then partook in some ridiculous chocolate sundaes in Ghirardelli Square. On the following day we did a coach tour to Muir Woods and Sausalito, which was fun for all.

The flight home went reasonably quickly. I slept most of the way, then watched Monsters, Inc. to pass the time before we landed in New Zealand. With only a 90-minute wait this time, we were on the plane to Melbourne before we knew it.

I had just about forgotten how annoying the whole tipping/tax thing is in the States. You never pay the advertised price for anything. We ate at some nice restaurants, but the meals are advertised as, say, $25 (which sounds reasonable) and then you end up getting slapped with a 7.5% tax, then a 3.5% “San Francisco Health Fee” or something, and then an 18% gratuity. Suddenly that $25 steak doesn’t seem such good value. The food over there in general is very good though – on par with Australia in many ways.

Vacation Ending

I was going to entitle this post "Holiday Ending", but then I remembered that over here it's called a "vacation" so I changed it.

I'm writing this from a PC in our hotel lobby in San Francisco. It's our last day here after a twelve night stay that has felt like a month or more. It has been a great trip with many high points and only a few lows, all of which I will elaborate on in a post when I get back to Australia.

It's been a weird day today, since we had to check out by 11am but won't be picked up by our shuttle bus for the airport 'til 6pm. We've had all this time to kill looking round the shops in SF, but for the most part we've seen what we wanted to see in the last three days.

Anyway, can't wait to get back to my own couch, bed and (most importantly) shower (why do hotels assume that we're all munchkin-size?). Will write more from there and catch up on all the twitter-gossip etc.

Holiday Time!

One last blog post before we fly out to the US on Saturday.

Things I'll miss while I'm away:

  • Constantly monitoring my reputation on Stack Overflow
  • Constantly refreshing Twitter to see what people (some of whom I've never even met) are up to
  • AWDNUG next Tuesday, where Kym will be talking about SQL Server security
  • My Xbox 360

Things I won't miss:

  • Moving office furniture at work while the renovations are going on
  • Doing work at work after the renovations have finished
  • Having to save money for this holiday :)

So anyway, this blog will probably be darker than usual for the next two weeks. If I get a chance to jump on a PC over there I'll post, but otherwise brace yourself for some photos etc later in the month.

Dynamic Type Instantiation on the Compact Framework

Someone on Stack Overflow today asked how to dynamically instantiate a type that doesn't have a default constructor on the .NET Compact Framework. As an example, let's say you have a type called "Foo" which takes some parameters in its constructor:

public class Foo
{
    public Foo(string s, int i)
    {
        // do stuff with s, i
    }
}

So now with no information other than the name of the type as a string ("Application1.Foo" in this case), you want to spin up an instance of that type.

On the full framework you'd use an overload of Activator.CreateInstance, like this:

string s = "Application1.Foo";
Type t = Assembly.GetExecutingAssembly().GetType(s);
Foo f = Activator.CreateInstance(t, "hello", 17) as Foo;

But on the Compact Framework, the overload for CreateInstance that lets you pass extra parameters doesn't exist. Instead, you have to use the GetConstructor method to determine the correct constructor to use. Here's an extension method for the Assembly type to make it easy!

public static class AssemblyExtensions { static object CreateInstance(this Assembly a,

string typeName, params object[] pars) { var t = a.GetType(typeName); var c = t.GetConstructor(pars.Select(p => p.GetType()).ToArray()); if (c == null) return null; return c.Invoke(pars); } }

Note the use of LINQ's "Select" method to turn the parameters into an array of types! Pretty funky! Now you can just type this, even on the Compact Framework:

Foo f = Assembly.GetExecutingAssembly()

.CreateInstance("SmartDeviceProject1.Foo", "hello", 17) as Foo;

Did you get his permission?

Those who know me well enough know that I hate, with the fire of a thousand suns, companies whose business is based on mobile phone ringtones, SMS scams etc. To me they're only one step up from spammers, and it burns me every time I see one of their horrifically-bad advertisements on TV.

The other day one such ad came on, describing how you can find your "perfect match" simply by sending your name via SMS to the number on your screen. You know the drill. Anyway, hidden in the small print at the bottom of the screen (the bit that tells you that you're surrendering your firstborn child if you so much as pick up your phone after watching this ad) was this text:

ask bill ayers permission

Do I need to write "sic" here? I want to make it clear that none of that is a typo.

So after reading that, I jokingly posted this to Twitter:

Who is Bill Ayers? There's a dodgy SMS-dating ad on TV right now whose fine print includes the text, "bill ayers permission".

Nobody replied, but something magical happened. Within 24 hours, this story appeared on Digg: Obama Ayers Ad Responds To McCain Attack. It turns out that Bill Ayers is some sort of political activist from the US!

So now I'm really confused! A slimy SMS subscription company in Australia is demanding that you get William Ayers' permission before sending them your name! Ok, they forgot to include the trailing apostrophe, but their business model suggests that they're slightly retarded, so I can forgive them that.

What's the story, Bill Ayers? Why do I need your permission!!??

Tropic Thunder, Stack Overflow and More

It occurred to me this morning that I haven't made a simple "personal news" post here for a while, so here's all the goss.

This weekend was my mother-in-law Di's 60th birthday, and we went out to the local Chinese restaurant to celebrate. Chinese food is a little bit passe nowadays if you ask me, but the previous generation still seems to think it's the bee's knees, so we were happy to accommodate. Unfortunately the restaurant was just abysmal. Despite us having booked for 30 people, they only had one waitress working, so it was nearly an hour before we even had our orders taken. Then, after the three other tables in our party had all finished their main meals, we were asked if we wanted any dessert. This, mind you, was before we had been served our own main meals! It was obvious that they'd completely forgotten them, and the end-result was a rushed, tasteless array of meals that we shouldn't have had to pay for. What's worse is that our table included Di herself. Heck of a birthday party. And the fact that they still charged us full price really rubs me the wrong way. I'm kicking myself for paying. For Albury folks reading this, I'm talking about Ocean City restaurant in Lavington, so dine there at your own peril.

Sal and I caught Tropic Thunder yesterday and thoroughly enjoyed it. As many have written, Robert Downey Jr stole the show. His Australian accent is quite appalling, but it works well within the film's satirical context. Unfortunately I found his "southern black man" accent almost impossible to understand, although that may have been intentional.

Over the past few weeks I've been almost completely caught up in the fury that is Stack Overflow, the new forum/wiki project from Jeff Atwood. If you're a software developer then you're probably all over this already, and if you're not then you probably aren't interested, but I will say that the "reputation" and "badge" system (analogous to Xbox Live's "gamerscore" and "achievements") have made this site very addictive. It makes you want to answer questions, which means it's a very helpful ecosystem.

At work, we're in the middle of an office renovation which will bring all developers together into a shared workspace. It's a step back in that a couple of us lose our own private offices, but we gain our own "brainstorming" meeting room and a lot of wall space for whiteboards, so it should make for a collaborative, productive environment.

And lastly, we fly out to the US in two weeks' time for a 12 night holiday with both sets of parents. Sal and I decided at the beginning of the year that we'd take our parents to Vegas and show them where we were married. It was a way to take the parents away from it all for a while and share some quality time. Since making that decision, some others have decided to tag along, which has kind of defeated the purpose of the trip, but we'll try to make lemonade and have a good time.