willem
willem

Reputation: 2717

How to enforce that a field/property represents a certain type of physical quantity?

In certain types of code (e.g. simulation/ games/ optimization), many physical quantities/properties are stored, manipulated, and exposed for objects (position, velocity, distance, area, volume, time, time-differences). In these types of code, all these quantities are typically represented by floating point numbers, i.e. doubles in c#.

This allows you to add times to distances, area's to volumes, times to times, and positions to positions.

But you never want to do this. You will only want to add time-differences to times, distances to other distances or positions, and multiply areas with distances. Still, errors are easily made, especially when working on code by someone else.

There are many options to mitigate this danger. You could define custom classes representing various physical quantities, and subsequently forbidding certain types of interactions programmatically. Or you adhere to strict naming conventions (e.g. delay_timedifference). Which of those two options is best under what (if any) circumstances? How to implement the first option? How to do the naming for the second option?

Upvotes: 2

Views: 178

Answers (3)

jleahy
jleahy

Reputation: 16855

Both of the two options you've described are workable, but they have downsides. The first option results in too many classes (and then you need to define a lot of relationships) and the second one can be error prone.

A better option could be to create one custom class which both encapsulates a floating point number as well as a list of units and their multiplicities. For example 1 meter per second may become {n=1, types={length:1, time:-1}}. You can then enforce that no numbers may be compared, added or subtracted unless their units are the same. Multiplication and division would add the multiplicities of matching types together.

eg.

1 m/s * 4 seconds = 4 meters
{n=1, types={length:1, time:-1}} * {n=4, types={time:1}} = {n=4, types={length:1,time:0}}

Upvotes: 1

wizzardmr42
wizzardmr42

Reputation: 1644

I think it would make most sense to implement them as structures, not classes as they are value types. You'd want to do operator overloading to ensure that appropriate operators are available against other structures of the same type and appropriate different types. You probably also want to overload the ToString function to output the scalar value, possibly with units.

You can also create generic structures for creating vectors of other structures - these can be 2 dimensional (x and y properties), 3 dimensional (x,y and z properties) or N dimensional (an array of values). These structures can also have appropriate operator overloading to perform vector calculations. If you do it correctly, it will be a very natural way of interacting with the data.

Upvotes: 2

Reed Copsey
Reed Copsey

Reputation: 564373

Often, the second option is used, but more for convenience. Naming becomes a matter of making it obvious what you're dealing with, ie: naming properties Area and Volume, but even then, there is a unit issue.

You can handle the first case by making an entire set of custom classes (or immutable struct types) to handle each of the different combinations, which can work well, but can make the code quite a bit more cumbersome to work with. The DateTime struct in the framework is a good example - at its core, it's basically just a long integer value, but it's wrapped into a struct to prevent its usage as anything but a moment in time.

Note that other languages have options for this - for example, F# allows Units to be handled at a language level, but this isn't supported in any way in C#.

Upvotes: 2

Related Questions