如何创建一个通用的转换器,用于测量单位在C#中? [英] How do I create a generic converter for units of measurement in C#?

查看:165
本文介绍了如何创建一个通用的转换器,用于测量单位在C#中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力学习多一点,而工作的一个小的烹饪项目,涉及温度转换以及一些烹饪测量转换,例如帝公制,我一直在努力想办法,代表和lambda表达式做一个可扩展的单位换算。

下面是我开始的,以及在什么一些我的计划是code意见。我不打算使用它像下面,我只是测试出的C#我不很清楚的一些特点,我也不能确定如何进一步利用这个。有没有人对如何创造什么,我在下面的评论说什么有什么建议?谢谢

 命名空间TemperatureConverter
{
    类节目
    {
        静态无效的主要(字串[] args)
        {
            //华氏摄氏度:[°C] =([°F] ​​ -  32)×5/9
            VAR CelsiusResult = Converter.Convert(11M,Converter.FahrenheitToCelsius);

            //摄氏华氏:[°F] ​​= [°C]×9/5 + 32
            VAR FahrenheitResult = Converter.Convert(11M,Converter.CelsiusToFahrenheit);

            Console.WriteLine(华氏摄氏:+ CelsiusResult);
            Console.WriteLine(摄氏华氏:+ FahrenheitResult);
            到Console.ReadLine();

            //如果我想补充的温度的另一个单位,即开尔文
            //那么我就需要计算开尔文为摄氏温度,摄氏开尔文,开尔文飞轮海,飞轮海开尔文
            //摄氏开尔文:[K] = [°C] + 273.15
            //开尔文为摄氏:[°C] = [K]  -  273.15
            //华氏开尔文:[K] =([°F] ​​+ 459.67)×5/9
            //开尔文为华氏:[°F] ​​= [K]×9/5  -  459.67
            //我们的计划是有转换器,一个单一的目的转换为
            //一个特定的单位类型如摄氏和创建独立的单元转换器
            //包含计算,这需要一指定单元类型,然后把它们转换成特定单元的类型,在这个例子中其摄氏的列表。
        }
    }

    //目前这是一个静态类,但我希望把它变成一个接口或抽象类
    //所以,无论实现此接口将与通用deligate转换的列表提供
    //它可以调用,您可以通过在需要时增加更多的扩展。
    公共静态类转换器
    {
        公共静态函数功能:LT;小数,小数> CelsiusToFahrenheit = X => (X *(9M / 5M))+ 32M;
        公共静态函数功能:LT;小数,小数> FahrenheitToCelsius = X => (X  -  32)*(5M / 9M);

        公共静态十进制转换(十进制valueToConvert,Func键<小数,小数>转换){
            返回conversion.Invoke(valueToConvert);
        }
    }
}
 

更新:试图澄清我的问题:

使用只是我的体温下面的例子中,我如何创建一个包含了拉姆达转换的列表,以摄氏一类通过它来传递一个给定的温度下,它会尝试将其转换为摄氏(如果计算可用)

例伪code:

 枚举温度
{
    摄氏,
    飞轮海,
    开
}

UnitConverter CelsiusConverter =新UnitConverter(Temperature.Celsius);
CelsiusConverter.AddCalc(FahrenheitToCelsius,拉姆达这里);
CelsiusConverter.Convert(Temperature.Fahrenheit,11);
 

解决方案

我认为这是一个有趣的小问题,所以我决定去看看很好,这可能被包装成一个通用的实现。这是没有得到很好的测试(和不处理所有的错误情况下 - 比如,如果你不注册一个特定的单位类型转换为,然后通过在),但它可能是有用的。重点是使继承的类( TemperatureConverter )的整齐越好。

  ///<总结>
