Design Guideline Update: Uri vs. string

ChrisAn blogged about this a few weeks ago and I am happy to announce that Krzysztof Cwalina has a guideline ready for you to comment on! 

System.URI Usage

Use System.Uri to represent URI/URL data. This applies to parameter types, property types, and return value types.

public class Navigator {

public Navigator(Uri initialLocation);

public Uri CurrentLocation { get; }

public Uri NavigateTo(Uri location);

}

Note: System.Uri is a much safer and richer way of representing URIs. Extensive manipulation of URI related data using plain strings has been shown to cause many security and correctness problems. An excellent whitepaper describing the issues in more details is on it's way

Consider providing string-based overloads for most commonly used members with System.Uri parameters.

In cases where the usage pattern of taking a string from a user will be common enough, you should consider adding a convenience overload accepting a string. The string-based overload should be implemented in terms of the Uri-based overload.

public class Navigator {

    public void NavigateTo(Uri location);
    public void NavigateTo(string location) {

      NavigateTo (new Uri (location));

    }
}

Do not automatically overload all Uri-based members with a version that accepts a string.

Generally, Uri-based APIs are preferred. String-based overloads are meant to be helpers for the most common scenarios. Therefore, you should not automatically provide string-based overloads for all variants of the Uri-based members. Be selective and provide such helpers just for the most commonly used variants.

public class Navigator {

    public void NavigateTo(Uri location);

    public void NavigateTo(string location);
    public void NavigateTo(Uri location, NavigationMode mode);
}

1.1.1 URI/URL Data Implementation Rules

Call the Uri-based overloads if available.

Do not store URI/URL data in a string.

When you accept a URI/URL input as a string, you should convert the string to a System.Uri and store the instance of System.Uri.

 

In addition here are the FxCop rules we are thinking about to help enforce this guideline:

If a parameter name contains “uri” or “url” or “urn” and it’s typed as string, the parameter type should be changed to System.Uri, unless there is an overload method where the same parameter is types as Uri.

If a function name contains “uri” or “url” or “urn” and the return type is string, the return type should be changed to System.Uri.

If a property name contains “uri” or “url” or “urn” and it’s typed as string, it should be changed to System.Uri.

If a parameter is typed as System.Uri and the member has an overload where a parameter at the same position is typed as System.String, the member should not do anything with the string except to pass it to Uri.TryParse or Uri.ctor, and call the overload that takes Uri.

If there are two overloads one taking System.Uri and one taking System.String, library code should never call the string-based overload.

Published 12 January 04 04:14 by BradA

Comments

# Andy said on January 12, 2004 4:24 PM:
Any chance the "excellent" doc can be put somewhere public?
# Brad Abrans said on January 12, 2004 5:16 PM:
Sorry about the bad whitepaper link... I am working on getting it posted outside the farm now..
# Ken Brubaker said on January 16, 2004 1:08 PM:
Can you make a more generic rule for when to use composite types and simple types. This rule could apply for System.Guid and string for example.
# Sam Gentile's Blog said on January 18, 2004 11:32 AM:
# Peter said on January 19, 2004 12:21 PM:
I'm not so happy with the uri class because:

I’ve noticed some strange behaviors with the uri class,
First the constructor has strange behavior (first code snippet)
Second the EQUALS method and the ==/!= operators behave differently ?


string partialUrl="../../Styles/Common/Styles.css";
string anotherUrl="../Styles/Common/Styles.css";
Uri remoteUri = new Uri("http://pdemeyer01/nbbcms/test.html");
Uri anUri = new Uri(remoteUri,partialUrl);
Console.WriteLine(anUri.AbsoluteUri); //prints http://pdemeyer01/../Styles/Common/Styles.css INCORRECT
anUri = new Uri(remoteUri,anotherUrl );
Console.WriteLine(anUri.AbsoluteUri); //prints http://pdemeyer01/Styles/Common/Styles.css CORRECT


Queue aQueue = new Queue();
Uri NL =new Uri("http://localhost/NBBCMS/WebForm1.aspx?Language=NL");
Uri UK=new Uri("http://localhost/NBBCMS/WebForm1.aspx?Language=UK");

aQueue.Enqueue(NL);
if (!aQueue.Contains(UK))
Console.WriteLine("UK Not in the queue");
else
Console.WriteLine("UK Already in Queue"); //prints UK Already in Queue INCORRECT
if (UK.Equals(NL))
Console.WriteLine("UK Equals NL"); //prints UK Equals NL INCORRECT
if (UK!=NL)
Console.WriteLine("UK != NL"); //prints UK != NL CORRECT
# Brad Abrams said on January 19, 2004 8:55 PM:
From the dev team: You will be glad to know that we have fixed this in the current builds...

Now it prints:

http://pdemeyer01/Styles/Common/Styles.css">http://pdemeyer01/Styles/Common/Styles.css
http://pdemeyer01/Styles/Common/Styles.css">http://pdemeyer01/Styles/Common/Styles.css

UK Not in the queue

UK != NL

Note Uri.Equals() will still ignore userinfo and fragment parts.

For now this is to maintain compatibility with v1.0 when a uri is used to resolve a resource (not to identify it)

Ignoring a query part was just a plain bug.
# James Geurts' Blog said on March 1, 2004 12:34 PM:
# Doug McClean said on May 17, 2004 7:57 AM:
This is off topic, but does anyone have any idea why System.Uri inherits System.MarshalByRefObject? Seems like a very odd choice for such an obviously serializable type, that is marked as serializable, and that appears to be immutable (doesn't have any set accessors or methods that seem like they should have side effects). Can anyone weigh in on this? Under what circumstances would a serializable type be marshaled by reference, anyway?
# Brad Abrams said on July 27, 2004 11:14 PM:
New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker