当前位置: 首页 > 编程语言 > C#

C#LearningTutorial-PatternsforCreatingSimpleandEfficientValueTypesSharing

时间:2023-04-10 18:54:16 C#

PatternsforCreatingSimpleandEfficientValueTypes动机:在阅读MarkSeemann关于CodeSmell:AutomaticProperty的博客时,他在接近尾声时说:底线是自动的属性很少起作用。事实上,只有当属性的类型是值类型并且允许所有可能的值时它们才适用。他以intTemperature作为难闻气味的示例,并建议最好的解决方法是像Celsius这样的单位特定值类型。所以我决定尝试编写一个自定义Celsius值类型,将所有边界检查和类型转换逻辑封装为更可靠的练习。基本要求:不可能有无效值封装转换操作的有效响应(相当于用int替换它)尽可能直观地使用(尝试int的语义)实现:[System.Diagnostics.DebuggerDisplay("{m_value}")]publicstructCelsius//:IComparable,IFormattable,etc...{privateintm_value;publicstaticreadonlyCelsiusMinValue=newCelsius(){m_value=-273};//绝对零publicstaticreadonlyCelsiusMaxValue=newCelsius(){m_value=int.MaxValue};privateCelsius(inttemp){if(tempCelsius.MaxValue)thrownewArgumentOutOfRangeException("temp","ValuecannotbemorethenCelsius.MaxValue");m_value=温度;}publicstaticimplicitoperatorCelsius(inttemp){returnnewCelsius(temp);}publicstaticimplicitoperatorint(Celsiusc){returnc.m_value;}//其他数字类型的运算符...publicoverridestringToString(){returnm_value.ToString();}//覆盖Equals、HashCode等...}测试:[TestClass]publicclassTestCelsius{[TestMethod]publicvoidQuickTest(){Celsiusc=41;摄氏度c2=c;inttemp=c2;Assert.AreEqual(41,temp);Assert.AreEqual("41",c.ToString());}[TestMethod]publicvoidOutOfRangeTest(){try{Celsiusc=-300;断言。Fail("不应该赋值-300");}catch(ArgumentOutOfRangeException){//通过}catch(Exception){Assert.Fail("Threwwrongexception");}}}问题:有没有办法让MinValue/MaxValueconst而不是只读?不仅如此,BCL也没有这么做。例如,DateTime.MinValue是静态只读的。您当前的方法适用于MinValue和MaxValue。至于你的其他两个问题——可用性和架构本身。就个人而言,我会避免像这样的“温度”类型的自动转换(隐式转换运算符)。温度不是整数值(事实上,如果你要这样做,我认为它应该是浮点数——93.2摄氏度是完全有效的。)将温度视为整数,尤其是将任何整数值隐式表示温度似乎不合适并且是错误的潜在原因。我发现具有隐式转换的结构通常会导致比它们解决的更多的可用性问题。强制用户写:Celsiusc=newCelsius(41);实际上并不比整数的隐式转换难多少。但是,它更清楚。我认为从可用性的角度来看,我会选择温度而不是摄氏温度。摄氏度只是一个测量单位,而温度代表实际测量值。然后你的类型可以支持多个单位,如摄氏度、华氏度和开尔文。我也会选择decimal作为后备存储。关于这些:publicstructTemperature{privatedecimalm_value;私有const十进制CelsiusToKelvinOffset=273.15m;publicstaticreadonlyTemperatureMinValue=Temperature.FromKelvin(0);publicstaticreadonlyTemperatureMaxValue=Temperature.FromKelvin(Decimal.decimalCelsius{get{returnm_value-CelsiusToKelvinOffset;}}publicdecimalKelvin{get{returnm_value;}}privateTemperature(decimaltemp){if(tempTemperature.MaxValue.Kelvin)}thrownewArgumentOutOfRangeException("temp","Value{0}大于Temperature.MaxValue({1})",temp,Temperature.MaxValue);m_value=temp;}publicstaticTemperatureFromKelvin(decimaltemp){returnnewTemperature(temp);}publicstaticTemperatureFromCelsius(decimaltemp){returnnewTemperature(temp+CelsiusToKelvinOffset);}....}我会避免隐式转换,因为Reed说它让事情变得不那么明显。但我会重载运算符(,==,+,-,*,/)因为在这种情况下这样做是有意义的。谁知道呢,在.net的某些未来版本中,我们甚至能够指定运算符约束,最终能够编写更多可重用的数据结构(想象一个stat类,它可以计算任何支持+、-、*统计数据的类型,/).DebuggerDisplay很有用。我将添加度量单位“{m_value}C”,以便您可以立即看到类型。根据目标用例,您可能还想将基本单元的通用转换框架添加到具体类中。即以SI单位存储值,但能够根据(摄氏度、公里、千克)与(华氏度、英里、磅)等文化进行显示/编辑。您还可以查看F#度量单位以了解其他想法(http://msdn.microsoft.com/en-us/library/dd233243.aspx)-请注意,它是一个编译时构造。我认为这是一个非常好的值类型实现模式。我过去做过类似的事情并且效果很好。只有一件事,因为Celsius无论如何都可以隐式转换为int,你可以这样定义边界:publicconstintMinValue=-273;publicconstintMaxValue=int.MaxValue;然而,实际上staticreadonly和const之间并没有真正的区别。以上就是《C#学习教程:创建简单高效值类型的模式》的全部内容。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: