Why System.Uri sucks, part 2

Look up irony in a dictionary; do you know what you’ll find?

The definition of the word irony.

That may not help you, how about examples of irony? Alanis Morrisette’s song Ironic – none of the situations in the song are ironic, yet the name of the song is … Ironic; another example of irony is the recommendation, in a book about public APIs, of a class that has a pretty bad public API: System.Uri.

I wrote last time about System.Uri’s inability to parse mailto-like URI schemes. This time I’m going to talk about what you have to go through to if you want to remedy Uri’s problem: I’ll discuss the API exposed to inheritors.

Go to the documentation on MSDN about System.Uri; browse the members of Uri. It’ll show you one protected method in the list: the static EscapeString(). However, inherit from System.Uri, then type “override” followed by a space in Visual Studio 2003. You’ll see a number of methods that you can override: Canonicalize(), CheckSecurity(), Escape(), Parse(), and Unescape(). I believe that the reason they are not documented in MSDN is because they never should have shipped. Too bad Abrams and Cwalina didn’t point this mistake out when they advised using this class, like they did throughout the book with other classes.

Documentation probably wouldn’t make much difference because these methods aren’t very useful. They take no arguments, and return nothing (with the exception of Unescape() which takes a string and returns a string). There is no protected property exposing the string given to the constructor, so overriding the methods won’t help you. However, there’s nothing to stop a malicious, or incompetent, coder from overriding them and passing an instance of their class to your API. Will it compromise your system? I’m no security expert, but I don’t think so: they won’t be able to steal passwords with it. But they could take the system down: override Unescape() to return null and you’ll get a NullReferenceException.

Some further suckiness for this API is that they violate one of the rules that Abrams and Cwalina recommend in the Framework Design Guidelines book: don’t call virtual methods from constructors. Run the code below and you’ll see the order of execution.

using System;

public class MyUri : Uri

{

public MyUri(string uriString) : base(uriString)

{

Console.WriteLine(“In ctor”);

string escaped = EscapeString(uriString);

Console.WriteLine(“escaped = {0}”, escaped);

}

protected override void Canonicalize()

{

Console.WriteLine(“In Canonicalize()”);

}

protected override void CheckSecurity()

{

Console.WriteLine(“In CheckSecurity()”);

}

protected override void Escape()

{

Console.WriteLine(“In Escape()”);

}

protected override void Parse()

{

Console.WriteLine(“In Parse()”);

}

protected override string Unescape(string path)

{

Console.WriteLine(“In Unescape(path = {0})”, path);

return path;

}

}

class Program

{

static void Main()

{

new MyUri(pres:jason@example.com;param= pvalue”);

Console.ReadLine();

}

}

Yields the following output:

In Parse()

In Canonicalize()

In Escape()

In Unescape(path = ::-1pres:jason@example.com;param= pvalue)

In ctor

escaped = pres:jason@example.com;param=%20pvalue

Hmph. Not so hot.

They changed a lot in .NET 2.0, of course, so you can now override how to parse URIs that System.Uri doesn’t know about without extending System.Uri. Oh, and they deprecated the above methods, so you’ll get compiler warnings. I’ll talk more about to override UriParser next time. (I won’t follow the same title scheme next time, just to keep it interesting. So pay attention! J)