Search code examples
c#interfacestatic-constructor

Converting between similar types in an open/closed principle sort of way


First, let me state what I have learned so far: C# interfaces cannot have static members - see here or here

Now for what I would like to do:

I have several different coordinate types that I want to convert between. I was thinking that if I created a LatLon class and made them all implement this:

ICoordinate
{
    LatLon ToLatLon();
    object FromLatLon(LatLon latLon); // must have an instance to call this method :(
}

then it would be trivial to convert between types, and I'd have followed the Open Closed Principle - I can add as many types of coordinates as I want (open to extension), and as long as they implement ICoordinate, I can immediately convert between them and ANY OTHER COORDINATE TYPE without updating any old code (closed for modification).

Then I realized that FromLatLon could only be called if I already had an instance of a coordinate and it seems silly to say something like new MyCoordinate().FromLatLon( aLatLon );*

Is there a better pattern for this kind of thing?

Other options I've thought about:

  • Factory.CreateCoordinate(double lat, double lon, string coordinateType) - but in order to create more coordinate types, I'll also have to update my Factory, so my factory is not closed.
  • require classes to implement new(lat, lon), but this isn't possible in C# 7

Edit

*so I'd have to create a new instance, then access a method on it that will also create a new instance. I'd have to create two instances every time I needed one...


Solution

  • it seems silly to say something like new MyCoordinate().FromLatLon( aLatLon );

    Is that your only complaint about the interface-based approach? If so, why not elaborate on the interface-based approach by including implicit (or explicit) conversion operators?

    The interface-based approach is useful where you don't already know the type of the object you're dealing with. Indeed, your LatLon type could also implement the interface, and just return itself.

    But where you know the type or types you're dealing with (as you do in the example you give), why bother with an interface? Something like public static implicit operator MyCoordinate(LatLon d) => /* your conversion here */; will allow for code like MyCoordinate myCoordinate = someLatLonValue;. As an added bonus, if these types are value types, you won't run the risk of having them boxed just for the sake of the interface.

    And the interface implementation would almost certainly be trivial, deferring to the operator. E.g. something like:

    struct MyCoordinate : ICoordinate
    {
        // Implicit conversion "operator LatLon(MyCoordinate mc) { }" takes care of this
        LatLon ToLatLon() => this;
    
        // No need for `FromLatLon()`...this would be provided for by the other implicit conversion
    }