Updated on
Guid.TryParse() is how we validate a GUID in C#. It takes a string, returns true or false, hands back the parsed Guid through an out parameter, and never throws an exception, even when the string is null.
Five other approaches exist, and each of them is a worse default. In this article, we go through all six, see what each one does with a null or malformed string, and measure what the difference costs.
What Does a Valid GUID Look Like?
A GUID is a 128-bit value, which C# represents with the System.Guid struct. A string can only hold its text form, so validating a GUID means asking whether a particular piece of text can become such a value.
Written out, those 128 bits are 32 hexadecimal digits. The familiar form adds four hyphens in an 8-4-4-4-12 grouping, which makes 36 characters in total: 81a130d2-502f-4cf1-a376-63edeb000e9f.
That is one of five layouts .NET recognizes. The same value is also legal with no hyphens at all, wrapped in braces, wrapped in parentheses, or written as a C-style list of hexadecimal numbers. Guid.Parse() and Guid.TryParse() accept all five and trim surrounding white space, while a hand-written regular expression usually accepts exactly one of them.
The specification behind the format is RFC 9562, which replaced RFC 4122 in 2024. A well-formed string can still hold a value that identifies nothing: 00000000-0000-0000-0000-000000000000 is a valid GUID string.
The bit layout, including the version and variant fields we return to at the end of this article, is defined in RFC 9562.
How Do We Check Whether a String Is a Valid GUID?
Guid.TryParse() is the answer for almost every case. Its bool return value is the validation result and its out parameter carries the parsed value, so one call does both the check and the conversion, and a bad string never throws.
The exception-based options do the same parsing work and then pay for failure. Guid.Parse(), Guid.ParseExact() and the Guid constructor throw a FormatException when the string is not a GUID, and all three throw an ArgumentNullException when the input is null, which a catch (FormatException) does not catch.
Reach for the Exact variants when the layout itself is part of the contract, for example when a provider promises braces and anything else is worth reporting as a bug. They still trim surrounding white space, so they are strict about the layout and nothing else.
A regular expression is the weakest option. It takes 2 to 3 times as long as TryParse() in our benchmark, it recognizes a single layout, and it hands back no Guid afterwards.
The same split between a throwing Parse() and a non-throwing TryParse() runs through the numeric types, DateTime and enums too, and we cover the Parse() and TryParse() pattern across .NET types in a separate article.
Regular Expressions
Utilizing regular expressions enables us to recognize GUIDs within strings. The pattern we write here matches the hyphenated 36-character layout and none of the other four, as the table at the end of this section shows. By crafting a regex pattern that aligns with the GUID format, we can check whether a whole string is a GUID.
Let’s write a regular expression that identifies a string as a GUID, in a new GuidHelper class:
using System.Text.RegularExpressions;
public partial class GuidHelper
{
[GeneratedRegex("^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$")]
private static partial Regex GuidValidatorRegex();
}
Here, we introduce a GuidHelper class to streamline the validation process for GUIDs. Within this class, we use the GeneratedRegex attribute. It takes a regex pattern, and the Roslyn source generator uses it to generate the source code of the regular expression at compile time instead of runtime. This approach promises faster regex execution, thereby enhancing the overall performance of the application. To accomplish this, we define a partial method named GuidValidatorRegex(), which returns a Regex type response and carries the GeneratedRegex attribute. We cover the attribute in more depth in our article on source-generated regular expressions.
When formulating the pattern, we specify that the input must consist of five parts separated by hyphens. The first part should be 8 characters long, the final part 12 characters long, and the remaining parts 4 characters each. Each part must comprise either a numerical value (0-9) or a character ranging from A to F, in upper or lower case. The total length, including hyphens, is 36 characters, although the $ anchor also matches before a final newline.
Let’s see how we can use this feature:
public static bool ValidateWithRegex(string? input)
{
return input is not null && GuidValidatorRegex().IsMatch(input);
}
Here, we establish a helper method ValidateWithRegex(), which accepts a string input parameter. Within this method, we utilize the previously defined GuidValidatorRegex() function and invoke the IsMatch() method to validate the input string. IsMatch() throws an ArgumentNullException for a null input, so the parameter is a nullable string? and we return false for null before we call it.
In Program.cs, let’s invoke this method with both GUID and non-GUID inputs, and print both results with a small Message() local function:
var guidString = Guid.NewGuid().ToString();
const string nonGuidString = "loremipsum-sid-dolar-amet-34648208e86d";
const string messageFormat = "{0} | guidString is {1}, nonGuidString is {2}";
Message("Regex", GuidHelper.ValidateWithRegex(guidString), GuidHelper.ValidateWithRegex(nonGuidString));
static void Message(string prefix, bool result1, bool result2)
{
var guidInfo1 = result1 ? "GUID" : "not a GUID";
var guidInfo2 = result2 ? "GUID" : "not a GUID";
Console.WriteLine(messageFormat, prefix, guidInfo1, guidInfo2);
}
Let’s now inspect the result:
Regex | guidString is GUID, nonGuidString is not a GUID
As expected, our guidString is a valid GUID, while our nonGuidString is not. You can generate valid test GUIDs, or decode one to see its version, with the GUID generator instead of calling Guid.NewGuid() to get a value to test against your validator.
Guid.Parse To Validate a GUID in C#
We can use the Guid.Parse() method in C# to convert either a string or a character span into a GUID value. It first trims any leading or trailing white space characters from the input before processing the remaining characters to generate a GUID. Then, it processes the remaining characters to generate a GUID. If the method encounters difficulties in parsing the string, it promptly throws a FormatException.
Let’s use this method to validate whether a string is a GUID or not:
public static bool ValidateWithGuidParse(string? input)
{
try
{
Guid.Parse(input!);
}
catch (Exception ex) when (ex is FormatException or ArgumentNullException)
{
return false;
}
return true;
}
Here, we introduce the ValidateWithGuidParse() method, which accepts a string input parameter. Within the method, we use the Guid.Parse() method in a try-catch block. If the input is valid, no exception is thrown. However, if an invalid input is provided, the method will raise a FormatException.
A null input raises an ArgumentNullException instead, and a plain catch (FormatException) lets that one through, so our catch clause has a when filter that names both exception types. The null-forgiving operator in Guid.Parse(input!) only silences the compiler’s nullable warning. At runtime, a null still reaches Guid.Parse(), and the filter catches the exception it throws. Our article on how exception handling works in C# covers when filters in its conditional exception handling section.
From this point onward, we will omit the steps for invoking and verifying the result, as they closely resemble those used in the regex approach.
Guid.ParseExact To Validate a GUID in C#
Another essential tool for validating whether a string is a GUID or not is the Guid.ParseExact() method in C#. Like the Guid.Parse() method, it lives on the System.Guid struct and serves to parse a string representation of a GUID into a Guid structure. The difference is that it takes a format parameter, allowing us to enforce a specific format for the input string, and provides more control over the parsing process compared to the Guid.Parse() method.
The format parameter dictates the formatting of the GUID string, determining factors such as the placement and type of separators (e.g., hyphens, braces). It supports various format specifiers:
| Format | Description |
|---|---|
| N | Represents 32 digits without hyphens (e.g., "6F9619FF8B86D011B42D00C04FC964FF"). |
| D | Represents 32 digits separated by hyphens in the standard 8-4-4-4-12 format (e.g., “6F9619FF-8B86-D011-B42D-00C04FC964FF”). |
| B | Represents 32 digits separated by hyphens and enclosed in braces (e.g., “{6F9619FF-8B86-D011-B42D-00C04FC964FF}”). |
| P | Represents 32 digits separated by hyphens and enclosed in parentheses (e.g., “(6F9619FF-8B86-D011-B42D-00C04FC964FF)”). |
| X | Represents four hexadecimal values enclosed in braces, where the fourth value is a subset of eight hexadecimal values that is also enclosed in braces (e.g., “{0x81a130d2,0x502f,0x4cf1,{0xa3,0x76,0x63,0xed,0xeb,0x00,0x0e,0x9f}}”). |
With a format specifier, the method accepts that one layout and rejects the other four, even when they hold a valid GUID.
Let’s utilize this method to validate a GUID:
public static bool ValidateWithGuidParseExact(string? input, string format)
{
try
{
Guid.ParseExact(input!, format);
}
catch (Exception ex) when (ex is FormatException or ArgumentNullException)
{
return false;
}
return true;
}
Here, we define the ValidateWithGuidParseExact() method. Inside the method, we invoke the Guid.ParseExact() method with the incoming input and format parameters, and we pass "D" as the format when we call it. Like the ValidateWithGuidParse() method, it returns false when Guid.ParseExact() raises a FormatException for an invalid input or an ArgumentNullException for a null one.
Guid.TryParse To Validate a GUID in C#
In comparison to the Guid.Parse() and Guid.ParseExact() methods, Guid.TryParse() offers a safer approach to validate whether a string is a GUID. Rather than throwing an exception, Guid.TryParse() returns a boolean value indicating the success or failure of the parsing operation.
The Guid.TryParse reference on Microsoft Learn says the method “returns false if input is null or not in a recognized format, and doesn’t throw an exception.”
Let’s illustrate its usage:
public static bool ValidateWithGuidTryParse(string? input)
{
return Guid.TryParse(input, out Guid _);
}
Here, we create the ValidateWithGuidTryParse() method. It simply invokes and returns the result of Guid.TryParse() method. Since Guid.TryParse() accepts a null string, the parameter is a nullable string? and needs no extra check.
Guid.TryParseExact To Validate a GUID in C#
If we want to avoid exceptions and our string has to match one predefined GUID format exactly, we can use the Guid.TryParseExact() method. Likewise the Guid.ParseExact() method, this approach necessitates the string to precisely match the format specified by the format parameter, following the removal of any leading or trailing white-space characters. If the input is null or doesn’t conform to the specified format, the method returns false without throwing an exception:
public static bool ValidateWithGuidTryParseExact(string? input, string format)
{
return Guid.TryParseExact(input, format, out Guid _);
}
Here, we introduce the ValidateWithGuidTryParseExact() method, which takes a parameter, input to validate based on the format parameter. In the method, we use the Guid.TryParseExact() method and return the result of it.
Guid Constructor
In programming, there are often multiple approaches available for finding a solution. Given that our objective is to verify whether a string represents a GUID, we might indirectly repurpose the new Guid() constructor as a validation method. We know that passing a value to the new Guid() constructor results in a successful instance if it’s a valid GUID. However, if the input format is not suitable for a GUID, we will encounter a FormatException error.
Let’s encapsulate what we want to do into a method:
public static bool ValidateWithNewGuid(string? input)
{
try
{
var _ = new Guid(input!);
}
catch (Exception ex) when (ex is FormatException or ArgumentNullException)
{
return false;
}
return true;
}
Here, we establish the ValidateWithNewGuid() method, which accepts an input parameter. Within the method, we attempt to initialize an instance of Guid using new Guid(input!) within a try-catch block. If the filter catches a FormatException for a malformed string or an ArgumentNullException for null, the input string is not a valid GUID string.
Here are the six approaches side by side:
| Method | Invalid string | Null input | Accepts |
|---|---|---|---|
Guid.TryParse(input, out var id) | returns false | returns false | all five formats, trims white space |
Guid.TryParseExact(input, "D", out var id) | returns false | returns false | only the named format, still trims white space |
Guid.Parse(input) | throws FormatException | throws ArgumentNullException | all five formats, trims white space |
Guid.ParseExact(input, "D") | throws FormatException | throws ArgumentNullException | only the named format, still trims white space |
new Guid(input) | throws FormatException | throws ArgumentNullException | all five formats, trims white space |
GuidValidatorRegex().IsMatch(input) | returns false | throws ArgumentNullException | the D format only, no white space except a final newline |
The table lists the raw .NET calls. Our helpers catch the exceptions of the three throwing methods and check for null before the regular expression runs, so all six of them take a nullable string? and return false for null.
Which GUID Validation Method Is Fastest?
Guid.TryParse() wins, even though all five Guid methods run the same parser underneath. What differs is what happens when a string fails.
Rejecting an invalid string costs TryParse() a false. It costs Guid.Parse() a thrown and caught FormatException, which is hundreds of times more expensive and allocates an exception object every time.
The benchmark below feeds every method an equal mix of valid and invalid strings, so nearly all of the time in the exception-based rows is the cost of failing, not the cost of parsing. The regular expression lands between the two groups, slower than both Try methods and at least 36 times faster than the three methods that throw.
Point the same methods at input that is always valid and the gap almost disappears, because there is nothing left to throw. Exceptions are affordable when they are rare, and a validation routine is exactly the place where failure is expected.
So far, we’ve explored various approaches to validate a string as a GUID. Now, we’re ready to compare the performance of these approaches in terms of speed, memory allocation, and efficiency. To streamline this comparison, we’ll leverage the BenchmarkDotNet library for benchmarking. Each benchmark calls one of our six helpers 1,000, 10,000 or 100,000 times, alternating between a valid GUID string and our nonGuidString:
BenchmarkDotNet v0.15.8, Windows 10 (10.0.19045.6466/22H2/2022Update) AMD Ryzen 5 3600 3.60GHz, 1 CPU, 12 logical and 6 physical cores .NET SDK 10.0.302 [Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 DefaultJob : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 | Method | Iterations | Mean | Median | Gen0 | Allocated | |--------------------------------- |----------- |--------------:|--------------:|----------:|-----------:| | UseValidateWithGuidTryParse | 1000 | 11.50 us | 11.44 us | - | - | | UseValidateWithGuidTryParseExact | 1000 | 21.72 us | 21.31 us | - | - | | UseValidateWithRegex | 1000 | 30.85 us | 30.64 us | - | - | | UseValidateWithGuidParse | 1000 | 1,123.95 us | 1,116.42 us | 11.7188 | 108000 B | | UseValidateWithGuidParseExact | 1000 | 1,311.62 us | 1,312.81 us | 17.5781 | 160000 B | | UseValidateWithNewGuid | 1000 | 1,416.79 us | 1,406.70 us | 17.5781 | 160000 B | | | | | | | | | UseValidateWithGuidTryParse | 10000 | 117.68 us | 117.99 us | - | - | | UseValidateWithGuidTryParseExact | 10000 | 200.91 us | 201.91 us | - | - | | UseValidateWithRegex | 10000 | 341.81 us | 341.48 us | - | - | | UseValidateWithGuidParse | 10000 | 14,070.95 us | 13,981.14 us | 187.5000 | 1600000 B | | UseValidateWithNewGuid | 10000 | 14,129.81 us | 14,052.73 us | 187.5000 | 1600000 B | | UseValidateWithGuidParseExact | 10000 | 14,804.48 us | 14,282.67 us | 187.5000 | 1600000 B | | | | | | | | | UseValidateWithGuidTryParse | 100000 | 1,138.80 us | 1,134.54 us | - | - | | UseValidateWithGuidTryParseExact | 100000 | 1,981.79 us | 1,978.12 us | - | - | | UseValidateWithRegex | 100000 | 2,537.68 us | 2,535.24 us | - | - | | UseValidateWithGuidParseExact | 100000 | 134,181.12 us | 134,371.20 us | 1750.0000 | 16000000 B | | UseValidateWithNewGuid | 100000 | 137,391.37 us | 136,389.58 us | 1750.0000 | 16000000 B | | UseValidateWithGuidParse | 100000 | 137,520.85 us | 137,990.35 us | 1750.0000 | 16000000 B |
At 100,000 iterations, UseValidateWithGuidTryParse takes 1,138.80 us and UseValidateWithGuidParse takes 137,520.85 us, about 120 times as long. The three exception-based rows allocate 16,000,000 B there, which is 320 bytes for each of the 50,000 strings that fail, while the other three rows allocate nothing.
Is Guid.Empty a Valid GUID?
Yes, and that catches people out. Guid.TryParse("00000000-0000-0000-0000-000000000000", out var id) returns true, because the string is a well-formed GUID. It is also the default value of the Guid struct, so a field nobody assigned and a deliberate all-zero identifier look exactly alike.
Keep the two questions apart. Parsing answers whether the text is a GUID at all, and a comparison against Guid.Empty answers whether the value means anything to us. An API that accepts an identifier from a caller needs both checks, in that order.
.NET 9 added two properties that go further. Version returns the version field and Variant returns the variant field, so a value from Guid.NewGuid() reports version 4 and one from Guid.CreateVersion7() reports version 7, while Guid.Empty reports 0 for both.
Those are worth checking when the version carries meaning, as it does for the time-ordered version 7 identifiers, which RFC 9562 designed to sort well in database indexes.
Both checks fit in a single method, which we can add to our GuidHelper class:
public static bool IsUsableGuid(string? input) =>
Guid.TryParse(input, out var id) && id != Guid.Empty;
It returns false for null, for a malformed string and for the all-zero GUID, and true for any other GUID.
Conclusion
In this article, we explored several approaches to validate whether a string represents a GUID in C#. Whether through regular expressions, or built-in parsing methods like Guid.TryParse(), or the Guid constructor, we have a range of tools at our disposal to effectively validate GUIDs. Each approach offers its advantages in terms of readability, performance, and precision, enabling us to choose the most suitable method based on our specific requirements.
Tested with .NET 10.0.10 (SDK 10.0.302), BenchmarkDotNet 0.15.8 and MSTest 4.5.1.
