75

I know the = operator can't be overloaded, but there must be a way to do what I want here:

I'm just creating classes to represent quantitative units, since I'm doing a bit of physics. Apparently I can't just inherit from a primitive, but I want my classes to behave exactly like primitives -- I just want them typed differently.

So I'd be able to go,

Velocity ms = 0;
ms = 17.4;
ms += 9.8;

etc.

I'm not sure how to do this. I figured I'd just write some classes like so:

class Power
{
    private Double Value { get; set; }

    //operator overloads for +, -, /, *, =, etc
}

But apparently I can't overload the assignment operator. Is there any way I can get this behavior?

Carson Myers
  • 37,678
  • 39
  • 126
  • 176
  • Have you looked at the `units of measure` feature of F# ? it knows standard (ISO) units like M, KG and M/S, and it can calculate with units too. – oɔɯǝɹ Apr 03 '11 at 17:34
  • 1
    absolutely, I'm using it now. It doesn't know ISO units, rather you define the units yourself, like `[] type m; [] type s` and can then use things like `let a = 9.8 / 4.3` which yields `val a : float = 2.279069767` – Carson Myers Apr 03 '11 at 18:37
  • 1
    Sorry, i meant to say SI units, which are predefined in `Microsoft.FSharp.Math.SI`. See: http://blogs.msdn.com/b/andrewkennedy/archive/2008/09/02/units-of-measure-in-f-part-two-unit-conversions.aspx – oɔɯǝɹ Apr 03 '11 at 18:42

3 Answers3

109

It sounds like you should be using a struct rather than a class... and then creating an implicit conversion operator, as well as various operators for addition etc.

Here's some sample code:

public struct Velocity
{
    private readonly double value;

    public Velocity(double value)
    {
        this.value = value;
    }

    public static implicit operator Velocity(double value)
    {
        return new Velocity(value);
    }

    public static Velocity operator +(Velocity first, Velocity second)
    {
        return new Velocity(first.value + second.value);
    }

    public static Velocity operator -(Velocity first, Velocity second)
    {
        return new Velocity(first.value - second.value);
    }

    // TODO: Overload == and !=, implement IEquatable<T>, override
    // Equals(object), GetHashCode and ToStrin
}

class Test
{
    static void Main()
    {
        Velocity ms = 0;
        ms = 17.4;
        // The statement below will perform a conversion of 9.8 to Velocity,
        // then call +(Velocity, Velocity)
        ms += 9.8;
    }
}

