fork download
  1. using System;
  2. public class Test
  3. {
  4. // This example showcase the following characteristics:
  5. // (1) Choice of UoM (Unit of Measure) as Property get/set
  6. // (2) Named constructor for choice of UoM
  7. // (3) ToString support for UoM
  8. // (4) Roundtrip conversion (negligible rounding loss permitted
  9. // by double precision)
  10. // (5) Operator overloading (but lacking dimensional type check)
  11. //
  12. // Keep in mind this is just prototype code.
  13.  
  14. public static void Main()
  15. {
  16. Length len = Length.FromFeet(8.0);
  17. Console.WriteLine("feets: " + len.Feet.ToString("F15"));
  18. Console.WriteLine("meters: " + len.Meters);
  19. Console.WriteLine("tostring: " + len);
  20. Console.WriteLine("tostring(ft): " + len.ToString("ft"));
  21.  
  22. Length lenRT = Length.FromMeters(Length.FromFeet(Length.FromMeters(len.Meters).Feet).Meters);
  23. Console.WriteLine("roundtrip (m): " + lenRT.ToString("F15"));
  24. Console.WriteLine("roundtrip (ft): " + lenRT.ToString("ftF15"));
  25. Console.WriteLine("roundtrip (ft): " + lenRT.Feet.ToString("F15"));
  26.  
  27. Dimensioned sum1 = DimensionProduct.FromSum(len, lenRT);
  28. Console.WriteLine("sum1: " + sum1);
  29.  
  30. Dimensioned prod1 = DimensionProduct.FromProduct(len, lenRT);
  31. Dimensioned prod2 = len * lenRT;
  32. Console.WriteLine("product1: " + prod1);
  33. Console.WriteLine("product2: " + prod2);
  34. }
  35.  
  36. public interface Dimensioned
  37. {
  38. int[] DimensionIndicator { get; }
  39. double Value { get; }
  40. }
  41.  
  42. public class DimensionProduct : Dimensioned
  43. {
  44. private int[] myIndicator = new int[] {0, 0, 0, 0, 0, 0, 0};
  45. private double myValue = 0.0;
  46. public int[] DimensionIndicator
  47. {
  48. get { return (int[]) myIndicator.Clone(); }
  49. }
  50. public double Value
  51. {
  52. get { return myValue; }
  53. }
  54. private DimensionProduct(int[] indicator, double value)
  55. {
  56. myIndicator = (int[])indicator.Clone();
  57. myValue = value;
  58. }
  59. public static DimensionProduct FromProduct(Dimensioned a, Dimensioned b)
  60. {
  61. return new DimensionProduct(
  62. _ArrayAdd(a.DimensionIndicator, b.DimensionIndicator),
  63. a.Value * b.Value);
  64. }
  65. public static DimensionProduct FromSum(Dimensioned a, Dimensioned b)
  66. {
  67. if (!_ArraySame(a.DimensionIndicator, b.DimensionIndicator))
  68. {
  69. throw new ArgumentException("Incompatible dimensions", "b");
  70. }
  71. return new DimensionProduct(a.DimensionIndicator,
  72. a.Value + b.Value);
  73. }
  74. private static int[] _ArrayAdd(int[] a, int[] b)
  75. {
  76. int len = Math.Min(a.Length, b.Length);
  77. int[] result = new int[len];
  78. for (int k = 0; k < len; ++k)
  79. {
  80. result[k] = a[k] + b[k];
  81. }
  82. return result;
  83. }
  84. private static bool _ArraySame(int[] a, int[] b)
  85. {
  86. int len = Math.Min(a.Length, b.Length);
  87. for (int k = 0; k < len; ++k)
  88. {
  89. if (a[k] != b[k]) return false;
  90. }
  91. return true;
  92. }
  93. public override string ToString()
  94. {
  95. // This is a diagnostic implementation only;
  96. // we won't know how to properly format the string
  97. // for a generic dimension vector.
  98. return myValue.ToString("F15") + " " + _ArrayStr(myIndicator);
  99. }
  100. private static string _ArrayStr(int[] arr)
  101. {
  102. return "{" + string.Join(",", Array.ConvertAll(arr, x => x.ToString())) + "}";
  103. }
  104. }
  105.  
  106. public static class Constants
  107. {
  108. // note: scaling factors need to be as precise as permitted by
  109. // double-precision, to preserve precision in round-trip conversions.
  110. public static double FeetToMeter = 0.3048000000; // exact
  111. public static double MeterToFeet = 1.0 / 0.3048000000; // nearly exact
  112. }
  113.  
  114. public class Length : Dimensioned
  115. {
  116. public int[] DimensionIndicator
  117. {
  118. get { return new int[] { 0, 1, 0, 0, 0, 0, 0 }; }
  119. }
  120. public double Value
  121. {
  122. get { return myValue; }
  123. }
  124.  
  125. private double myValue;
  126. public double Meters
  127. {
  128. get { return myValue; }
  129. set { myValue = value; }
  130. }
  131. public double Feet
  132. {
  133. get { return myValue * Constants.MeterToFeet; }
  134. set { myValue = value * Constants.FeetToMeter; }
  135. }
  136. public Length() {} // default zero
  137. private Length(double initValue) // made private to avoid ambiguity
  138. {
  139. myValue = initValue;
  140. }
  141. // unambiguous "named constructors"
  142. public static Length FromMeters(double meter)
  143. {
  144. return new Length(meter);
  145. }
  146. public static Length FromFeet(double feet)
  147. {
  148. Length len = new Length();
  149. len.Feet = feet;
  150. return len;
  151. }
  152. public override string ToString()
  153. {
  154. return ToString("");
  155. }
  156. public string ToString(string fmt)
  157. {
  158. bool useFeet = (fmt != null && fmt.StartsWith(
  159. "ft", StringComparison.InvariantCultureIgnoreCase));
  160. if (useFeet)
  161. {
  162. return Feet.ToString(fmt.Substring(2)) + " feet";
  163. }
  164. else
  165. {
  166. return Meters.ToString(fmt) + " meters";
  167. }
  168. }
  169.  
  170. // Try both. The choice depends on whether one has enough time
  171. // to create customized classes for each derived physical quantity
  172. // (such as the Area class)
  173. #if false
  174. public static DimensionProduct operator * (Length a, Length b)
  175. {
  176. return DimensionProduct.FromProduct(a, b);
  177. }
  178. #else
  179. public static Area operator * (Length a, Length b)
  180. {
  181. return Area.FromSquareMeters(a.Meters * b.Meters);
  182. }
  183. #endif
  184. }
  185.  
  186. public class Area : Dimensioned
  187. {
  188. public int[] DimensionIndicator
  189. {
  190. get { return new int[] { 0, 2, 0, 0, 0, 0, 0 }; }
  191. }
  192. public double Value
  193. {
  194. get { return myValue; }
  195. }
  196. private double myValue;
  197. public double SquareMeters
  198. {
  199. get { return myValue; }
  200. set { myValue = value; }
  201. }
  202. public double SquareFeet
  203. {
  204. get { return myValue * Math.Pow(Constants.MeterToFeet, 2); }
  205. set { myValue = value * Math.Pow(Constants.FeetToMeter, 2); }
  206. }
  207. public static Area FromSquareMeters(double sqm)
  208. {
  209. Area obj = new Area();
  210. obj.SquareMeters = sqm;
  211. return obj;
  212. }
  213. public static Area FromSquareFeet(double sqft)
  214. {
  215. Area obj = new Area();
  216. obj.SquareFeet = sqft;
  217. return obj;
  218. }
  219. public override string ToString() // for brevity I did not customize this
  220. {
  221. return myValue.ToString("F15") + " square meters";
  222. }
  223. }
  224. }
Success #stdin #stdout 0.06s 33984KB
stdin
stdout
feets: 8.000000000000000
meters: 2.4384
tostring: 2.4384 meters
tostring(ft): 8 feet
roundtrip (m): 2.438400000000000 meters
roundtrip (ft): 8.000000000000000 feet
roundtrip (ft): 8.000000000000000
sum1: 4.876800000000000 {0,1,0,0,0,0,0}
product1: 5.945794560000000 {0,2,0,0,0,0,0}
product2: 5.945794560000000 square meters