BMTLab.ImeiType is a .NET library
that provides a strongly-typed IMEI (International Mobile Equipment Identity) readonly struct.
It offers a simple API for validation, parsing, and generating IMEI numbers.
This package helps improve type safety, reduce errors, and handle IMEI values consistently in your applications.
Supported Platforms
-.NET 9.0,8.0,7.0,6.0, and
-.NET Standard 2.1
Install via NuGet:
dotnet add package BMTLab.ImeiTypeOr edit your .csproj:
<PackageReference Include="BMTLab.ImeiType" Version="1.2.0" />public readonly struct Imei
: IEquatable<Imei>,
IEqualityOperators<Imei, Imei, bool>, // .NET 7.0 or greater
ISpanParsable<Imei>, // .NET 7.0 or greater
IUtf8SpanParsable<Imei> // .NET 8.0 or greater- Strongly-Typed Imei Struct
Stores the IMEI number (long) internally.
Providing high-performance methods and approaches with minimized memory allocation,
as well as a set of specific tweaks and interface implementations for each supported platform.
- Multiple Parsing And Creation Options
Imei.Parse(...) and Imei.TryParse(...) from long, string, ReadOnlySpan<char>, or ReadOnlySpan<byte> (UTF-8)
with overload options.
- Random IMEI Generation
Imei.NewRandomImei(); // Creates a secure random IMEI using secure RandomNumberGenerator.
Imei.NewRandomImei(int seed); // Useful for testing or reproducible results.- Detailed IMEI Parts & Constants
Imei.Tac; // Type Allocation Code ('35', e.g.)
Imei.Fac; // Final Assembly Code ('630348', e.g.)
Imei.Snr; // Serial Number ('991680', e.g.)public const int Length = 15;
// Minimum and maximum allowed value of IMEI number
public const long MinValue = 00_000000_000001_9;
public const long MaxValue = 99_999999_999999_4;-
Conversions
β Implicit conversion to
string,ReadOnlySpan<char>, andReadOnlySpan<byte>;β Explicit conversion from
long,string, or spans;β Helper methods:
ToInt64(),ToReadOnlySpan()andToUtf8()and vice versaToImei(). -
Validation
Checks values of different types to see if the value is a valid IMEI (via Luhn checksum).
using BMTLab.ImeiType;
// This line shall not compile
Imei illegal = new Imei();
// Create an IMEI from a long (int64)
Imei imei1 = new Imei(356303489916807);
// From string or char arrays (or spans)
Imei imei2 = new Imei("356303489916807");
Imei imei3 = new Imei("356303489916807".AsSpan());
Imei imei4 = new Imei(['3', '5', '6', '3', '0', '3', '4', '8', '9', '9', '1', '6', '8', '0', '7']);
// From UTF-8 text
Imei imei5 = new Imei("356303489916807"u8);
Imei imei6 = new Imei([0x33, 0x35, 0x36, 0x33, 0x30, 0x33, 0x34, 0x38, 0x39, 0x39, 0x31, 0x36, 0x38, 0x30, 0x37]);Important
Because Imei is a readonly struct, a parameterless constructor cannot be fully removed.
We therefore mark the empty constructor with [Obsolete(error: true)] to prevent its usage.
Alternatively, you can cast values into an Imei:
using BMTLab.ImeiType;
Imei imei1 = (Imei) 356303489916807;
Imei imei2 = (Imei) "356303489916807";
Imei imei3 = (Imei) "356303489916807".AsSpan();
Imei imei4 = (Imei) new char[] {'3', '5', '6', '3', '0', '3', '4', '8', '9', '9', '1', '6', '8', '0', '7'};
Imei imei5 = (Imei) "356303489916807"u8;
Imei imei6 = (Imei) stackalloc byte[] { 0x33, 0x35, 0x36, 0x33, 0x30, 0x33, 0x34, 0x38, 0x39, 0x39, 0x31, 0x36, 0x38, 0x30, 0x37 };Or parse it, for example:
using BMTLab.ImeiType;
bool canBeParsedAndValid = Imei.TryParse(356303489916807, out Imei parsedImei);
// ...and so onBy default,
every new Imei(...) is validated to ensure the 15-digit number is correct,
according to the IMEI specification (using a Luhn checksum).
To avoid getting a FormatException during creation, you can use one of these overloads to check validity
(return boolean):
IsValid(long)IsValid(ReadOnlySpan<byte>)IsValid(ReadOnlySpan<char>)IsValid(string?)IsValid(Imei)- in caseImeiwas created viadefault(Imei), which unfortunately we cannot restrict.
Tip
If you are sure that the passed value will never be wrong, you can disable the check on creation and improve performance a bit. To do this, set this static property:
/* (`true` by default) can be set to `false` to skip validation on creation (use carefully) */
Imei.ShouldValidateWhileInitialization = false; Important
but use this at your own risk as it will affect the behavior globally.
Where a primitive type is expected, the IMEI type itself can easily be used, typically:
using BMTLab.ImeiType;
var imei = (Imei) 356303489916807;
string imeiAsString = imei;
ReadOnlySpan<char> imeiAsCharSpan = imei;
ReadOnlySpan<byte> imeiAsUtf8Text = imei;
/* Conversion to long is explicit, it is necessary to avoid ambiguous type reference when printing e.g. */
long imeiAsLong = (long) imei; Console.WriteLine($"IMEI value: {imei}"); //> "IMEI value: 015434904561440", e.g.using BMTLab.ImeiType;
var imeiA = (Imei) "356303489916807";
var imeiB = (Imei) "356303489916807";
Console.WriteLine(imeiA == imeiB); //> true
Console.WriteLine(imeiA.Equals(imeiB)); //> trueusing BMTLab.ImeiType;
var newImeiA = Imei.NewRandomImei(seed: 42); // By using System.Random. Useful for testing or reproducible results
var newImeiB = Imei.NewRandomImei(); // By using System.Security.Cryptography
WriteLine($"Random IMEI (with seed): {newImeiA}");
WriteLine($"Random IMEI (secure, but slower a bit): {newImeiB}");BMTLab.ImeiType.SystemTextJson is an optional add-on package that provides JSON serialization and deserialization
of Imei values using the built-in System.Text.Json APIs.
It includes a specialized converter: JsonImeiConverter,
which can read IMEI numbers from JSON as either numeric or string values, and write them back in a controlled manner.
The converter also supports IMEIs as list items, keys or dictionary values.
Supported Platforms
.NET 9.0,8.0,7.0,6.0
dotnet add package BMTLab.ImeiType.SystemTextJsonOr edit your .csproj:
<PackageReference Include="BMTLab.ImeiType.SystemTextJson" Version="1.2.0" />using System.Text.Json;
using BMTLab.ImeiType.SystemTextJson.Extensions;
var options = new JsonSerializerOptions().WithImeiConverter(); // Configure with IMEI converter
var obj = new ImeiContainer { Val = (Imei) 356303489916807 };
var json = JsonSerializer.Serialize(obj, options); //> { "val": 356303489916807 }
var deserialized = JsonSerializer.Deserialize<ImeiContainer>(json, options); //> ImeiContainer with Imei property setFor convenience, you can use one of the suggested extension methods:
.WithImeiConverter(JsonImeiWriteOptions writeOptions = JsonImeiWriteOptions.Default)(onJsonSerializerOptions) Attaches the IMEI converter with a chosen writing strategy..AddImeiConverter(JsonImeiWriteOptions writeOptions = JsonImeiWriteOptions.Default)(onIList<JsonConverter>) Allows adding the same converter in a more granular manner if you manage your own converter list.
The enum JsonImeiWriteOptions specifies how to write the IMEI
(number, string, or auto-select based on JsonSerializerOptions.NumberHandling).
[DefaultValue(Default)]
public enum JsonImeiWriteOptions
{
/// <summary>
/// Applies the default logic based on <see cref="JsonSerializerOptions.NumberHandling" />
/// to determine how IMEIs is written.
/// </summary>
Default = 0,
/// <summary>
/// Always writes IMEIs as a numeric (for example, <c>{"val": 356303489916807}</c>).
/// </summary>
ForceWriteAsNumber,
/// <summary>
/// Always writes IMEIs as a string (for example, <c>{"val": "356303489916807"}</c>).
/// </summary>
ForceWriteAsString
}I plan to add a couple of new optional extension projects for ImeiType,
such as integration with EF Core / Dapper.
This project uses the dependency FluentAssertions
version 7.0.0,
which is distributed under a license other than
MIT β Apache 2.0 β
but the ImeiType project does not include any binary files or source code
of FluentAssertions.
From creator:
Although this repo ships a working, reliable implementation of an IMEI type, it is a very niche need, and many folks who need it have likely built their own already. If the CI/CD and layout feel a bit over-engineered for a small .NET project, that is intentional: the primary goal here is to sharpen my MSBuild and GitHub Actions skills, practice publishing NuGet packages that multi-target different TFMs, and end up with a reusable template for future ideas.
If you have suggestions for cleaner build/automation flows, a better project structure, or practices that improve the developer/maintainer experience, I would love to hear them. Please feel free to fork this, contribute or let me know if you find a bug. Any ideas for improvement are always welcome as well π