Words Software Fun Stuff About me

RSS feed for my blog
Atom feed for my blog
CDF feed for my blog

Sunday, June 26, 2005

Working around the new() constraint in C# 2.0 generics 

I'm posting this a placeholder for folks Googling for a solution to an annoying limitation of the new() constrain when using generics in C# 2.0

.NET 2.0 introduces a new feature called generics that lets you build parameterized types, which amongst other things lets you easily make typesafe lists and dictionaries (so no more casting values from ArrayList or Hashtable).

On neat use of generics is for building generic factories. Lets look at the simplest case:

public T Create<T>() where T new()
{
    return new T();
}

The where T new() is called a constraint, and tells the compiler that we are going to want to create a new instance of the generic type T.

In code we can node say:

int i = Create<int>();
XmlDocument doc = Create<XmlDocument>();

The compiler substitutes the passed in type in place of T and the method's code creates/constructs/returns the object.

Obviously this isn't much use by itself. Let's look at a more complex example. Imagine you have an XML document that at a given point contains tags of type <nut> or type <bolt>. We want to create a list of Nut and Bolt objects according to what it found, and have the list be null if none were found.

I will use XPathNavigator for this example. Let's assume the navigator is currently sitting on the node containing the <nut> and <bolt> tags...

I want my calling code to look like this:

List<Nut> nuts = Parse<Nut>( nav, "nut" );
List<Bolt> bolts = Parse<Bolt>( nav, "bolt" );

With this we're saying parse creating a list of Nut objects by looking in the given XPathNavigator (nav) looking for "nut" elements. Same for bolts.

The Parse method looks like this:

public List<T> Parse<T>( System.Xml.XPath.XPathNavigator nav, string name )
    where T : new()
{
    System.Xml.XPath.XPathNodeIterator iter;
    List<T> list = null;
    iter = nav.SelectChildren( name, nav.NamespaceURI );
    while( iter.MoveNext() )
    {
        if( list == null )
        {
            list = new List<T>();
        }
        list.Add( new T() );
    }
    return list;
}

This will work well, but there is a major limitation. I mentioned how the new() constraint was needed to allow us to create new instances of T. The problem is that new() only lets us use a parameterless constructor. Why do we care? We could just call an Init() method right away, right? While that is true there are challenges with this approach:

  • You wouldn't be able to derive your class from a base class that only had parameterized constructors. You may be able to work around this for code you own by also splitting base class initialization to an Init() method. However, this may not be an option for third party code or code in the .NET Framework itself.
  • C# provides the readonly that lets you define values that you define once but which cannot be modified from that point on. This is very useful for exposing fields directly for simple values where you don't want to clutter your object with property accessors. But the one shot you get to set the object is in the constructor. You couldn't set a readonly field from your Init() method.


Thankfully there is a simple workaround. Going back to our Nut and Bolt example, lets say the Nut and Bolt objects would like to get their node's XPathNavigator as a parameter. Here is the modifed code:

public List<T> Parse<T>( System.Xml.XPath.XPathNavigator nav, string name )
{
    System.Xml.XPath.XPathNodeIterator iter;
    List<T> list = null;
    iter = nav.SelectChildren( name, nav.NamespaceURI );
    while( iter.MoveNext() )
    {
        if( list == null )
        {
            list = new List<T>();
        }
        T inst = (T) Activator.CreateInstance( typeof(T), iter.Current );
        list.Add( new T() );
    }
    return list;
}

In this listing we have removed the constraint entirely, and changed over to calling Activator.CreateInstance() to construct our type. The second (and subsequent) parameters to Activator.CreateInstance() are parameters passed to the object's constructor.

We can now define out Nut class like this:

public class Nut
{
    public readonly single Gauge;
    public Nut( System.Xml.XPath.XPathNavigator nav )
    {
        string g = nav.GetAttribute( "gauge", string.Empty );
        Gauge = single.Parse( g );
    }
}

While this workaround is ok, there is one caveat, which is that the use of Activator.CreateInstance() relies on runtime parameter matching — the system has to hunt for a matching constructor.

This has two impacts.
  • Performance is going to be quite a bit worse than using the new keyword. That may ok for one-off or infrequent operations such as one-time parsing of XML documents, but not for operations you perform millions of times a second.
  • The compiler cannot verify the types you pass in. If Nut doesn't have a constructor that doesn't take an XPathNavigator the code will fail at runtime, not at compile time.