///对于不同单位的值之间转换的通用转换类。
///< /总结>
///< typeparam名=TUnitType>该类型重新presenting单位类型(如枚举。)< / typeparam>
///< typeparam名=TValueType>将价值这个单位(浮点,小数,INT等)所述的类型; / typeparam>
抽象类UnitConverter< TUnitType,TValueType>
{
    ///<总结>
    ///基本单元,所有的计算将是pssed来讲EX $ P $。
    ///< /总结>
    受保护的静态TUnitType的BaseUnit;

    ///<总结>
    ///功能字典,以从基座单元类型转换为特定的类型。
    ///< /总结>
    静态ConcurrentDictionary< TUnitType,Func键< TValueType,TValueType>> ConversionsTo =新ConcurrentDictionary< TUnitType,Func键< TValueType,TValueType>>();

    ///<总结>
    ///功能字典从指定的类型分为基本单元类型转换。
    ///< /总结>
    静态ConcurrentDictionary< TUnitType,Func键< TValueType,TValueType>> ConversionsFrom =新ConcurrentDictionary< TUnitType,Func键< TValueType,TValueType>>();

    ///<总结>
    ///一个值转换从一个单位到另一种。
    ///< /总结>
    ///<参数名称=值>要转换的值小于/参数>
    ///<参数=从&GT的名字;单位类型所提供的价值是与< /参数>
    ///< PARAM NAME =到>在单位类型转换值到< /参数>
    ///<返回>转换后的值小于/回报>
    公共TValueType转换(TValueType值,从TUnitType,TUnitType到)
    {
        //如果两个自/至是相同的,没有做任何的工作。
        如果(from.Equals(到))
            返回值;

        //转换为基本单元,如果需要的话。
        VAR valueInBaseUnit = from.Equals(的BaseUnit)
                                ?值
                                :ConversionsFrom [从](值);

        //从基本单元转换为所请求的单元,如果需要的话
        VAR valueInRequiredUnit = to.Equals(的BaseUnit)
                                ? valueInBaseUnit
                                :ConversionsTo [来](valueInBaseUnit);

        返回valueInRequiredUnit;
    }

    ///<总结>
    ///注册功能,用于从单元转换为/。
    ///< /总结>
    ///&所述; PARAM NAME =convertToUnit>由单元的类型转换到/从,从基本单元和所述; /参数>
    ///< PARAM NAME =conversionTo> A函数从基本单元转换< /参数>
    ///< PARAM NAME =conversionFrom> A函数将转换为基本单元< /参数>
    受保护的静态无效RegisterConversion(TUnitType convertToUnit,Func键< TValueType,TValueType> conversionTo,Func键< TValueType,TValueType> conversionFrom)
    {
        如果(!ConversionsTo.TryAdd(convertToUnit,conversionTo))
            抛出新的ArgumentException(已经存在,convertToUnit);
        如果(!ConversionsFrom.TryAdd(convertToUnit,conversionFrom))
            抛出新的ArgumentException(已经存在,convertToUnit);
    }
}
 

泛型类型的args是为重新presents单位枚举,并为值的类型。要使用它,你只要从这个类(提供种类)继承并注册一些lambda表达式进行转换。下面是温度的例子(有一些虚拟计算):

 枚举温度
{
    摄氏,
    飞轮海,
    开
}

类TemperatureConverter:UnitConverter<温度,浮动>
{
    静态TemperatureConverter()
    {
        的BaseUnit = Temperature.Celcius;
        RegisterConversion(Temperature.Fahrenheit,V => V * 2F,V => V * 0.5F);
        RegisterConversion(Temperature.Kelvin,V => V * 10f中,V => V * 0.05f);
    }
}
 

,然后用它是pretty的简单:

  VAR转换器=新TemperatureConverter();

Console.WriteLine(converter.Convert(1,Temperature.Celcius,Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1,Temperature.Fahrenheit,Temperature.Celcius));

Console.WriteLine(converter.Convert(1,Temperature.Celcius,Temperature.Kelvin));
Console.WriteLine(converter.Convert(1,Temperature.Kelvin,Temperature.Celcius));

Console.WriteLine(converter.Convert(1,Temperature.Kelvin,Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1,Temperature.Fahrenheit,Temperature.Kelvin));
 

I have been trying to learn a bit more about delegates and lambdas while working on a small cooking project that involves temperature conversion as well as some cooking measurement conversions such as Imperial to Metric and I've been trying to think of a way to make an extensible Unit converter.

Here is what I started with, along with code comments on what some of my plans were. I have no plan to use it like the below, I was just testing out some features of C# I don't know very well, I am also unsure how to take this further. Does anyone have any suggestions on how to create what I am talking about in the comments below? Thanks

namespace TemperatureConverter
{
    class Program
    {
        static void Main(string[] args)
        {
            // Fahrenheit to Celsius :  [°C] = ([°F] − 32) × 5⁄9
            var CelsiusResult = Converter.Convert(11M,Converter.FahrenheitToCelsius);

            // Celsius to Fahrenheit : [°F] = [°C] × 9⁄5 + 32
            var FahrenheitResult = Converter.Convert(11M, Converter.CelsiusToFahrenheit);

            Console.WriteLine("Fahrenheit to Celsius : " + CelsiusResult);
            Console.WriteLine("Celsius to Fahrenheit : " + FahrenheitResult);
            Console.ReadLine();

            // If I wanted to add another unit of temperature i.e. Kelvin 
            // then I would need calculations for Kelvin to Celsius, Celsius to Kelvin, Kelvin to Fahrenheit, Fahrenheit to Kelvin
            // Celsius to Kelvin : [K] = [°C] + 273.15
            // Kelvin to Celsius : [°C] = [K] − 273.15
            // Fahrenheit to Kelvin : [K] = ([°F] + 459.67) × 5⁄9
            // Kelvin to Fahrenheit : [°F] = [K] × 9⁄5 − 459.67
            // The plan is to have the converters with a single purpose to convert to
            //one particular unit type e.g. Celsius and create separate unit converters 
            //that contain a list of calculations that take one specified unit type and then convert to their particular unit type, in this example its Celsius.
        }
    }

