Guid.ToString vs Sitecore’s .ToGuidString and ID.ToString

Thursday, March 12, 2015 @ 01:00

Or: Why you should never assume an overridable method will do what you want it to do

By Corey Adler

Let’s begin this post a little differently than my other posts. It’s time for a one-question pop quiz:

Question: Without looking it up (or looking below), will ‘toStringCheck’ evaluate to true or false for the following?

Test

Answer: It will return false. More on that in a little bit.

A situation, similar to what you see above, happened to me on a project I was working on recently. There was a check to see if a GUID value matched with the Sitecore ID of a specific page, and, if it did, display something extra in a breadcrumb control on that page. There was a Constants file that contained the ID as a const string value, and the controller for that particular rendering was using Guid.ToString().Equals() on that value to make the check. Since it was returning false, I decided to debug it, and see what the GUID was being set to.

(Note: Under normal circumstances the best course of action for doing comparisons with a Guid would be to use Guid.Parse/Guid.TryParse [documentation at http://bit.ly/18EOW4j] on the string in question, instead of grabbing the string from the Guid. I acknowledge this. This point of this post, however, is two-fold: First, what to do if the situation happens to dictate that you can’t use Guid.Parse (like for performance reasons), and second, for the point made at the end of this post: To never assume that an overridable method is doing what you want it to.)

Guid Watch

Aha! It appears that the GUID contains some lower-case letters, and here I am trying to make an Equals comparison with a string whose letters are all upper-case! So all that I need to do is make sure that my comparison ignores the case of the letters, and I should be good to go, right? Just something like this:

Test with Ignore Case

And…

False Test

Well, dang, so much for that idea. But why is it doing this? You can see that the value in the watch and the value of the string are equal (when ignoring case), so why does toStringCheck keep coming back as false? I banged my head on my desk for a few minutes when I saw this. Then I noticed that Sitecore has an extension method to the Guid class called ToGuidString, found in the Sitecore.Mvc.Extensions assembly. I decided to give that a try and see what would happen.

ToGuidString Test

Hey, it worked! So, why did that work, but not Guid.ToString? After thinking about it some more, I realized that something might be happening in the background code. I decided to take a deeper look at Guid.ToString. What I found was very interesting:

Guid ToString

Ok, so that’s looking a little different than expected. Wondering what the “D” meant, I immediately went to MSDN’s Guid.ToString reference (found at https://msdn.microsoft.com/en-us/library/97af8hh4%28v=vs.110%29.aspx) and found this:

Guid Formats

Well, how about that? It turns out that Guid.ToString defaults to showing the hyphens, but not the curly braces! It defaults to this value because items in the system registry have the keys without the curly braces, so Microsoft set the Guid class’ default to not show the braces unless specified. No wonder the comparison failed: It turned out that I was operating on a false assumption the whole time debugging it. So what about the Watch window? Why did it show the curly braces if they weren’t actually there? It turns out that the Watch window, when showing the value of an object, will show the value to that object’s ToString method in between curly braces. It doesn’t matter if the object has them in front or not—they are shown in the Watch window anyway. If the ToString method does return a string with curly braces, those braces get subsumed in the Watch window—it won’t show double braces.

An example: Given an object like this:

Random Object

And some code that instantiates the object like this:

Random Object Start

The Watch window will populate with:

Random Object Watch

That certainly is good to know. Now back to the topic at hand: What about Sitecore’s ToGuidString method?

Sitecore ToGuidString

It uses Guid.ToString(“B”)—meaning that the curly braces will make an appearance. How about that, huh?

After fixing the bug, I went and talked this over with one of my more experienced Sitecore developer colleagues—Geoff Harden. Geoff listened, and at the end informed of something else that I was unaware of: That Sitecore’s ID class’ ToString method returns the GUID part of the object—with curly braces! I immediately went back to my machine, fired up the dotPeek functionality in my copy of ReSharper and…

ID ToString

So it does. Yet another instance of an overridable method that doesn’t do what you’d assume it would do! You can see from the examples shown above the importance of double checking your method calls. Even though it appears obvious to you what’s happening in a particular method or class, you should still always be sure to crack it open and take a look first. This is especially true with virtual methods, which can be overridden by anything that extends that object type. Interface methods can also pose a similar problem, although this concern can be mollified if one correctly implements good design practices—especially SOLID Principles like the Open/Closed and Liskov Substitution Principles. Don’t get stuck wasting your time on something as easy to find as this!

One of the easiest ways to make sure that you never waste your time on faulty assumptions like this is to use decompilers like dotPeek, which can be downloaded as a free, standalone application (https://www.jetbrains.com/decompiler/) if you don’t want to spend the money to buy a full ReSharper license. Using decompilers will allow you to see what’s really going on behind the “magic”, and is especially useful for decompiling base .NET code—like the Guid class above. Spending extra time in the here and now to make sure that your code is valid and correct will pay dividends later in having more maintainable code and the reduction of time that QA has to spend on potential bugs arising even from something as simple as this. It’s a no-brainer.

I sincerely hope that I’ve driven my point home. Until next time, I wish you good coding.