模式的创建一个简单而有效的值类型 [英] Pattern for Creating a Simple and Efficient Value type

查看:152
本文介绍了模式的创建一个简单而有效的值类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

动机:



在阅读的码味:自动属性他说接近尾声:




底线是该自动属性很少适当的。
在实际上,它们当属性的类型是一个
值为类型和所有可能的值是允许只适合




他给 INT温度作为臭味的例子,并建议最好的解决办法是单位具体的值类型一样摄氏度。所以我决定尝试写封装了所有边界检查和类型转换逻辑中被越来越的 SOLID



基本要求:




  1. 不可能有一个无效值

  2. 封装转换操作

  3. Effient应对(相当于INT其替换)

  4. 尽可能直观使用(试图为int的语义)



实施

  [System.Diagnostics.DebuggerDisplay({} m_value)] 
公共结构摄氏度//:IComparable的,IFormattable等..
{
私人诠释m_value;

公共静态只读摄氏度MINVALUE =新摄氏度(){m_value = -273}; //绝对零度
公共静态只读摄氏度的MaxValue =新摄氏度(){m_value = int.MaxValue};

私人摄氏度(INT TEMP)
{
如果(温度< Celsius.MinValue)
抛出新ArgumentOutOfRangeException(温度,值不能小于Celsius.MinValue(绝对零));如果
(温度> Celsius.MaxValue)
抛出新ArgumentOutOfRangeException(温度,值不能为更多的则Celsius.MaxValue);

m_value =温度;
}

公共静态隐运营商摄氏度(INT TEMP)
{
返回新摄氏度(温度);
}

公共静态隐运营商INT(摄氏三)
{
返回c.m_value;
}

//运营商的数字类型...

公共重写字符串的ToString()
{
返回m_value.ToString ();
}

//重载equals,hashCode时等...
}

测试:

  [TestClass中] 
公共类TestCelsius
{
[TestMethod的]
公共无效的QuickTest()
{
摄氏度C = 41;
摄氏度C2 = C;
INT TEMP = C2;
Assert.AreEqual(41,温度);
Assert.AreEqual(41,c.ToString());
}

[TestMethod的]
公共无效OutOfRangeTest()
{

{
摄氏度C = -300;
Assert.Fail(不应该是能够分配-300);
}
赶上(ArgumentOutOfRangeException)
{
//通
}
赶上(例外)
{
Assert.Fail (扔错了异常);
}
}
}



问题:




  • 有没有一种方法,使MINVALUE / ​​MaxValue的常量,而不是只读?纵观BCL我喜欢 INT 的元数据定义如何明确规定的MaxValue和MINVALUE的编译时间常数。我怎么能模仿?我不明白的方式没有任何调用构造函数或暴露的实现细节摄氏存储一个int。

  • 我失去了任何的可用性功能来创建对象摄氏度<? / STRONG>

  • 有用于创建自定义的单个字段值类型更好的模式?


解决方案

有没有一种方法,使MINVALUE / ​​MaxValue的常量,而不是只读?




没有。然而,BCL没有做到这一点,无论是。例如, DateTime.MinValue 静态只读。您当前的做法,为 MINVALUE MaxValue的是合适的。



至于你的其他两个问题 - 实用性和模式本身



就个人而言,我会避免自动转换(隐式转换运营商)的温度型状这个。温度是不是整数值(事实上,如果你的的要做到这一点,我认为应该浮点 - 93.2摄氏度是完全有效)处理的温度为整数,特别是隐式处理的任何整数值作为温度似乎不妥和错误的潜在原因。



我发现,与隐式转换结构往往会导致更多的可用性问题比解决。强制用户写的:

 摄氏度C =新摄氏度(41); 



是不是真的困难得多从一个整数隐含转换。这是更为清晰,但是。


Motivation:

In reading Mark Seemann’s blog on Code Smell: Automatic Property he says near the end:

The bottom line is that automatic properties are rarely appropriate. In fact, they are only appropriate when the type of the property is a value type and all conceivable values are allowed.

He gives int Temperature as an example of a bad smell and suggests the best fix is unit specific value type like Celsius. So I decided to try writing a custom Celsius value type that encapsulates all the bounds checking and type conversion logic as an exercise in being more SOLID.

Basic requirements:

  1. Impossible to have an invalid value
  2. Encapsulates conversion operations
  3. Effient coping (equivalent to the int its replacing)
  4. As intuitive to use as possible (trying for the semantics of an int)

Implementation:

[System.Diagnostics.DebuggerDisplay("{m_value}")]
public struct Celsius // : IComparable, IFormattable, etc...
{
    private int m_value;

    public static readonly Celsius MinValue = new Celsius() { m_value = -273 };           // absolute zero
    public static readonly Celsius MaxValue = new Celsius() { m_value = int.MaxValue };

    private Celsius(int temp)
    {
        if (temp < Celsius.MinValue)
            throw new ArgumentOutOfRangeException("temp", "Value cannot be less then Celsius.MinValue (absolute zero)");
        if (temp > Celsius.MaxValue)
            throw new ArgumentOutOfRangeException("temp", "Value cannot be more then Celsius.MaxValue");

        m_value = temp;
    }

    public static implicit operator Celsius(int temp)
    {
        return new Celsius(temp);
    }

    public static implicit operator int(Celsius c)
    {
        return c.m_value;
    }

    // operators for other numeric types...

    public override string ToString()
    {
        return m_value.ToString();
    }

    // override Equals, HashCode, etc...
}

Tests:

[TestClass]
public class TestCelsius
{
    [TestMethod]
    public void QuickTest()
    {
        Celsius c = 41;             
        Celsius c2 = c;
        int temp = c2;              
        Assert.AreEqual(41, temp);
        Assert.AreEqual("41", c.ToString());
    }

    [TestMethod]
    public void OutOfRangeTest()
    {
        try
        {
            Celsius c = -300;
            Assert.Fail("Should not be able to assign -300");
        }
        catch (ArgumentOutOfRangeException)
        {
            // pass
        }
        catch (Exception)
        {
            Assert.Fail("Threw wrong exception");
        }
    }
}

Questions:

  • Is there a way to make MinValue/MaxValue const instead of readonly? Looking at the BCL I like how the meta data definition of int clearly states MaxValue and MinValue as compile time constants. How can I mimic that? I don’t see a way to create a Celsius object without either calling the constructor or exposing the implementation detail that Celsius stores an int.
  • Am I missing any usability features?
  • Is there a better pattern for creating a custom single field value type?

解决方案

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.

这篇关于模式的创建一个简单而有效的值类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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