(As a side-note... I don't see how this really represents a velocity, as surely that needs a direction as well as a magnitude.)

Ben James
  • 121,135
  • 26
  • 193
  • 155
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    Perfect! Thanks. Also... you're right. Obviously my physics professor failed to bash vectors into my brain hard enough :) – Carson Myers Dec 27 '10 at 09:59
  • 2
    Is there any reason this can't be a class? I was hoping to inherit from a `ScalarUnit` or `VectorUnit` class so as not to repeat _everything_ for each unit, and apparently structs cannot inherit – Carson Myers Dec 27 '10 at 10:01
  • 8
    @Henk: No, *speed* is a scalar, but velocity has a direction. See http://en.wikipedia.org/wiki/Velocity - "It is a vector physical quantity; both magnitude and direction are required to define it." – Jon Skeet Dec 27 '10 at 10:01
  • 1
    @Henk: nope. *Velocity* is a vector, and *speed* is the magnitude of that vector. – R. Martinho Fernandes Dec 27 '10 at 10:02
  • 3
    @Carson: You *could* make it a class, but it sounds like it would be more appropriate to be a value type, given that it represents a reasonably primitive "value" rather than a natural "object". The code for a class would basically be the same though, so you can experiment with both reasonably easily. You'll have difficulties expressing the operators in an inheritance tree though... – Jon Skeet Dec 27 '10 at 10:03
  • OK, I was thinking speed==velocity. – H H Dec 27 '10 at 10:05
  • I can't just create a `VectorUnit` class, and then create a bunch of `public class Velocity : VectorUnit {}` type classes? – Carson Myers Dec 27 '10 at 10:05
  • @Jon: I see, so I'd need an implicit operator signature for each subclass in the parent class? Because `someScalar = 42` would convert to a ScalarUnit, and that would then have to be implicitly converted to whatever subclass it was being assigned to? Wouldn't that still be better than duplicating all the other operator overloads? – Carson Myers Dec 27 '10 at 10:11
  • Hi Jon, so I gather from another of your excellent answers that [mutable structs are evil](http://stackoverflow.com/a/945708/340045). I noted that the Velocity struct you proposed here is immutable, but it is not clear to me why you chose to use a struct? If it is immutable and can't change then what difference does it make if it is a class or a struct? I would have thought that classes would perform better when variables of type Velocity get passed around. – Ben Oct 05 '15 at 15:34
  • @Ben: No, *mutable* structs are evil. The struct I've presented here is immutable. As for why I've used a struct here - it seems reasonable as a value type in this case, in the same way that DateTime is. – Jon Skeet Oct 05 '15 at 15:36
  • 1
    @Ben: Right - read the rest of my comment, basically... and see https://msdn.microsoft.com/en-us/library/ms229017.aspx – Jon Skeet Oct 05 '15 at 15:37
  • I'm not one to post a "Thank you" comment (this would be the first ever), but this time I feel it is in order: Thank you very much for making my life a lot easier with this very straight forward example. Definitely a +1. – Heki Dec 23 '16 at 11:43
13

You can create implicit conversion operators. There is a page on MSDN with a nice example.

It's also a good idea to make them immutable structs. That's exactly what the "primitives" are, and that's what makes it impossible to inherit from them. You want a struct because you want value-type semantics, instead of reference type semantics. And you want them immutable because mutable value types are generally a bad idea.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • The bad idea link is dead, seems to just take you to the MS MVPs homepage. I think https://codeblog.jonskeet.uk/2010/07/27/iterate-damn-you/ is the article in question? – Nemo Feb 25 '21 at 07:56
-9

I think it cannot be overloaded because C# classes are all derived from Object, so they are basically objects, and when you use the assignment operators, you are basically just referencing another object. On the other hand, if you use a structure, then you basically need all the information, so when you use = operator, all fields will be copied.

So I would say, face it, and implement a function called Copy() and you should be fine :-)

Rafid
  • 18,991
  • 23
  • 72
  • 108
  • How does that differ from the other operators? How do you add all of the information of two objects together? I want to assign directly to the internal value, instead of overwriting the entire object – Carson Myers Dec 27 '10 at 09:49
  • 1
    I think it cannot be overloaded because the language designers thought it would be a bad idea. There is no technical reason it couldn't be done. – R. Martinho Fernandes Dec 27 '10 at 09:50
  • 1
    No, I think there is a reason. Say you have an object a, and an object b. Once you run the statement a=b, the object which was pointed to by a will actually be garbage collected (because there is no object pointing to it anymore), so how will implement an assignment operator here? On the other hand, all other operators either return another object (e.g. a + b), or actually change the current object when it is not going to be lost (i.e. garbage collected) (e.g. a += b). – Rafid Dec 27 '10 at 09:53
  • Say, you have an object a, and an object b. Once you run the statement a=b, what really happens is that the method ToString is called on a and that string is used as the first parameter of a Console.WriteLine call, and b is the second parameter. That's silly and hideous but that could have been the meaning chosen for the `=` symbol. It's just a symbol that happens to have a fixed meaning in C#. FYI, C++ allows overloading that operator. – R. Martinho Fernandes Dec 27 '10 at 10:00
  • @Martinho C++ is not a garbage collected language :) – Carson Myers Dec 27 '10 at 10:04
  • @Carson What does language syntax have to do with garbage collection? If the designers decided that you could implement `public static operator=(ref T assigned, T assignee)` to overload `=`, what would be the problem with garbage collection? – R. Martinho Fernandes Dec 27 '10 at 10:07
  • in C#, if you create two objects, and then assign the second to the first, then the garbage collector can collect the first one because it is now a reference to the second, and there are no more references to the first. In C++ it's up to you to keep track of that reference and `delete` the object, but in C# whether or not there are remaining references to an object has to be deterministic. If you could overload `=`, it wouldn't be deterministic anymore, because the gc wouldn't know if the assignment reseated the reference or not. Or something. – Carson Myers Dec 27 '10 at 10:16
  • 3
    @Carson: The runtime can always know when you change a reference because it has to run the code that changes it. Also, look carefully at the language as it is. There is already a special instance of overloading `=` in place that the designers deemed ok: property setters. Let `X` be a property of `foo`. In `foo.X = 3`, the `=` symbol is replaced by the *compiler* by a call to `foo.set_X(3)`. You can already define a `public static T op_Assign(ref T assigned, T assignee)` method. All that is left is for the compiler to replace `=` with a call to it. – R. Martinho Fernandes Dec 27 '10 at 10:20