//https://www.codeproject.com/Articles/311333/A-Csharp-Temperature-Struct namespace FoodFactory; using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; /// /// Options for temperature measurement units. /// public enum TemperatureUnit { /// /// The SI base unit of thermodynamic temperature, /// equal in magnitude to the degree Celsius. /// Kelvin, /// /// A scale of temperature on which water freezes /// at 0° and boils at 100° under standard conditions. /// Celsius, /// /// A scale of temperature on which water freezes at 32° /// and boils at 212° under standard conditions. /// Fahrenheit } /// /// A temperature value. /// public struct Temperature : IFormattable, IComparable, IComparable, IEquatable, IAdditionOperators, ISubtractionOperators, ISubtractionOperators, IEqualityOperators, IComparisonOperators { private double _kelvin; /// /// Creates a new temperature with the specified value in Kelvin. /// /// The value of the temperature. public Temperature(double kelvin) : this() { _kelvin = kelvin; } /// /// Creates a new temperature with the specified value in the /// specified unit of measurement. /// /// The value of the temperature. /// The unit of measurement that defines how /// the value is used. public Temperature(double temperature, TemperatureUnit unit) : this() { switch (unit) { case TemperatureUnit.Kelvin: _kelvin = temperature; break; case TemperatureUnit.Celsius: Celsius = temperature; break; case TemperatureUnit.Fahrenheit: Fahrenheit = temperature; break; default: throw new ArgumentException( "The temperature unit '" + unit.ToString() + "' is unknown."); } } /// /// Gets or sets the temperature value in Kelvin. /// public double Kelvin { readonly get => _kelvin; set => _kelvin = value; } /// /// Gets or sets the temperature value in Celsius. /// public double Celsius { readonly get => KelvinToCelsius(_kelvin); set => _kelvin = CelsiusToKelvin(value); } /// /// Gets or sets the temperature value in Fahrenheit. /// public double Fahrenheit { readonly get => KelvinToFahrenheit(_kelvin); set => _kelvin = FahrenheitToKelvin(value); } /// /// Gets the temperature value in the specified unit of measurement. /// /// The unit of measurement /// in which the temperature should be retrieved. /// The temperature value in the specified /// . public readonly double ValueIn(TemperatureUnit unit) => unit switch { TemperatureUnit.Kelvin => _kelvin, TemperatureUnit.Celsius => Celsius, TemperatureUnit.Fahrenheit => Fahrenheit, _ => throw new ArgumentException( "Unknown temperature unit '" + unit.ToString() + "'."), }; /// /// Returns a string representation of the temperature value. /// /// /// A single format specifier that indicates how to format the value of this /// temperature. The format parameter can be "G", /// "C", "F", or "K". If format /// is null or the empty string (""), "G" is used. /// /// /// An IFormatProvider reference that supplies culture-specific formatting /// services. /// /// A string representation of the temperature. /// /// The value of format is not null, the empty string (""), "G", "C", "F", or /// "K". /// public readonly string ToString(string? format, IFormatProvider? provider) { if (string.IsNullOrEmpty(format)) { format = "G"; } provider ??= CultureInfo.CurrentCulture; return format switch { "G" or "C" or "g" or "c" => Celsius.ToString("F2", provider) + " °C", "F" or "f" => Fahrenheit.ToString("F2", provider) + " °F", "K" or "k" => _kelvin.ToString("F2", provider) + " K", _ => throw new FormatException( string.Format("The {0} format string is not supported.", format)), }; } /// /// Returns a string representation of the temperature value. /// /// /// A single format specifier that indicates how to format the value of this /// temperature. The format parameter can be "G", /// "C", "F", or "K". If format /// is null or the empty string (""), "G" is used. /// /// A string representation of the temperature. /// /// The value of format is not null, /// the empty string (""), "G", "C", "F", or /// "K". /// public readonly string ToString(string format) => ToString(format, null); /// /// Returns a string representation of the temperature value. /// /// A string representation of the temperature. public override readonly string ToString() => ToString(null, null); /// /// Returns a string representation of the temperature value. /// /// /// The temperature unit as which the temperature value should be displayed. /// /// /// An IFormatProvider reference that supplies culture-specific formatting /// services. /// /// A string representation of the temperature. public readonly string ToString(TemperatureUnit unit, IFormatProvider? provider) { return unit switch { TemperatureUnit.Celsius => ToString("C", provider), TemperatureUnit.Fahrenheit => ToString("F", provider), TemperatureUnit.Kelvin => ToString("K", provider), _ => throw new FormatException("The temperature unit '" + unit.ToString() + "' is unknown."), }; } /// /// Returns a string representation of the temperature value. /// /// /// The temperature unit as which the temperature value should be displayed. /// /// A string representation of the temperature. public readonly string ToString(TemperatureUnit unit) => ToString(unit, null); /// /// Compares this instance to a specified Temperature object and returns an indication /// of their relative values. /// /// A Temperature object /// to compare to this instance. /// /// A signed number indicating the relative values of this instance and value. /// Value Description A negative integer This instance is less than value. Zero /// This instance is equal to value. A positive integer This instance is greater /// than value. /// public readonly int CompareTo(Temperature value) => _kelvin.CompareTo(value._kelvin); /// /// Compares this instance to a specified object and returns an indication of /// their relative values. /// /// An object to compare, or null. /// /// A signed number indicating the relative values of this instance and value. /// Value Description A negative integer This instance is less than value. Zero /// This instance is equal to value. A positive integer This instance is greater /// than value, or value is null. /// /// /// The value is not a Temperature. /// public readonly int CompareTo(object? value) { if (value == null) { return 1; } if (value is not Temperature) { throw new ArgumentException($"Can not compare {value.GetType()} and {GetType()}"); } return CompareTo((Temperature)value); } /// /// Determines whether or not the given temperature is considered equal to this instance. /// /// The temperature to compare to this instance. /// True if the temperature is considered equal /// to this instance. Otherwise, false. public readonly bool Equals(Temperature value) => _kelvin == value._kelvin; /// /// Determines whether or not the given object is considered equal to the temperature. /// /// The object to compare to the temperature. /// True if the object is considered equal /// to the temperature. Otherwise, false. public override readonly bool Equals([NotNullWhen(true)] object? value) => value is Temperature temp && Equals(temp); /// /// Returns the hash code for this instance. /// /// A 32-bit signed integer hash code. public override readonly int GetHashCode() => _kelvin.GetHashCode(); /// /// Determines the equality of two temperatures. /// /// The first temperature to be compared. /// The second temperature to be compared. /// True if the temperatures are equal. Otherwise, false. public static bool operator ==(Temperature t1, Temperature t2) => t1.Equals(t2); /// /// Determines the inequality of two temperatures. /// /// The first temperature to be compared. /// The second temperature to be compared. /// True if the temperatures are NOT equal. Otherwise, false. public static bool operator !=(Temperature t1, Temperature t2) => !t1.Equals(t2); /// /// Determines whether one temperature is considered greater than another. /// /// The first temperature to be compared. /// The second temperature to be compared. /// True if the first temperature is greater than the second. /// Otherwise, false. public static bool operator >(Temperature t1, Temperature t2) => t1._kelvin > t2._kelvin; /// /// Determines whether one temperature is considered less than another. /// /// The first temperature to be compared. /// The second temperature to be compared. /// True if the first temperature is less than the second. /// Otherwise, false. public static bool operator <(Temperature t1, Temperature t2) => t1._kelvin < t2._kelvin; /// /// Determines whether one temperature is considered greater to or equal to another. /// /// The first temperature to be compared. /// The second temperature to be compared. /// /// True if the first temperature is greater to or equal to the second. Otherwise, false. /// public static bool operator >=(Temperature t1, Temperature t2) => t1._kelvin >= t2._kelvin; /// /// Determines whether one temperature is considered less than or equal to another. /// /// The first temperature to be compared. /// The second temperature to be compared. /// /// True if the first temperature is less than or equal to the second. Otherwise, false. /// public static bool operator <=(Temperature t1, Temperature t2) => t1._kelvin <= t2._kelvin; /// /// Adds two instances of the temperature object. /// /// The temperature on the left-hand side of the operator. /// /// The temperature on the right-hand side of the operator. /// /// The sum of the two temperatures. public static Temperature operator +(Temperature t1, TemperatureDelta t2) => new(t1._kelvin + t2.KelvinDelta); /// /// Subtracts one instance from another. /// /// The temperature on the left-hand side of the operator. /// /// The temperature on the right-hand side of the operator. /// /// The difference of the two temperatures. public static TemperatureDelta operator -(Temperature t1, Temperature t2) => new(t1._kelvin - t2._kelvin); public static TemperatureDelta operator -(Temperature left, TemperatureDelta right) => new(left._kelvin - right.KelvinDelta); /// /// Converts a Kelvin temperature value to Celsius. /// /// The Kelvin value to convert to Celsius. /// /// The Kelvin value in Celsius. public static double KelvinToCelsius(double kelvin) => kelvin - 273.15; /// /// Converts a Celsius value to Kelvin. /// /// The Celsius value to convert to Kelvin. /// /// The Celsius value in Kelvin. public static double CelsiusToKelvin(double celsius) => celsius + 273.15; /// /// Converts a Kelvin value to Fahrenheit. /// /// The Kelvin value to convert to Fahrenheit. /// /// The Kelvin value in Fahrenheit. public static double KelvinToFahrenheit(double kelvin) => (kelvin * 9 / 5) - 459.67; /// /// Converts a Fahrenheit value to Kelvin. /// /// The Fahrenheit value to convert to Kelvin. /// /// The Fahrenheit value in Kelvin. public static double FahrenheitToKelvin(double fahrenheit) => (fahrenheit + 459.67) * 5 / 9; /// /// Converts a Fahrenheit value to Celsius. /// /// The Fahrenheit value to convert to Celsius. /// /// The Fahrenheit value in Celsius. public static double FahrenheitToCelsius(double fahrenheit) => (fahrenheit - 32) * 5 / 9; /// /// Converts a Celsius value to Fahrenheit. /// /// The Celsius value to convert to Fahrenheit. /// /// The Celsius value in Fahrenheit. public static double CelsiusToFahrenheit(double celsius) => (celsius * 9 / 5) + 32; } public readonly struct TemperatureDelta : IFormattable, IComparable, IComparable, IEquatable, IComparisonOperators, IEqualityOperators, IAdditionOperators, ISubtractionOperators, IMultiplyOperators, IDivisionOperators { private readonly double _kelvinDelta; public TemperatureDelta(double kelvin) { _kelvinDelta = kelvin; } public readonly double KelvinDelta => _kelvinDelta; public readonly double FahrenheitDelta => CelsiusToFahrenheitDelta(_kelvinDelta); public readonly double CelsiusDelta => _kelvinDelta; public TemperatureDelta(double temperature, TemperatureUnit unit) { _kelvinDelta = unit switch { TemperatureUnit.Celsius => temperature, TemperatureUnit.Fahrenheit => FahrenheitToKelvinDelta(temperature), TemperatureUnit.Kelvin => temperature, _ => throw new ArgumentException($"Unit {unit} is not a valid {nameof(TemperatureUnit)}"), }; } public static double FahrenheitToKelvinDelta(double fahrenheit) => fahrenheit * 5 / 9; public static double KelvinToFahrenheitDelta(double kelvin) => kelvin * 9 / 5; public static double FahrenheitToCelsiusDelta(double fahrenheit) => FahrenheitToKelvinDelta(fahrenheit); public static double CelsiusToFahrenheitDelta(double celsius) => KelvinToFahrenheitDelta(celsius); public bool Equals(TemperatureDelta other) => _kelvinDelta == other._kelvinDelta; public override bool Equals([NotNullWhen(true)] object? obj) => obj is TemperatureDelta delta && Equals(delta); public override int GetHashCode() => _kelvinDelta.GetHashCode(); public readonly string ToString(string? format, IFormatProvider? provider) { if (string.IsNullOrEmpty(format)) { format = "G"; } provider ??= CultureInfo.CurrentCulture; return format switch { "G" or "C" or "g" or "c" => CelsiusDelta.ToString("F2", provider) + " °C", "F" or "f" => FahrenheitDelta.ToString("F2", provider) + " °F", "K" or "k" => _kelvinDelta.ToString("F2", provider) + " K", _ => throw new FormatException( string.Format("The {0} format string is not supported.", format)), }; } public readonly string ToString(string format) => ToString(format, null); public override string ToString() => ToString(null, null); public readonly int CompareTo(TemperatureDelta value) => _kelvinDelta.CompareTo(value._kelvinDelta); public readonly int CompareTo(object? value) { if (value == null) { return 1; } if (value is not TemperatureDelta) { throw new ArgumentException( $"Object must be of type {nameof(TemperatureDelta)}", nameof(value)); } return CompareTo((TemperatureDelta)value); } public static TemperatureDelta operator +(TemperatureDelta left, TemperatureDelta right) => new(left._kelvinDelta + right._kelvinDelta); public static TemperatureDelta operator -(TemperatureDelta left, TemperatureDelta right) => new(left._kelvinDelta - right._kelvinDelta); public static TemperatureDelta operator *(TemperatureDelta left, double right) => new(left._kelvinDelta * right); public static TemperatureDelta operator /(TemperatureDelta left, double right) => new(left._kelvinDelta / right); public static bool operator >(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta > right._kelvinDelta; public static bool operator >=(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta >= right._kelvinDelta; public static bool operator <(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta < right._kelvinDelta; public static bool operator <=(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta <= right._kelvinDelta; public static bool operator ==(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta == right._kelvinDelta; public static bool operator !=(TemperatureDelta left, TemperatureDelta right) => left._kelvinDelta != right._kelvinDelta; }