この投稿で指定されている単位変換クラスを拡張しました: https://stackoverflow.com/a/7852721/1474894 これは私が今持っているものです:
public abstract class UnitBase<TUnitType, TValueType> where TUnitType : struct, IComparable, IConvertible, IFormattable
{
protected static TUnitType BaseUnit;
protected static TValueType BaseValue;
private static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();
private static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();
public static TValueType Convert(TValueType value, TUnitType from, TUnitType to)
{
// If both From/To are the same, don't do any work.
if (from.Equals(to))
return value;
// Convert into the base unit, if required.
var valueInBaseUnit = from.Equals(BaseUnit)
? value
: ConversionsFrom[from](value);
// Convert from the base unit into the requested unit, if required
var valueInRequiredUnit = to.Equals(BaseUnit)
? valueInBaseUnit
: ConversionsTo[to](valueInBaseUnit);
return valueInRequiredUnit;
}
protected static void RegisterConversion(TUnitType convertToUnit, Func<TValueType, TValueType> conversionTo, Func<TValueType, TValueType> conversionFrom)
{
if (!ConversionsTo.TryAdd(convertToUnit, conversionTo))
throw new ArgumentException("Already exists", "convertToUnit");
if (!ConversionsFrom.TryAdd(convertToUnit, conversionFrom))
throw new ArgumentException("Already exists", "convertToUnit");
}
public static string GetUnit(TUnitType unit)
{
var type = typeof(TUnitType);
if (!type.IsEnum)
{
throw new ArgumentException();
}
return Enums.GetEnumDescription(unit as Enum);
}
public TValueType InUnit(TUnitType unit)
{
return Convert(BaseValue, BaseUnit, unit);
}
}
次のような具体的な実装があります。
public enum TemperatureUnit
{
[System.ComponentModel.Description("°C")]
Celcius,
[System.ComponentModel.Description("°F")]
Fahrenheit
}
public class Temperature : UnitBase<TemperatureUnit, double>
{
static Temperature()
{
BaseUnit = TemperatureUnit.Celcius;
RegisterConversion(TemperatureUnit.Fahrenheit, x => x * 1.8d + 32d, x => (x - 32d) / 1.8d);
}
private Temperature(double value)
{
BaseValue = value;
}
public static Temperature FromUnit(double value, TemperatureUnit unit)
{
return new Temperature(Convert(value, unit, BaseUnit));
}
}
基本クラスの一部として ToUnit メソッドをジェネリックにすることができましたが、FromUnit メソッドを使用すると、具象クラスごとに 1 つになりました (これらはすべて非常に似ています)。私はこれを行うことができるようにしたい:
Temperature temperature = Temperature.FromUnit(10, TemperatureUnit.Celcius);
Pressure pressure = Pressure.FromUnit(50, PressureUnit.Bar);
...各具象クラスに FromUnit を実装する必要はありません。これは静的メソッドであるため、インターフェイスを使用して実装を強制することはできません。具体的なクラスにパブリックコンストラクターを持ち、次のようなものを持つことができます:
public static T FromUnit<T>(TValueType value, TUnitType unit) where T : new()
{
BaseValue = Convert(value, unit, BaseUnit);
return new T();
}
しかし、それはあまり良くないと思います。私は行かなければなりません:
Temperature temperature = Temperature.FromUnit<Temperature>(10, TemperatureUnit.Celcius);
は<Temperature>
ちょっと冗長だと思います。
ジェネリック FromUnit をより良い方法で実装することは可能ですか?
編集: 各サブクラスに FromUnit を実装する代わりに、基本的には欲しい:
public static Temperature FromUnit(double value, TemperatureUnit unit)
{
return new Temperature(Convert(value, unit, BaseUnit));
}
public static Pressure FromUnit(double value, PressureUnit unit)
{
return new Pressure(Convert(value, unit, BaseUnit));
}
public static Speed FromUnit(double value, SpeedUnit unit)
{
return new Speed(Convert(value, unit, BaseUnit));
}
...
...ジェネリック FromUnit を基本クラスに配置します。