Pattern for Creating a Simple and Efficient Value type

孤街醉人 提交于 2019-11-30 04:38:39

Is there a way to make MinValue/MaxValue const instead of readonly?

No. However, the BCL doesn't do this, either. For example, DateTime.MinValue is static readonly. Your current approach, for MinValue and MaxValue is appropriate.

As for your other two questions - usability and the pattern itself.

Personally, I would avoid the automatic conversions (implicit conversion operators) for a "temperature" type like this. A temperature is not an integer value (in fact, if you were going to do this, I would argue that it should be floating point - 93.2 degrees C is perfectly valid.) Treating a temperature as an integer, and especially treating any integer value implicitly as a temperature seems inappropriate and a potential cause of bugs.

I find that structs with implicit conversion often cause more usability problems than they address. Forcing a user to write:

 Celsius c = new Celcius(41);

Is not really much more difficult than implicitly converting from an integer. It is far more clear, however.

I think from a usability point of view I would opt for a type Temperature rather than Celsius. Celsius is just a unit of measure while a Temperature would represent an actual measurement. Then your type could support multiple units like Celsius, Fahrenheit and Kelvin. I would also opt for decimal as backing storage.

Something along these lines:

public struct Temperature
{
    private decimal m_value;

    private const decimal CelsiusToKelvinOffset = 273.15m;

    public static readonly Temperature MinValue = Temperature.FromKelvin(0);
    public static readonly Temperature MaxValue = Temperature.FromKelvin(Decimal.MaxValue);

    public decimal Celsius
    {
        get { return m_value - CelsiusToKelvinOffset; }
    }

    public decimal Kelvin 
    {
        get { return m_value; }
    }

    private Temperature(decimal temp)
    {
        if (temp < Temperature.MinValue.Kelvin)
               throw new ArgumentOutOfRangeException("temp", "Value {0} is less than Temperature.MinValue ({1})", temp, Temperature.MinValue);
        if (temp > Temperature.MaxValue.Kelvin)
               throw new ArgumentOutOfRangeException("temp", "Value {0} is greater than Temperature.MaxValue ({1})", temp, Temperature.MaxValue);
         m_value = temp;
    }

    public static Temperature FromKelvin(decimal temp)
    {     
           return new Temperature(temp);
    }

    public static Temperature FromCelsius(decimal temp)
    {
        return new Temperature(temp + CelsiusToKelvinOffset);
    }

    ....
}

I would avoid the implicit conversion as Reed states it makes things less obvious. However I would overload operators (<, >, ==, +, -, *, /) as in this case it would make sense to perform these kind of operations. And who knows, in some future version of .net we might even be able to specify operator constraints and finally be able to write more reusable data structures (imagine a statistics class which can calculate statistics for any type which supports +, -, *, /).

DebuggerDisplay is useful touch. I'd add unit of measurements "{m_value} C" so you can immediately see the type.

Depending on target usage you may also want to have generic conversion framework to/from base units in addtion to concrete classes. I.e. store values in SI units, but be able to display/edit based on culture like (degrees C, km, kg) vs. (degrees F, mi, lb).

You may also check out F# measurement units for additioanl ideas ( http://msdn.microsoft.com/en-us/library/dd233243.aspx ) - note that it is compile time construct.

I think this is a perfectly fine implementation pattern for value types. I've done similar things in the past that have worked out well.

Just one thing, since Celsius is implicitly convertible to/from int anyway, you can define the bounds like this:

public const int MinValue = -273;
public const int MaxValue = int.MaxValue;

However, in reality there's no practical difference between static readonly and const.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!