Why System.Uri sucks, Part 1

I recently reviewed Framework Design Guidelines, the new book by Abrams and Cwalina about designing frameworks for .NET. In the review, I mentioned that I disagreed with their advice of using System.Uri (v.1.1) to represent some URIs. Here’s why.

 

The name System.Uri implies, to me, a URI, any URI. If your URI scheme follows the “rules” of the URI RFC (when .NET 1.1 came out, that would be RFC 2396), the System.Uri should be able to parse it, and you should be able to use the properties exposed by the type for information inside the URI: the host, the scheme, the user info, etc.

 

For the “big” URI schemes – http, ftp, file, mailto, news, and, of course, gopher – this is not a problem. They are parsed according to the rules and everything works as expected. Even if you decided to use your own, custom hierarchical URI scheme (http is an example of a hierarchical URI scheme), then System.Uri will parse it, as the code below demonstrates.

 

However, try giving System.Uri a mailto-style URI (i.e. something:user@example.com), and it’ll shit the bed. Then it’ll deny anything is wrong. You may be struggling to come up with an example, but, trust me, there are many; and they will quickly gain importance in our daily programmer lives: SIP, XMPP (kinda), and common presence and IM all represent users with mailto-style URIs. The only property that doesn’t return null with those URIs is Uri.Scheme. When I first found this out, I was shocked; I thought I was doing something wrong. One of the things that Brad and Krzysztof advise is not to surprise the user of your API, or go against their expectations.

 

Do you think System.Uri succeeds here?

 

using System;

 

class Program

{

   static void Main(string[] args)

   {

      Uri[] u = new Uri[7];

      u[0] = new Uri(“http://www.jasonkemp.ca/Rss.aspx”);

      u[1] = new Uri(“feed://www.jasonkemp.ca/Rss.aspx”);

      u[2] = new Uri(“purplemonkeydishwasher://www.jasonkemp.ca/Rss.aspx”);

 

      u[3] = new Uri(“mailto:jasonkemp@example.com”);

      u[4] = new Uri(“msn:jasonkemp@example.com”);

      u[5] = new Uri(“sip:jasonkemp@example.com”);

      u[6] = new Uri(“pres:jasonkemp@example.com”);

 

      for (int i = 0; i < u.Length; i++)

      {

         PrintHost(u[i]);

      }

 

      for (int i = 3; i < u.Length; i++)

      {

         PrintUserInfo(u[i]);

      }

   }

 

   private static void PrintUserInfo(Uri uri)

   {

      Console.WriteLine(“uri.UserInfo for {0} = {1}”,

uri, uri.UserInfo);

   }

 

   private static void PrintHost(Uri uri)

   {

      Console.WriteLine(“uri.Host for {0} = {1}”, uri, uri.Host);

   }

}

 

In part 2, I’ll discuss why extending Uri to make up for the above deficiencies will cause you to shit the bed.