495 lines
19 KiB
C#
495 lines
19 KiB
C#
//https://www.codeproject.com/Articles/311333/A-Csharp-Temperature-Struct
|
|
namespace FoodFactory;
|
|
|
|
|
|
|
|
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Numerics;
|
|
|
|
/// <summary>
|
|
/// Options for temperature measurement units.
|
|
/// </summary>
|
|
public enum TemperatureUnit
|
|
{
|
|
/// <summary>
|
|
/// The SI base unit of thermodynamic temperature,
|
|
/// equal in magnitude to the degree Celsius.
|
|
/// </summary>
|
|
Kelvin,
|
|
|
|
/// <summary>
|
|
/// A scale of temperature on which water freezes
|
|
/// at 0° and boils at 100° under standard conditions.
|
|
/// </summary>
|
|
Celsius,
|
|
|
|
/// <summary>
|
|
/// A scale of temperature on which water freezes at 32°
|
|
/// and boils at 212° under standard conditions.
|
|
/// </summary>
|
|
Fahrenheit
|
|
}
|
|
|
|
/// <summary>
|
|
/// A temperature value.
|
|
/// </summary>
|
|
|
|
public struct Temperature : IFormattable, IComparable,
|
|
IComparable<Temperature>, IEquatable<Temperature>,
|
|
IAdditionOperators<Temperature, TemperatureDelta, Temperature>,
|
|
ISubtractionOperators<Temperature, Temperature, TemperatureDelta>,
|
|
ISubtractionOperators<Temperature, TemperatureDelta, TemperatureDelta>,
|
|
IEqualityOperators<Temperature, Temperature, bool>,
|
|
IComparisonOperators<Temperature, Temperature, bool>
|
|
{
|
|
private double _kelvin;
|
|
|
|
/// <summary>
|
|
/// Creates a new temperature with the specified value in Kelvin.
|
|
/// </summary>
|
|
/// <param name="kelvin">The value of the temperature.</param>
|
|
public Temperature(double kelvin) : this() { _kelvin = kelvin; }
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a new temperature with the specified value in the
|
|
/// specified unit of measurement.
|
|
/// </summary>
|
|
/// <param name="temperature">The value of the temperature.</param>
|
|
/// <param name="unit">The unit of measurement that defines how
|
|
/// the <paramref name="temperature"/> value is used.</param>
|
|
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.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the temperature value in Kelvin.
|
|
/// </summary>
|
|
public double Kelvin
|
|
{
|
|
readonly get => _kelvin;
|
|
|
|
set => _kelvin = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the temperature value in Celsius.
|
|
/// </summary>
|
|
public double Celsius
|
|
{
|
|
readonly get => KelvinToCelsius(_kelvin); set => _kelvin = CelsiusToKelvin(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the temperature value in Fahrenheit.
|
|
/// </summary>
|
|
public double Fahrenheit
|
|
{
|
|
readonly get => KelvinToFahrenheit(_kelvin); set => _kelvin = FahrenheitToKelvin(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the temperature value in the specified unit of measurement.
|
|
/// </summary>
|
|
/// <param name="unit">The unit of measurement
|
|
/// in which the temperature should be retrieved.</param>
|
|
/// <returns>The temperature value in the specified
|
|
/// <paramref name="unit"/>.</returns>
|
|
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() + "'."),
|
|
};
|
|
|
|
|
|
/// <summary>
|
|
/// Returns a string representation of the temperature value.
|
|
/// </summary>
|
|
/// <param name="format">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="provider">
|
|
/// An IFormatProvider reference that supplies culture-specific formatting
|
|
/// services.
|
|
/// </param>
|
|
/// <returns>A string representation of the temperature.</returns>
|
|
/// <exception cref="FormatException">
|
|
/// The value of format is not null, the empty string (""), "G", "C", "F", or
|
|
/// "K".
|
|
/// </exception>
|
|
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)),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a string representation of the temperature value.
|
|
/// </summary>
|
|
/// <param name="format">
|
|
/// 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.
|
|
/// </param>
|
|
/// <returns>A string representation of the temperature.</returns>
|
|
/// <exception cref="FormatException">
|
|
/// The value of format is not null,
|
|
/// the empty string (""), "G", "C", "F", or
|
|
/// "K".
|
|
/// </exception>
|
|
public readonly string ToString(string format) => ToString(format, null);
|
|
|
|
/// <summary>
|
|
/// Returns a string representation of the temperature value.
|
|
/// </summary>
|
|
/// <returns>A string representation of the temperature.</returns>
|
|
public override readonly string ToString() => ToString(null, null);
|
|
|
|
/// <summary>
|
|
/// Returns a string representation of the temperature value.
|
|
/// </summary>
|
|
/// <param name="unit">
|
|
/// The temperature unit as which the temperature value should be displayed.
|
|
/// </param>
|
|
/// <param name="provider">
|
|
/// An IFormatProvider reference that supplies culture-specific formatting
|
|
/// services.
|
|
/// </param>
|
|
/// <returns>A string representation of the temperature.</returns>
|
|
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."),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a string representation of the temperature value.
|
|
/// </summary>
|
|
/// <param name="unit">
|
|
/// The temperature unit as which the temperature value should be displayed.
|
|
/// </param>
|
|
/// <returns>A string representation of the temperature.</returns>
|
|
public readonly string ToString(TemperatureUnit unit) => ToString(unit, null);
|
|
|
|
/// <summary>
|
|
/// Compares this instance to a specified Temperature object and returns an indication
|
|
/// of their relative values.
|
|
/// </summary>
|
|
/// <param name="value">A Temperature object
|
|
/// to compare to this instance.</param>
|
|
/// <returns>
|
|
/// 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.
|
|
/// </returns>
|
|
public readonly int CompareTo(Temperature value) => _kelvin.CompareTo(value._kelvin);
|
|
|
|
/// <summary>
|
|
/// Compares this instance to a specified object and returns an indication of
|
|
/// their relative values.
|
|
/// </summary>
|
|
/// <param name="value">An object to compare, or null.</param>
|
|
/// <returns>
|
|
/// 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.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentException">
|
|
/// The value is not a Temperature.
|
|
/// </exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether or not the given temperature is considered equal to this instance.
|
|
/// </summary>
|
|
/// <param name="value">The temperature to compare to this instance.</param>
|
|
/// <returns>True if the temperature is considered equal
|
|
/// to this instance. Otherwise, false.</returns>
|
|
public readonly bool Equals(Temperature value) => _kelvin == value._kelvin;
|
|
|
|
/// <summary>
|
|
/// Determines whether or not the given object is considered equal to the temperature.
|
|
/// </summary>
|
|
/// <param name="value">The object to compare to the temperature.</param>
|
|
/// <returns>True if the object is considered equal
|
|
/// to the temperature. Otherwise, false.</returns>
|
|
public override readonly bool Equals([NotNullWhen(true)] object? value) => value is Temperature temp && Equals(temp);
|
|
|
|
|
|
/// <summary>
|
|
/// Returns the hash code for this instance.
|
|
/// </summary>
|
|
/// <returns>A 32-bit signed integer hash code.</returns>
|
|
public override readonly int GetHashCode() => _kelvin.GetHashCode();
|
|
|
|
/// <summary>
|
|
/// Determines the equality of two temperatures.
|
|
/// </summary>
|
|
/// <param name="t1">The first temperature to be compared.</param>
|
|
/// <param name="t2">The second temperature to be compared.</param>
|
|
/// <returns>True if the temperatures are equal. Otherwise, false.</returns>
|
|
public static bool operator ==(Temperature t1, Temperature t2) => t1.Equals(t2);
|
|
|
|
/// <summary>
|
|
/// Determines the inequality of two temperatures.
|
|
/// </summary>
|
|
/// <param name="t1">The first temperature to be compared.</param>
|
|
/// <param name="t2">The second temperature to be compared.</param>
|
|
/// <returns>True if the temperatures are NOT equal. Otherwise, false.</returns>
|
|
public static bool operator !=(Temperature t1, Temperature t2) => !t1.Equals(t2);
|
|
|
|
/// <summary>
|
|
/// Determines whether one temperature is considered greater than another.
|
|
/// </summary>
|
|
/// <param name="t1">The first temperature to be compared.</param>
|
|
/// <param name="t2">The second temperature to be compared.</param>
|
|
/// <returns>True if the first temperature is greater than the second.
|
|
/// Otherwise, false.</returns>
|
|
public static bool operator >(Temperature t1, Temperature t2) => t1._kelvin > t2._kelvin;
|
|
|
|
/// <summary>
|
|
/// Determines whether one temperature is considered less than another.
|
|
/// </summary>
|
|
/// <param name="t1">The first temperature to be compared.</param>
|
|
/// <param name="t2">The second temperature to be compared.</param>
|
|
/// <returns>True if the first temperature is less than the second.
|
|
/// Otherwise, false.</returns>
|
|
public static bool operator <(Temperature t1, Temperature t2) => t1._kelvin < t2._kelvin;
|
|
|
|
/// <summary>
|
|
/// Determines whether one temperature is considered greater to or equal to another.
|
|
/// </summary>
|
|
/// <param name="t1">The first temperature to be compared.</param>
|
|
/// <param name="t2">The second temperature to be compared.</param>
|
|
/// <returns>
|
|
/// True if the first temperature is greater to or equal to the second. Otherwise, false.
|
|
/// </returns>
|
|
public static bool operator >=(Temperature t1, Temperature t2) => t1._kelvin >= t2._kelvin;
|
|
|
|
/// <summary>
|
|
/// Determines whether one temperature is considered less than or equal to another.
|
|
/// </summary>
|
|
/// <param name="t1">The first temperature to be compared.</param>
|
|
/// <param name="t2">The second temperature to be compared.</param>
|
|
/// <returns>
|
|
/// True if the first temperature is less than or equal to the second. Otherwise, false.
|
|
/// </returns>
|
|
public static bool operator <=(Temperature t1, Temperature t2) => t1._kelvin <= t2._kelvin;
|
|
|
|
/// <summary>
|
|
/// Adds two instances of the temperature object.
|
|
/// </summary>
|
|
/// <param name="t1">The temperature on the left-hand side of the operator.
|
|
/// </param>
|
|
/// <param name="t2">The temperature on the right-hand side of the operator.
|
|
/// </param>
|
|
/// <returns>The sum of the two temperatures.</returns>
|
|
public static Temperature operator +(Temperature t1, TemperatureDelta t2) => new(t1._kelvin + t2.KelvinDelta);
|
|
|
|
/// <summary>
|
|
/// Subtracts one instance from another.
|
|
/// </summary>
|
|
/// <param name="t1">The temperature on the left-hand side of the operator.
|
|
/// </param>
|
|
/// <param name="t2">The temperature on the right-hand side of the operator.
|
|
/// </param>
|
|
/// <returns>The difference of the two temperatures.</returns>
|
|
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);
|
|
|
|
/// <summary>
|
|
/// Converts a Kelvin temperature value to Celsius.
|
|
/// </summary>
|
|
/// <param name="kelvin">The Kelvin value to convert to Celsius.
|
|
/// </param>
|
|
/// <returns>The Kelvin value in Celsius.</returns>
|
|
public static double KelvinToCelsius(double kelvin) => kelvin - 273.15;
|
|
|
|
/// <summary>
|
|
/// Converts a Celsius value to Kelvin.
|
|
/// </summary>
|
|
/// <param name="celsius">The Celsius value to convert to Kelvin.
|
|
/// </param>
|
|
/// <returns>The Celsius value in Kelvin.</returns>
|
|
public static double CelsiusToKelvin(double celsius) => celsius + 273.15;
|
|
|
|
/// <summary>
|
|
/// Converts a Kelvin value to Fahrenheit.
|
|
/// </summary>
|
|
/// <param name="kelvin">The Kelvin value to convert to Fahrenheit.
|
|
/// </param>
|
|
/// <returns>The Kelvin value in Fahrenheit.</returns>
|
|
public static double KelvinToFahrenheit(double kelvin) => (kelvin * 9 / 5) - 459.67;
|
|
|
|
/// <summary>
|
|
/// Converts a Fahrenheit value to Kelvin.
|
|
/// </summary>
|
|
/// <param name="fahrenheit">The Fahrenheit value to convert to Kelvin.
|
|
/// </param>
|
|
/// <returns>The Fahrenheit value in Kelvin.</returns>
|
|
public static double FahrenheitToKelvin(double fahrenheit) => (fahrenheit + 459.67) * 5 / 9;
|
|
|
|
/// <summary>
|
|
/// Converts a Fahrenheit value to Celsius.
|
|
/// </summary>
|
|
/// <param name="fahrenheit">The Fahrenheit value to convert to Celsius.
|
|
/// </param>
|
|
/// <returns>The Fahrenheit value in Celsius.</returns>
|
|
public static double FahrenheitToCelsius(double fahrenheit) => (fahrenheit - 32) * 5 / 9;
|
|
|
|
/// <summary>
|
|
/// Converts a Celsius value to Fahrenheit.
|
|
/// </summary>
|
|
/// <param name="celsius">The Celsius value to convert to Fahrenheit.
|
|
/// </param>
|
|
/// <returns>The Celsius value in Fahrenheit.</returns>
|
|
public static double CelsiusToFahrenheit(double celsius) => (celsius * 9 / 5) + 32;
|
|
}
|
|
|
|
|
|
|
|
|
|
public readonly struct TemperatureDelta : IFormattable, IComparable,
|
|
IComparable<TemperatureDelta>, IEquatable<TemperatureDelta>,
|
|
IComparisonOperators<TemperatureDelta, TemperatureDelta, bool>,
|
|
IEqualityOperators<TemperatureDelta, TemperatureDelta, bool>,
|
|
IAdditionOperators<TemperatureDelta, TemperatureDelta, TemperatureDelta>,
|
|
ISubtractionOperators<TemperatureDelta, TemperatureDelta, TemperatureDelta>,
|
|
IMultiplyOperators<TemperatureDelta, double, TemperatureDelta>,
|
|
IDivisionOperators<TemperatureDelta, double, TemperatureDelta>
|
|
{
|
|
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;
|
|
}
|