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 FloatValuecan provide all the numerical operations, but these then return theFloatValuetype 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.