Yes, you can create types that behave like numeric values but can't be assigned to each other. You can't derive from numeric type, but wrapping one into a struct
would be comparable efficient (if that's a concern) or you can add more info (like units). You may be even create generic type if you don't need cross-type operations.
You can see Complex
type for full set of operations and interfaces that make type behave very close to regular numbers (including plenty of conversions back and forth as needed).
Some basic class:
class Distance
{
float d;
public Distance(float d)
{
this.d = d;
}
public static Distance operator+(Distance op1, Distance op2)
{
return new Distance(op1.d + op2.d);
}
// ==, !=, Equals and GetHashCode are not required but if you
// need one (i.e. for comparison you need ==, to use values of this
// type in Dictionaries you need GetHashCode)
// you have to implement all
public static bool operator == (Distance op1, Distance op2)
{
return op1.d == op2.d;
}
public static bool operator !=(Distance op1, Distance op2)
{
return op1.d != op2.d;
}
public override bool Equals(object obj)
{
return (object)this == obj || ((obj is Distance) && (obj as Distance)==this);
}
public override int GetHashCode()
{
return d.GetHashCode();
}
// Some basic ToString so we can print it in Console/use in
// String.Format calls
public override string ToString()
{
return $"{d} M";
}
}
Which lets you add values of the same type but will fail to add any other type:
Console.WriteLine(new Distance(1) + new Distance(2)); // "3 M"
// Console.WriteLine(new Distance(1) + 2); // fails to compile
Picking between class
and struct
for such sample is mostly personal preference, for real usage make sure to know difference between value and reference type before picking one and decide what works for you (struct is likely better for numbers).
More information:
Units of measure in C# - almost - even if you don't go all the way it shows how to make generic numeric type so you can easily create many types without much code (UnitDouble<T>
in that post), Arithmetic operator overloading for a generic class in C# - discusses issues you face if you want to go other way and support varying base numeric types (like Distance<float>
and Distance<int>
).