Still, given that the new() constraint is so restrictive, the code presented here isn't a bad compromise.

# posted 6/26/2005 09:14:00 AM | 1 comments

Thursday, June 23, 2005

End of an era 

One of my close friends at Dell resigned on Tuesday. John is one of the guys that has worked hard to make Dell's eCommerce backend the global leviathan it is today. He was also the only other Mac guy I know at Dell — the other three I knew both left years ago. Now who am I going to share Objective C war stories with?

Anyway, John: Best of luck at TheKnot.com. Do amazing things!

# posted 6/23/2005 12:17:00 PM | 0 comments

Sunday, June 05, 2005

Apple to run on PC hardware? 

So there's a lot of activity in the Apple rumor community about whether or not Apple is going to ditch IBM and move to Intel chips, and possibly even to running on PC hardware. As many folks have pointed out, NeXT's OpenStep OS ran just fine on PC hardware before NeXT was bought out by Apple and used OpenStep as the basic for MacOS X.

A lot of Apple fans are shocked/horrified by this, but it seems pretty logical to me. I think what the iPod has taught Steve Jobs is that by focusing on products that consumers can't live without, you can do some pretty interesting things. Steve isn't just building it for himself anymore.

How does this apply to the PC world? In a word: Tiger. Microsoft seems to have seriously lost the plot. It seemed like when they fired the starters pistol to begin work on Longhorn, almost every dev group at Microsoft went off "inventing" -- it was totally incoherent. When Bill Gates took over as "Chief Software Architect" I thought you would see a lot of the maverick projects get reigned in... and some were, but its still looks like a total cluster.

Now Apple comes out with Tiger - a very stable, very capable OS that does most of what most folks need. But there is a cap on how many folks can use it -- the Macintosh user base. While some folks will turn in their PCs for Mac Minis and G5 iMacs, most folks aren't going to throw away their existing investment. To me the logical answer: make Tiger (the next version... 10.5 or whatever they call it) run on PCs. Suddenly you've got hundreds of millions of potential new customers overnight.

While Linux may be great for hacker/enthusiast types, my Mom isn't going to run it. But MacOS X is easy -- even my 4 year old can use it. I'm not seeing a downside.

There are of course detractors. Comments like "historically, Apple makes most of its money on hardware", while true, don't take into account the potential gold rush of folks moving their PCs over to Apple software. Now that Apple is charging $129 for Tiger, I suspect they're doing just fine margin-wise, thank you very much. The iPod changes the equation too, providing a steady (and steadily growing) revenue stream independent of what their computer hardware and OS business is doing.

So, what about Apple hardware? At the end of the day, the innards of a Mac are pretty standard stuff. Most of the technology you find in a PowerMac G5 in terms of use of HyperTransport, liquid cooling etc. can already be found an AMD-based gaming system. They have innovations, such as the illuminated keyboard on the PowerBook, but those kind of things could easily be done on a PC too (nobody has done it because Apple has the patents sown up and probably isn't willing to license it). So Apple becomes a "boutique supplier" of PC hardware. It will probably be too expensive for most folks (compared feature for feature), but you're buying into the design ethic - the attention to the little things. Apple's hardware market share might not grow much but they would bring something special to the marketplace.

When I walk around the offices at Dell wearing my Apple Developer Connection t-shirt, I sometimes get funny looks. Apple is a competitor after all. But I'm not wearing it because I own a PowerMac G4 at home. I wear it because I love MacOS X. I need the G4 to be able to run it. I would like nothing more than to be able to install MacOS X on my Latitude C400 at work. Microsoft Word would run just fine, and I could use Keynote to do presentations.

I'll be at WWDC this coming week. If his Steveness does announce such a move Monday morning, you'll not be hearing any booing from me.

# posted 6/05/2005 07:02:00 AM | 2 comments

Wednesday, June 01, 2005

Oooh... competition! 

So it looks like the folks at Monkey Business Labs are working on a web comic dashboard widget...

Comic Tracker link

A part of me is a little upset at having competition, but the larger part of me is glad because they did such a great job with their Package Tracker widget.

Should be interesting to see what they come up with.

# posted 6/01/2005 07:49:00 AM | 2 comments


This page is powered by Blogger. Isn't yours?
Copyright 2004 Steve Saxon