Article printed from:
`https://blog.jermdavis.dev/posts/2021/the-power-of-implicit-conversions`

C#
~2 min. read

There are bits of the C# language that we don't think about too much when writing websites – and implicit conversions are one of those things. But while I've been messing about with some ray-tracing code in evenings recently, I found a couple of examples they patterns they can be a help with...

There are various places in a raytracer where you need to deal with angles. The orientation of geometry, working out the directions the rays travel in and how the light reflects and refracts. And wherever your expressing angles you need to choose your units. While a lot of maths is defined in terms of radians, I find that with my "not very good" maths skills, I think in terms of degrees.

So how can I write the code so that it the internals work in the "right" angle units for their work, but I have the option to define things as degrees or as radians to suit my thinking?

A lot of code which does units conversions provides helper methods to convert from one set of units to another. Something like:

public void CalculateSomeAngles(double radians) { ... } var radians = Math.ToRadians(45); CalculateSomeAngles(radians);

That's fine – but it's not really very type safe. Because the method takes a double, you can supply anything. And there's not really anything stopping you accidentally supplying degrees instead of radians.

So what else could you do?

So what if our CalculateSomeAngles() method was explicit about the units it expected by expressing a type?

public struct Radians { public double Value { get; private set; } public Radians(double value) { Value = value; } } public void CalculateSomeAngles(Radians radians) { ... } CalculateSomeAngles(new Radians(1.1));

With a wrapper type to be explicit about the units, it's harder to make a mistake. And we can also use this pattern to allow you to pass whatever units you prefer. We can define a struct for degrees too:

public struct Degrees { public double Value { get; private set; } public Degrees(double value) { Value = value; } }

But the magic here is adding some operators to allow the compiler to automatically do some conversions for us. If we add a "to degrees" conversion to the radians struct and the equivalent degrees struct:

public struct Degrees { public static implicit operator Radians(Degrees d) { return new Radians(d.Value * Math.PI / 180); } } public struct Radians { public static implicit operator Degrees(Radians r) { return new Degrees(r.Value * 180 / Math.PI); } }

Then the compiler no longer cares which you pass – it will just convert to the one it needs in any situation...

So you can write:

CalculateSomeAngles(new Radians(1.1)); CalculateSomeAngles(new Degrees(72));

And now I don't have to worry about my units.

Using this for unit conversion is one very simple usage for this approach. (And in fact, one you could argue can be solved in other ways) The other place I found this sort of pattern helpful was in implementation animations. I wanted to enable a situation where most (if not all) of the properties of objects in a scene could be animated. Broadly that means "animate the values of a double" or "animate a matrix". And in both cases the implicit conversions idea can help.

The pattern I ended up with for animation was that it needed a mechanism to set which frame is being rendered, and a mechanism to get the value for this point. Something like:

public interface IAnimateable<T> { T Value { get; } void SetFrame(int thisFrame, int frameCount); }

So if we have a material with "shinyness" that can be amimated, we can replace the normal
`double`

-typed property with something which exposes this interface, and when the material gets initialised for a specific frame we just pass that data down to the animateable properties:

public class PhongMaterial { .... public IAnimateable<double> Shinyness { get; init ; } public void SetFrame(int thisFrame, int frameCount) { .... Shinyness.SetFrame(thisFrame, frameCount); .... } .... }

And we can have a whatever implementations of this animatable double we like. So to move between 1 and 100, we might declare something like:

var someShape = new Sphere() { Material = new PhongMaterial() { Shinyness = new AnimatedRange(1, 100) } };

But the side effect of doing this is that when we don't need an animated value we still have to pass an object for that value:

var someShape = new Sphere() { Material = new PhongMaterial() { Shinyness = new StaticValue(25) } };

But the trick above with implicit conversions can save us some typing here. The definition for the static value can help:

public class StaticValue : IAnimateable<double> { public double Value { get; private set; } public StaticValue(double value) { Value = value; } public void SetFrame(int thisFrame, int frameCount) { } public static implicit operator StaticValue(double value) { return new StaticValue(value); } }

And now a value which doesn't animate can be declared as:

var someShape = new Sphere() { Material = new PhongMaterial() { Shinyness = 25 } };

This pattern has ended up very useful for both simple scene properties like above, and for more complex Matrix properties for transformations.

↑ Back to top