26

I'd like to be able to implicitly convert between two classes which are otherwise incompatible.

One of the classes is Microsoft.Xna.Framework.Vector3, and the other is just a Vector class used in an F# project. I'm writing a 3d game in C# with XNA, and -- although it's drawn in 3D, the gameplay takes place in only two dimensions (it's a birds-eye-view). The F# class takes care of the physics, using a 2D vector:

type Vector<'t when 't :> SuperUnit<'t>> =
    | Cartesian of 't * 't
    | Polar of 't * float
    member this.magnitude =
        match this with
        | Cartesian(x, y) -> x.newValue(sqrt (x.units ** 2.0 + y.units ** 2.0))
        | Polar(m, _) -> m.newValue(m.units)
    member this.direction =
        match this with
        | Cartesian(x, y) -> tan(y.units / x.units)
        | Polar(_, d) -> d
    member this.x =
        match this with
        | Cartesian(x, _) -> x
        | Polar(m, d) -> m.newValue(m.units * cos(d))
    member this.y =
        match this with
        | Cartesian(_, y) -> y
        | Polar(m, d) -> m.newValue(m.units * sin(d))

This vector class makes use of the unit system used by the physics project, which takes native F# units of measure and groups them together (units of Distance, Time, Mass, etc).

But XNA uses its own Vector3 class. I want to add an implicit conversion from the F# Vector to the XNA Vector3 which takes care of which two dimensions the gameplay takes place in, which axis is "up", etc. It'd be simple, just Vector v -> new Vector3(v.x, v.y, 0) or something.

I can't figure out how to do it though. I can't add an implicit conversion in F# because the type system (rightly) doesn't allow it. I can't add it to the Vector3 class because that is part of the XNA library. As far as I can tell I can't use an extension method:

class CsToFs
{
    public static implicit operator Vector3(this Vector<Distance> v)
    {
        //...
    }
}

Is an error on the this keyword, and

class CsToFs
{
    public static implicit operator Vector3(Vector<Distance> v)
    {
        return new Vector3((float)v.x.units, (float)v.y.units, 0);
    }

    public static void test()
    {
        var v = Vector<Distance>.NewCartesian(Distance.Meters(0), Distance.Meters(0));
        Vector3 a;
        a = v;
    }
}

is an error on a = v; (cannot implicitly convert...).

Is there a way to do this without being able to put the cast in either of the classes? As a last resort I could open Microsoft.Xna.Framework and do the conversion in F#, but that seems wrong to me -- the physics library shouldn't know or care what framework I'm using to write the game.

Carson Myers
  • 37,678
  • 39
  • 126
  • 176

2 Answers2

23

No, you can't. The implicit operator has to be defined as a member of one of the classes. However, you can define an extension method (your example didn't work as extension methods have to be in a public static class).

public static class ConverterExtensions
{
    public static Vector ToVector (this Vector3 input)
    {
      //convert
    }
}
Femaref
  • 60,705
  • 7
  • 138
  • 176
  • Can't believe I didn't think of this even though it's pretty much exactly what I was trying. – Carson Myers Apr 02 '11 at 23:46
  • Would it work if you had a user defined type that could be implicitly converted to/from the two other types? Type C implicitly converts from Type A and implicitly converts to Type B. Would that work? – Josh Gust Aug 14 '19 at 01:32
  • 4
    Saw this comment today and decided to try it out. Nope, it doesn't work. I think the language only does 1 casting at most. If you casted to the intermediate type explicitly then it would work, but at that point you might as well just write the extension method. – Cheri May 30 '20 at 03:59
4

In C# you cannot. You could provide descendant types that have the extra conversions (provided the types aren't sealed); Or you could provide a generic wrapper class that somehow adds the conversion.

The convenience of all this depends on the way in which these wrappers/adaptations can be used with the original APIs.

On another note I see you are useing F# as well. IIRC F# is supposed to have some level of meta-programming ability (like many functional languages for .NET, such as Boo and Nemerle). It would not surprise me if that could be used here. Unfortunately my F is not sharp enough to help there

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I've been thinking about using a child class, I'm worried it'll get overly convoluted though – Carson Myers Apr 02 '11 at 23:49
  • 1
    Seeing as both of these objects are value types - I think a child class is out of the question. – MattDavey Mar 23 '12 at 15:30
  • 3
    I tried to use a proxy class to implicitly convert, but C# won't automatically apply a double conversion. (And why aren't constructors considered conversion operators?) – NetMage Oct 06 '16 at 16:20