77

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?

3
  • 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. Commented Apr 3, 2011 at 17:34
  • 1
    absolutely, I'm using it now. It doesn't know ISO units, rather you define the units yourself, like [<Measure>] type m; [<Measure>] type s and can then use things like let a = 9.8<m> / 4.3<s> which yields val a : float<m/s> = 2.279069767 Commented Apr 3, 2011 at 18:37
  • 1
    Sorry, i meant to say SI units, which are predefined in Microsoft.FSharp.Math.SI. See: blogs.msdn.com/b/andrewkennedy/archive/2008/09/02/… Commented Apr 3, 2011 at 18:42

3 Answers 3

113

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.)

Sign up to request clarification or add additional context in comments.

12 Comments

Perfect! Thanks. Also... you're right. Obviously my physics professor failed to bash vectors into my brain hard enough :)
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
@Henk: No, speed is a scalar, but velocity has a direction. See en.wikipedia.org/wiki/Velocity - "It is a vector physical quantity; both magnitude and direction are required to define it."
@Henk: nope. Velocity is a vector, and speed is the magnitude of that vector.
@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...
|
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.

2 Comments

The bad idea link is dead, seems to just take you to the MS MVPs homepage. I think codeblog.jonskeet.uk/2010/07/27/iterate-damn-you is the article in question?
here's the bad idea
-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 :-)

8 Comments

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
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.
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).
@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?
@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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.