Context
Suppose I'm writing an app about cakes.
I need to store the weight of cakes in kg and their FCR (frosting/chocolate ratio. I made that up). I can store these values as float
. The problem I see with that is that I can assign a weight-in-kg value to an FCR field.
C#'s type system can prevent errors like this. If I create a class WeightInKg
and a class FrostingChocolateRatio
, I won't be able to assign one to the other.
Issue
I will then need to implement all numerical operators (+ - * / > < ==
etc) again. These are already annoying to implement, because they are only mere wrappers over the functionality of float
. However, as these structs are both based on float
, all those wrapper methods are virtually identical. This will again be the case for every other such float
-based type.
What I have tried / thought about
- Good old OO inheritance. An
abstract class FloatValue
can provide all the numerical operations, but these then return theFloatValue
type instead of the (type safe) subclass type. I also feel like a struct should be used for something that is inherently a bare-bones value, and structs don't support sub-classes. - Generics. I am currently using this; a
struct Quantity<T>
with all the numerical operations implemented. ForT
, I then use empty "marker classes", which only exist to identify a certain type of quantity (e.g.FrostingChocolateRatio
). This works, but constantly usingQuantity<WhatIReallyWant>
is awkward and produces more visual clutter in the code.
Question
Is this as close as I can get to what I want, or are there cleaner ways to have such type-safe values in C#?
Addition
As PaulF mentioned, FrostingChocolateRatio
is not an ideal example because the math works differently for ratios. However, I'm out of creativity for today; just assume it does exactly the same as WeightInKg
.
The point is that there are several different types of values which behave exactly like float
, but it doesn't make sense to add a centimeter to a liter.