    // at the moment this is a static class but I am looking to turn this into an interface or abstract class
    // so that whatever implements this interface would be supplied with a list of generic deligate conversions
    // that it can invoke and you can extend by adding more when required.
    public static class Converter
    {
        public static Func<decimal, decimal> CelsiusToFahrenheit = x => (x * (9M / 5M)) + 32M;
        public static Func<decimal, decimal> FahrenheitToCelsius = x => (x - 32M) * (5M / 9M);

        public static decimal Convert(decimal valueToConvert, Func<decimal, decimal> conversion) {
            return conversion.Invoke(valueToConvert);
        }
    }
}

Update: Trying to clarify my question:

Using just my temperature example below, how would I create a class that contains a list of lambda conversions to Celsius which you then pass it a given temperature and it will try and convert that to Celsius (if the calculation is available)

Example pseudo code:

enum Temperature
{
    Celcius,
    Fahrenheit,
    Kelvin
}

UnitConverter CelsiusConverter = new UnitConverter(Temperature.Celsius);
CelsiusConverter.AddCalc("FahrenheitToCelsius", lambda here);
CelsiusConverter.Convert(Temperature.Fahrenheit, 11);

解决方案

I thought this was an interesting little problem, so I decided to see how nicely this could be wrapped up into a generic implementation. This isn't well-tested (and doesn't handle all error cases - such as if you don't register the conversion for a particular unit type, then pass that in), but it might be useful. The focus was on making the inherited class (TemperatureConverter) as tidy as possible.

/// <summary>
/// Generic conversion class for converting between values of different units.
/// </summary>
/// <typeparam name="TUnitType">The type representing the unit type (eg. enum)</typeparam>
/// <typeparam name="TValueType">The type of value for this unit (float, decimal, int, etc.)</typeparam>
abstract class UnitConverter<TUnitType, TValueType>
{
    /// <summary>
    /// The base unit, which all calculations will be expressed in terms of.
    /// </summary>
    protected static TUnitType BaseUnit;

    /// <summary>
    /// Dictionary of functions to convert from the base unit type into a specific type.
    /// </summary>
    static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsTo = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    /// <summary>
    /// Dictionary of functions to convert from the specified type into the base unit type.
    /// </summary>
    static ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>> ConversionsFrom = new ConcurrentDictionary<TUnitType, Func<TValueType, TValueType>>();

    /// <summary>
    /// Converts a value from one unit type to another.
    /// </summary>
    /// <param name="value">The value to convert.</param>
    /// <param name="from">The unit type the provided value is in.</param>
    /// <param name="to">The unit type to convert the value to.</param>
    /// <returns>The converted value.</returns>
    public 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;
    }

    /// <summary>
    /// Registers functions for converting to/from a unit.
    /// </summary>
    /// <param name="convertToUnit">The type of unit to convert to/from, from the base unit.</param>
    /// <param name="conversionTo">A function to convert from the base unit.</param>
    /// <param name="conversionFrom">A function to convert to the base unit.</param>
    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");
    }
}

The generic type args are for an enum that represents the units, and the type for the value. To use it, you just have to inherit from this class (providing the types) and register some lambdas to do the conversion. Here's an example for temperature (with some dummy calculations):

enum Temperature
{
    Celcius,
    Fahrenheit,
    Kelvin
}

class TemperatureConverter : UnitConverter<Temperature, float>
{
    static TemperatureConverter()
    {
        BaseUnit = Temperature.Celcius;
        RegisterConversion(Temperature.Fahrenheit, v => v * 2f, v => v * 0.5f);
        RegisterConversion(Temperature.Kelvin, v => v * 10f, v => v * 0.05f);
    }
}

And then using it is pretty simple:

var converter = new TemperatureConverter();

Console.WriteLine(converter.Convert(1, Temperature.Celcius, Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1, Temperature.Fahrenheit, Temperature.Celcius));

Console.WriteLine(converter.Convert(1, Temperature.Celcius, Temperature.Kelvin));
Console.WriteLine(converter.Convert(1, Temperature.Kelvin, Temperature.Celcius));

Console.WriteLine(converter.Convert(1, Temperature.Kelvin, Temperature.Fahrenheit));
Console.WriteLine(converter.Convert(1, Temperature.Fahrenheit, Temperature.Kelvin));

这篇关于如何创建一个通用的转换器,用于测量单位在C#中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