#pragma warning disable CA1707 // Identifiers should not contain underscores #pragma warning disable IDE1006 // Naming rule violation // ReSharper disable InconsistentNaming /// Strongly-typed file size units (bytes through gibibytes) that convert and compare by underlying byte value. public static class FileSizes { /// Represents a file size that can be expressed as a number of bytes. public interface IFileSize : IComparable, IEquatable { /// Gets the size in bytes. ulong Value { get; } } /// /// Base implementation shared by all file size units. Handles comparison, equality /// and hashing on the underlying byte value so that any two /// instances are comparable regardless of the unit they were constructed with. /// public abstract class FileSize : IFileSize { /// Initializes a new instance of the class from a byte count. protected FileSize(ulong bytes) => Value = bytes; /// Gets the size in bytes. public ulong Value { get; } /// Gets the unit suffix used by , e.g. "KiB". protected abstract string UnitSuffix { get; } /// Gets the size expressed in this instance's own unit, for display purposes. protected abstract decimal UnitValue { get; } /// public int CompareTo(IFileSize? other) => other is null ? 1 : Value.CompareTo(other.Value); /// public bool Equals(IFileSize? other) => other is not null && Value == other.Value; /// public override bool Equals(object? obj) => obj is IFileSize other && Equals(other); /// public override int GetHashCode() => Value.GetHashCode(); /// /// Formats using this instance's own unit, e.g. "4.52 GiB" — not the smallest common unit. public override string ToString() => $"{UnitValue} {UnitSuffix}"; /// Determines whether two file sizes represent the same number of bytes. public static bool operator ==(FileSize? left, IFileSize? right) => left is null ? right is null : left.Equals(right); /// Determines whether two file sizes represent a different number of bytes. public static bool operator !=(FileSize? left, IFileSize? right) => !(left == right); /// Determines whether the left file size is smaller than the right. public static bool operator <(FileSize left, IFileSize right) => left.CompareTo(right) < 0; /// Determines whether the left file size is larger than the right. public static bool operator >(FileSize left, IFileSize right) => left.CompareTo(right) > 0; /// Determines whether the left file size is smaller than or equal to the right. public static bool operator <=(FileSize left, IFileSize right) => left.CompareTo(right) <= 0; /// Determines whether the left file size is larger than or equal to the right. public static bool operator >=(FileSize left, IFileSize right) => left.CompareTo(right) >= 0; /// Adds two file sizes, returning the result in bytes. public static Bytes operator +(FileSize left, IFileSize right) => new(left.Value + right.Value); /// Subtracts one file size from another, returning the result in bytes. public static Bytes operator -(FileSize left, IFileSize right) => new(left.Value - right.Value); /// Converts a fractional count of a unit (e.g. 4.52 GB) into whole bytes. protected static ulong ToBytes(decimal unitValue, decimal bytesPerUnit) => (ulong)Math.Round(unitValue * bytesPerUnit, MidpointRounding.AwayFromZero); } /// Represents a size in bytes. Bytes are atomic, so this is the only unit that takes a whole number. public sealed class Bytes(ulong value) : FileSize(value) { /// Converts a size expressed in any unit into bytes. public Bytes(IFileSize other) : this(other.Value) { } /// protected override string UnitSuffix => "bytes"; /// protected override decimal UnitValue => Value; } /// Represents a size in kilobytes (decimal: 1,000 bytes). public sealed class KB(decimal value) : FileSize(ToBytes(value, BytesPerUnit)) { private const decimal BytesPerUnit = 1_000; /// Converts a size expressed in any unit into kilobytes. public KB(IFileSize other) : this(other.Value / BytesPerUnit) { } /// protected override string UnitSuffix => "KB"; /// protected override decimal UnitValue => Value / BytesPerUnit; } /// Represents a size in kibibytes (binary: 1,024 bytes). public sealed class KiB(decimal value) : FileSize(ToBytes(value, BytesPerUnit)) { private const decimal BytesPerUnit = 1_024; /// Converts a size expressed in any unit into kibibytes. public KiB(IFileSize other) : this(other.Value / BytesPerUnit) { } /// protected override string UnitSuffix => "KiB"; /// protected override decimal UnitValue => Value / BytesPerUnit; } /// Represents a size in megabytes (decimal: 1,000,000 bytes). public sealed class MB(decimal value) : FileSize(ToBytes(value, BytesPerUnit)) { private const decimal BytesPerUnit = 1_000_000; /// Converts a size expressed in any unit into megabytes. public MB(IFileSize other) : this(other.Value / BytesPerUnit) { } /// protected override string UnitSuffix => "MB"; /// protected override decimal UnitValue => Value / BytesPerUnit; } /// Represents a size in mebibytes (binary: 1,048,576 bytes). public sealed class MiB(decimal value) : FileSize(ToBytes(value, BytesPerUnit)) { private const decimal BytesPerUnit = 1_048_576; /// Converts a size expressed in any unit into mebibytes. public MiB(IFileSize other) : this(other.Value / BytesPerUnit) { } /// protected override string UnitSuffix => "MiB"; /// protected override decimal UnitValue => Value / BytesPerUnit; } /// Represents a size in gigabytes (decimal: 1,000,000,000 bytes). public sealed class GB(decimal value) : FileSize(ToBytes(value, BytesPerUnit)) { private const decimal BytesPerUnit = 1_000_000_000; /// Converts a size expressed in any unit into gigabytes. public GB(IFileSize other) : this(other.Value / BytesPerUnit) { } /// protected override string UnitSuffix => "GB"; /// protected override decimal UnitValue => Value / BytesPerUnit; } /// Represents a size in gibibytes (binary: 1,073,741,824 bytes). public sealed class GiB(decimal value) : FileSize(ToBytes(value, BytesPerUnit)) { private const decimal BytesPerUnit = 1_073_741_824; /// Converts a size expressed in any unit into gibibytes. public GiB(IFileSize other) : this(other.Value / BytesPerUnit) { } /// protected override string UnitSuffix => "GiB"; /// protected override decimal UnitValue => Value / BytesPerUnit; } } #pragma warning restore CA1707 #pragma warning restore IDE1006