Provides a strongly-typed PhoneNumber value object that represents phone numbers in the E.164 format. Includes parsing (with optional region for local numbers), validation, comparison, and formatting helpers for international and national representations.
$ dotnet add package PosInformatique.Foundations.PhoneNumbersThis package provides a strongly-typed PhoneNumber value object that represents a phone number in E.164 format.
It centralizes validation, parsing, comparison and formatting logic for phone numbers, and ensures that only valid international phone numbers can be instantiated.
It is recommended to use E.164 format everywhere in your code. When parsing a local phone number (not already in E.164), you must explicitly specify the region to avoid ambiguity.
This library uses the libphonenumber-csharp library under the hood for parsing and formatting.
You can install the package from NuGet:
dotnet add package PosInformatique.Foundations.PhoneNumbers
IEquatable<PhoneNumber> for value-based equalityIFormattable and IParsable<PhoneNumber> for easy integration with .NET APIs (parsing/formatting)string and PhoneNumberIParsable / IFormattable in generic components (bindings, configuration, serialization, etc.).using PosInformatique.Foundations.PhoneNumbers;
// Implicit conversion from string (expects a valid E.164 number)
PhoneNumber phone = "+33123456789";
// To string (E.164 by default)
Console.WriteLine(phone); // "+33123456789"
// Validation
var valid = PhoneNumber.IsValid("+33123456789"); // true
var invalid = PhoneNumber.IsValid("1234"); // false
var phone = PhoneNumber.Parse("+14155552671");
Console.WriteLine(phone); // "+14155552671"
When you parse a local phone number (not starting with "+"), you must specify the region (ISO 3166-1 alpha-2, e.g. "FR", "US", ...).
// Local French number, region must be specified
var frenchPhone = PhoneNumber.Parse("01 23 45 67 89", defaultRegion: "FR");
Console.WriteLine(frenchPhone); // E.164: "+33123456789"
// TryParse with region
if (PhoneNumber.TryParse("06 12 34 56 78", out var mobile, defaultRegion: "FR"))
{
Console.WriteLine(mobile); // "+33612345678"
}
It is recommended to always work with E.164 numbers in your code (storage, comparison, APIs), and only handle local formats at the boundaries (UI, input parsing) by specifying the region explicitly.
var phone = PhoneNumber.Parse("+14155552671");
// Default ToString() = E.164
Console.WriteLine(phone.ToString()); // "+14155552671"
// International format
Console.WriteLine(phone.ToInternationalString()); // "+1 415-555-2671" (example output)
// National format
Console.WriteLine(phone.ToNationalString()); // "(415) 555-2671" (example output)
// string -> PhoneNumber (implicit)
PhoneNumber phone = "+447911123456";
// PhoneNumber -> string (implicit, E.164)
string phoneString = phone;
Console.WriteLine(phoneString); // "+447911123456"
IParsable<PhoneNumber> genericallyBecause PhoneNumber implements IParsable<PhoneNumber>, it can be used in generic parsing scenarios.
// Generic parsing using IParsable<T>
static T ParseValue<T>(string value)
where T : IParsable<T>
{
var result = T.Parse(value, provider: null);
return result;
}
var phone = ParseValue<PhoneNumber>("+33123456789");
Console.WriteLine(phone); // "+33123456789"
IFormattable genericallyPhoneNumber also implements IFormattable, so it can be formatted via APIs that rely on IFormattable.
static string FormatValue(IFormattable value)
{
// format and provider are ignored in this implementation,
// but this allows generic handling of different value objects.
return value.ToString(format: null, formatProvider: null);
}
var phone = PhoneNumber.Parse("+33123456789");
var formatted = FormatValue(phone);
Console.WriteLine(formatted); // "+33123456789"
defaultRegion when parsing:
PhoneNumber.Parse(localNumber, defaultRegion: "FR")PhoneNumber.TryParse(localNumber, out var number, defaultRegion: "FR")PhoneNumber value object everywhere to centralize validation and formatting.