4

この投稿で指定されている単位変換クラスを拡張しました: 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 を基本クラスに配置します。

4

2 に答える 2

1

<Temperature>「あまり良くない」ソリューションの型パラメーターは冗長に見えるかもしれませんが、実際にはそうではありません。あなたが書くとき

Temperature temperature = Temperature.FromUnit<Temperature>(10, TemperatureUnit.Celcius);

コードは実際に変換されます

Temperature temperature = UnitBase<TemperatureUnit, double>.FromUnit<Temperature>(10, TemperatureUnit.Celcius);

Temperatureコンパイラは、そのメソッドへのアクセスに使用できるほど優れています。実際に を呼び出しているため、UnitBase.FromUnit()実際に を使用する必要があることを認識していないためTemperature、追加の型パラメーターとして指定する必要があります。

于 2013-05-26T20:30:48.230 に答える