C# DataMeasurement object for comparing, converting, and adding or substracting data sizes
I created a C# DataMeasurement object a while ago but recently really started to use it. I wrote unit tests for it and fixed comparison and add/substract of nulls and wrote Unit tests for it.
The intent of the object is to be able to use any Data size and do the following:
- Convert it to any other data size type, 1 GB converts to 1024 MB.
- Compare it to any other data size type, 1 GB = 1024 MB.
- Add any other data size type and have it add correctly, 1 GB + 1024 MB = 2GB.
So you can easily compare things like 12343 bytes.
You can create the object in a lot of different ways as I have multiple constructors, and it is easy to add your own constructor.
Here is the object:
using System; using System.Collections.Generic; using System.Text; namespace LANDesk.Install.Common { public class DataMeasurement { #region Member Variables // This is the size in the current type. For example, 1 GB = 1.0. But // if you convert the Type to Megabyte, the value should change to 1024.0; protected Double _DataSize; protected DataType _DataType; #endregion #region Constructors /// <summary> /// The default constructor. This initializes and object with a /// DataSize of 0 and a DataType of Byte. /// </summary> public DataMeasurement() { _DataSize = 0; _DataType = DataType.Byte; } /// <summary> /// This constructor takes a value and assumes the data measurement /// type is in bytes. /// </summary> /// <param name="inValue">The measurement value as a ulong.</param> public DataMeasurement(UInt64 inSizeInBytes) { _DataType = DataType.Byte; _DataSize = inSizeInBytes * 8; } /// <summary> /// This constructor takes a value and a data measurement type. /// </summary> /// <param name="inValue">The measurement value as a ulong.</param> /// <param name="inType">The measurement type.</param> public DataMeasurement(UInt64 inValue, DataType inType) { _DataType = inType; _DataSize = inValue; } /// <summary> /// This constructor takes a double value and defaults to megabyte. /// </summary> /// <param name="inValue"></param> public DataMeasurement(double inValue) { _DataType = DataType.Megabyte; _DataSize = inValue; } /// <summary> /// This constructor takes a value and a data measurement type. /// </summary> /// <param name="inValue">The measurement value as a double.</param> /// <param name="inType">The measurement type.</param> public DataMeasurement(double inValue, DataType inType) { _DataType = inType; _DataSize = inValue; } /// <summary> /// This constructor takes a value and a data measurement type. /// </summary> /// <param name="inValue">The measurement value as a double.</param> /// <param name="inType">The measurement type as a string, "GB", "Gigabyte".</param> public DataMeasurement(UInt64 inValue, String inType) { _DataSize = inValue; GetDataTypeFromString(inType); } /// <summary> /// This constructor takes a string representation of a data measurement. /// </summary> /// <param name="inValueAndType">The measurement value and type in a /// single string such as "49 GB" or "49 GBSI" or "49 Gigabyte". There /// must be a space as it is used to split the value from the type, so /// "49GB" is invalid because there is no space delimeter.</param> public DataMeasurement(String inValueAndType) { char[] split = { ' ' }; String[] data = inValueAndType.Split(split); _DataSize = Convert.ToUInt64(data[0]); GetDataTypeFromString(data[1]); } #endregion #region Properties public Double Size { get { return _DataSize; } set { _DataSize = value; } } public DataType DataSizeType { get { return _DataType; } set { _DataType = value; } } public ShortName DataSizeTypeShortName { get { return (ShortName)_DataType; } set { _DataType = (DataType)value; } } public ulong Bit { get { return (ulong)ConvertToType(DataType.Bit, false); } set { _DataType = DataType.Bit; _DataSize = value; } } public Double Byte { get { return ConvertToType(DataType.Byte, false); } set { _DataType = DataType.Byte; _DataSize = value; } } public Double KilobitSI { get { return ConvertToType(DataType.KilobitSI, false); } set { _DataType = DataType.KilobitSI; _DataSize = value; } } public Double Kilobit { get { return ConvertToType(DataType.Kilobit, false); } set { _DataType = DataType.Kilobit; _DataSize = value; } } public Double KilobyteSI { get { return ConvertToType(DataType.KilobyteSI, false); } set { _DataType = DataType.KilobyteSI; _DataSize = value; } } public Double Kilobyte { get { return ConvertToType(DataType.Kilobyte, false); } set { _DataType = DataType.Kilobit; _DataSize = value; } } public Double MegabitSI { get { return ConvertToType(DataType.MegabitSI, false); } set { _DataType = DataType.MegabitSI; _DataSize = value; } } public Double Megabit { get { return ConvertToType(DataType.Megabit, false); } set { _DataType = DataType.Megabit; _DataSize = value; } } public Double MegabyteSI { get { return ConvertToType(DataType.MegabyteSI, false); } set { _DataType = DataType.MegabyteSI; _DataSize = value; } } public Double Megabyte { get { return ConvertToType(DataType.Megabyte, false); } set { _DataType = DataType.Megabyte; _DataSize = value; } } public Double GigabitSI { get { return ConvertToType(DataType.GigabitSI, false); } set { _DataType = DataType.GigabitSI; _DataSize = value; } } public Double Gigabit { get { return ConvertToType(DataType.Gigabit, false); } set { _DataType = DataType.Gigabit; _DataSize = value; } } public Double GigabyteSI { get { return ConvertToType(DataType.GigabyteSI, false); } set { _DataType = DataType.GigabyteSI; _DataSize = value; } } public Double Gigabyte { get { return ConvertToType(DataType.Gigabyte, false); } set { _DataType = DataType.Gigabyte; _DataSize = value; } } public Double TerabitSI { get { return ConvertToType(DataType.TerabitSI, false); } set { _DataType = DataType.TerabitSI; _DataSize = value; } } public Double Terabit { get { return ConvertToType(DataType.Terabit, false); } set { _DataType = DataType.Terabit; _DataSize = value; } } public Double TerabyteSI { get { return ConvertToType(DataType.TerabyteSI, false); } set { _DataType = DataType.TerabyteSI; _DataSize = value; } } public Double Terabyte { get { return ConvertToType(DataType.Terabyte, false); } set { _DataType = DataType.Terabyte; _DataSize = value; } } public Double PetabitSI { get { return ConvertToType(DataType.PetabitSI, false); } set { _DataType = DataType.PetabitSI; _DataSize = value; } } public Double Petabit { get { return ConvertToType(DataType.Petabit, false); } set { _DataType = DataType.Petabit; _DataSize = value; } } public Double PetabyteSI { get { return ConvertToType(DataType.PetabyteSI, false); } set { _DataType = DataType.PetabyteSI; _DataSize = value; } } public Double Petabyte { get { return ConvertToType(DataType.Petabyte, false); } set { _DataType = DataType.Petabyte; _DataSize = value; } } public Double ExabitSI { get { return ConvertToType(DataType.ExabitSI, false); } set { _DataType = DataType.ExabitSI; _DataSize = value; } } public Double Exabit { get { return ConvertToType(DataType.Exabit, false); } set { _DataType = DataType.Exabit; _DataSize = value; } } public Double ExabyteSI { get { return ConvertToType(DataType.ExabyteSI, false); } set { _DataType = DataType.ExabyteSI; _DataSize = value; } } public Double Exabyte { get { return ConvertToType(DataType.Exabyte, false); } set { _DataType = DataType.Exabyte; _DataSize = value; } } #endregion #region Functions /// <summary> /// Writes the DataMeasurement to string, such as "1 GB" or "1024 KB". /// It always uses the DataSize and DataType values. /// </summary> override public string ToString() { return String.Format("{0} {1}", _DataSize, ((ShortName)_DataType).ToString()); } /// <summary> /// Writes the DataMeasurement to string, such as "1 GB" or "1024 KB". /// However, it does so in the DataType that you specify. /// </summary> /// <param name="inDataType">The type to output to string. For example /// If the measurement is "4 Gigabyte" but you pass in Megabyte, it /// will output "4096 MB".</param> /// <returns></returns> public string ToString(DataType inDataType) { Double size = ConvertToType(inDataType, false); return String.Format("{0} {1}", size, ((ShortName)inDataType).ToString()); } public override int GetHashCode() { return base.GetHashCode(); } public override bool Equals(object obj) { if (obj == null) return false; DataMeasurement right = obj as DataMeasurement; if ((object)right == null) return false; return this == right; } private void GetDataTypeFromString(String inType) { if (inType.Length == 2 || inType.Length == 4) { // Short name was used so get the ShortName and case it to a DataType // KB and Kb are not the same, so case is Important _DataType = (DataType)Enum.Parse(typeof(ShortName), inType, false); } else { // Long name was used so get // We can ignore case because Kilobit and Kilobyte are different. _DataType = (DataType)Enum.Parse(typeof(DataType), inType, true); } } /// <summary> /// This function converts the stored measurement from its current /// value to a new value using the new DataType. /// </summary> /// <param name="inNewType">The new DataType. For example If the /// measurement is "4 Gigabyte" but you pass in Megabyte, it will /// change the value to "4096 Megabyte".</param> /// <returns>The new DataSize for the new type is returned.</returns> public Double ConvertToType(DataType inNewType) { return ConvertToType(inNewType, true); } /// <summary> /// This function converts the stored measurement from its current /// value to a new value using the new DataType. /// </summary> /// <param name="inNewType">The new DataType.. For example If the /// measurement is "4 Gigabyte" but you pass in Megabyte, it will /// change the value to "4096 Megabyte".</param> /// <param name="inChangeObjectMeasurementType">A bool value that /// specifies whether to change the whole object.</param> /// <returns>The new DataSize for the new type is returned.</returns> public Double ConvertToType(DataType inNewType, bool inChangeObjectMeasurementType) { double ret = 0; bool isSet = false; if (inNewType == _DataType) { ret = _DataSize; isSet = true; } else if (inNewType == DataType.Bit) { ret = (ulong)_DataType * _DataSize * 8; isSet = true; } else if (_DataType == DataType.Bit) { ret = _DataSize / 8 / (ulong)inNewType; isSet = true; } if (!isSet) ret = (ulong)_DataType * _DataSize / (ulong)inNewType; if (inChangeObjectMeasurementType) { _DataType = inNewType; Size = ret; } return ret; } #endregion #region Operator Override Functions public static bool operator ==(DataMeasurement left, DataMeasurement right) { if (null == (object)left && null == (object)right) return true; if (null == (object)right || null == (object)left) return false; if (left.DataSizeType == right.DataSizeType) { return left.Size == right.Size; } else { return left.Byte == right.Byte; } } public static bool operator !=(DataMeasurement left, DataMeasurement right) { if (null == (object)left && null == (object)right) return false; if (null == (object)right || null == (object)left) return true; if (left.DataSizeType == right.DataSizeType) { return left.Size != right.Size; } else { return left.Byte != right.Byte; } } public static bool operator >(DataMeasurement left, DataMeasurement right) { if (null == (object)left && null == (object)right) return false; if (null == (object)right) return true; if (null == (object)left) return false; if (left.DataSizeType == right.DataSizeType) { return left.Size > right.Size; } else { return left.Byte > right.Byte; } } public static bool operator >=(DataMeasurement left, DataMeasurement right) { if (left.DataSizeType == right.DataSizeType) { return left.Size >= right.Size; } else { return left.Byte >= right.Byte; } } public static bool operator <(DataMeasurement left, DataMeasurement right) { if (null == (object)left && null == (object)right) return false; if (null == (object)right) return false; if (null == (object)left) return true; if (left.DataSizeType == right.DataSizeType) { return left.Size < right.Size; } else { return left.Byte < right.Byte; } } public static bool operator <=(DataMeasurement left, DataMeasurement right) { if (left.DataSizeType == right.DataSizeType) { return left.Size <= right.Size; } else { return left.Byte <= right.Byte; } } public static DataMeasurement operator +(DataMeasurement left, DataMeasurement right) { return left + right.ConvertToType(left.DataSizeType, false); } public static DataMeasurement operator +(DataMeasurement left, double right) { if ((null == (object)left && null == (object)right) || null == (object)right) return left; if (null == (object)left) return new DataMeasurement(right); return new DataMeasurement(left.Size + right, left.DataSizeType); } public static DataMeasurement operator -(DataMeasurement left, DataMeasurement right) { return left - right.ConvertToType(left.DataSizeType, false); } public static DataMeasurement operator -(DataMeasurement left, double right) { if ((null == (object)left && null == (object)right) || null == (object)right) return left; if (null == (object)left) return new DataMeasurement(right); return new DataMeasurement(left.Size - right, left.DataSizeType); } #endregion #region Enums public enum ShortName : ulong { b = 0, // Bit must be handled special // Everything after is in bytes B = 1, KbSI = 125, Kb = 128, KBSI = 1000, KB = 1024, MbSI = 125000, Mb = 131072, MBSI = 1000000, MB = 1048576, GbSI = 125000000, Gb = 134217728, GBSI = 1000000000, GB = 1073741824, TbSI = 125000000000, Tb = 137438953472, TBSI = 1000000000000, TB = 1099511627776, PbSI = 125000000000000, Pb = 140737488355328, PBSI = 1000000000000000, PB = 1125899906842624, EbSI = 125000000000000000, Eb = 144115188075855872, EBSI = 1000000000000000000, EB = 1152921504606846976 } public enum DataType : ulong { Bit = 0, // Bit must be handled special // Everything after is in bytes Byte = 1, KilobitSI = 125, Kilobit = 128, KilobyteSI = 1000, Kilobyte = 1024, MegabitSI = 125000, Megabit = 131072, MegabyteSI = 1000000, Megabyte = 1048576, GigabitSI = 125000000, Gigabit = 134217728, GigabyteSI = 1000000000, Gigabyte = 1073741824, TerabitSI = 125000000000, Terabit = 137438953472, TerabyteSI = 1000000000000, Terabyte = 1099511627776, PetabitSI = 125000000000000, Petabit = 140737488355328, PetabyteSI = 1000000000000000, Petabyte = 1125899906842624, ExabitSI = 125000000000000000, Exabit = 144115188075855872, ExabyteSI = 1000000000000000000, Exabyte = 1152921504606846976 } #endregion } }
I could probably do a better job of testing this. Here is a test class that succeeds. If you want more testing add it.
Hopefully this helps you out.
using LANDesk.Install.Common; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; namespace LANDesk.Install.Common.Tests { /// <summary> ///This is a test class for DataMeasurementTest and is intended ///to contain all DataMeasurementTest Unit Tests ///</summary> [TestClass()] public class DataMeasurementTest { List<DataMeasurement> sizes = new List<DataMeasurement>(); [TestMethod()] public void DataMeasurementTestAdditionAndGreaterThanLessThan() { DataMeasurementEqualityTest(); DataMeasurement OneGB = new DataMeasurement("1 GB"); DataMeasurement OneGBinMB = new DataMeasurement("1024 MB"); DataMeasurement twoGB = new DataMeasurement(2, DataMeasurement.DataType.Gigabyte); DataMeasurement twoGBadded = OneGB + OneGBinMB; TestEqualValues(twoGB, twoGBadded); foreach (DataMeasurement dm in sizes) { TestLeftIsGreater(twoGB, dm); TestRightIsGreater(dm, twoGB); } } [TestMethod()] public void DataMeasurementTestSubtractionAndGreaterThanLessThan() { DataMeasurementEqualityTest(); DataMeasurement tenGB = new DataMeasurement(10, DataMeasurement.DataType.Gigabyte); DataMeasurement NineGB = new DataMeasurement(9, DataMeasurement.DataType.Gigabyte); DataMeasurement NineGBbySubtraction = tenGB - sizes[0]; TestEqualValues(NineGB, NineGBbySubtraction); TestLeftIsGreater(tenGB, NineGB); TestLeftIsGreater(tenGB, NineGBbySubtraction); TestRightIsGreater(NineGB, tenGB); TestRightIsGreater(NineGBbySubtraction, tenGB); } [TestMethod()] public void TestConversion() { DataMeasurement dm = new DataMeasurement("4 GB"); dm.ConvertToType(DataMeasurement.DataType.Megabyte); Assert.IsTrue(dm.Size == (double)4096); Assert.IsTrue(dm.ToString() == "4096 MB"); } [TestMethod()] public void DataMeasurementOtherTest() { sizes = new List<DataMeasurement>(); int i = -1; // Test 1 - Check that conversion is working i++; sizes.Add(new DataMeasurement("1024 bit")); Assert.IsTrue(sizes[i].Bit == 1024, "Test 1, to Bit."); Assert.IsTrue(sizes[i].Byte == 1024 / 8, "Test 1, to Byte."); double expected = 1024 / 8 / 1024.0; Assert.IsTrue(sizes[i].Kilobyte == expected, "Test 1, " + sizes[i].Kilobyte + " should equal " + expected); // Test 2 - Check that conversion is working i++; sizes.Add(new DataMeasurement(4, DataMeasurement.DataType.Gigabyte)); Assert.IsTrue(sizes[i].Bit == 34359738368, "Test 2, to bit."); Assert.IsTrue(sizes[i].Byte == 4294967296, "Test 2, to Byte."); Assert.IsTrue(sizes[i].Kilobyte == 4 * 1024 * 1024, "Test 2, to Kilobyte."); expected = 4.0 / 1024; Assert.IsTrue(sizes[i].Terabyte == expected, "Test 2, " + sizes[i].Terabyte + " should equal " + expected); // Test 3 - Comparisons Assert.IsTrue(sizes[i - 1] < sizes[i], "Test 3, " + sizes[i - 1] + " is less than " + sizes[i]); Assert.IsTrue(sizes[i - 1] <= sizes[i], "Test 3, " + sizes[i - 1] + " is less than or equal to " + sizes[i]); Assert.IsFalse(sizes[i - 1] > sizes[i], "Test 3, " + sizes[i - 1] + " is greater than " + sizes[i]); Assert.IsFalse(sizes[i - 1] >= sizes[i], "Test 3, " + sizes[i - 1] + " is greater than or equal to " + sizes[i]); Assert.IsFalse(sizes[i - 1] == sizes[i], "Test 3, " + sizes[i - 1] + " is equal to " + sizes[i]); Assert.IsTrue(sizes[i - 1] != sizes[i], "Test 3, " + sizes[i - 1] + " is not equal to " + sizes[i]); } public void DataMeasurementEqualityTest() { DataMeasurement last = null; DataMeasurement OneGB = new DataMeasurement("1 GB"); foreach (DataMeasurement.DataType dt in Enum.GetValues(typeof(DataMeasurement.DataType))) { DataMeasurement next = new DataMeasurement(OneGB.ConvertToType(dt, false), dt); sizes.Add(next); if (last != null) TestEqualValues(next, last); last = next; } } private void TestEqualValues(DataMeasurement left, DataMeasurement right) { Assert.IsFalse(left > right); Assert.IsFalse(left < right); Assert.IsTrue(left >= right); Assert.IsTrue(left <= right); Assert.IsTrue(left == right); Assert.IsFalse(left != right); Assert.IsTrue(left.Equals(right)); } private void TestLeftIsGreater(DataMeasurement left, DataMeasurement right) { Assert.IsTrue(left > right); Assert.IsFalse(left < right); Assert.IsTrue(left >= right); Assert.IsFalse(left <= right); Assert.IsFalse(left == right); Assert.IsTrue(left != right); } private void TestRightIsGreater(DataMeasurement left, DataMeasurement right) { Assert.IsFalse(left > right); Assert.IsTrue(left < right); Assert.IsFalse(left >= right); Assert.IsTrue(left <= right); Assert.IsFalse(left == right); Assert.IsTrue(left != right); } } }
I hope this object helps save you time.