Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending the .NET type system so the compiler enforces semantic meaning of primitive values in certain cases

I'm working developing a system right now that deals with lots of conversions between semantically different values that have the same primitive .NET type (double/string/int). This means that it's possible to get confused about which 'semantic type' you are using, either by not converting or converting too many times. Ideally I'd like the compiler to issue a warning/error if I try to use a value where it doesn't semantically make sense.

Some examples to indicate what I'm referring to:

  • Angles may be in units of degrees or radians, yet both are represented by double.
  • Vector positions may be in local/global coordinates, yet both are represented by a Vector3D struct.
  • Imagine a SQL library that accepts various query parameters as strings. It'd be good to have a way of enforcing that only clean strings were allowed to be passed in at runtime, and the only way to get a clean string was to pass through some SQL injection attack preventing logic.

I believe F# has a compile-time solution for this (called units of measure.) I'd like to do something similar in C#, although I don't need the dimensional analysis that units of measure in F# offers.

I believe C++ could achieve this using typedef (though I'm not a C++ expert).

The obvious solution is to wrap the double/string/whatever in a new type to give it the type information the compiler needs. I'm curious if anyone has an alternative solution. If you do think wrapping is the only/best way, then please go into some of the downsides of the pattern (and any upsides I haven't mentioned too.) I'm especially concerned about the performance of abstracted primitive numeric types on my calculations at runtime, so whatever solution I come up with must be lightweight both in terms of memory allocation and call dispatch.

like image 653
Drew Noakes Avatar asked May 31 '10 19:05

Drew Noakes


2 Answers

I am really interested how does compiler give warning when you mix radians and degrees. Are they both double? You are in OOP world, so you should walk this way. Two suggestions:

  1. Use only one unit internally, i think radians is better. Then convert only on input/output.\
  2. Create structs Degree, Radian and define rules of conversions. Or create class 'Angle' and hold all info about units and conversions there.
like image 163
Andrey Avatar answered Oct 01 '22 12:10

Andrey


I guess you could create two different structs to enforce type checking. In the following code I have added an implicit cast from radians to double and an explicit cast from degress to radians. You could use whatever set of implicit and explicit operators you like, but I think the ones I have defined here would work well since the Radians struct could be passed directly into the Math functions.

public struct Degrees
{
  private double m_Value;

  public static explicit operator Radians(Degrees rhs)
  {
    return rhs.m_Value * (Math.Pi / 180);
  }
}

public struct Radians
{
  private double m_Value;

  public static implicit operator double(Radians rhs)
  {
    return rhs.m_Value;    
  }
}
like image 40
Brian Gideon Avatar answered Oct 01 '22 10:10

Brian Gideon