34

Unlike C++, in C# you can't overload the assignment operator.

I'm doing a custom Number class for arithmetic operations with very large numbers and I want it to have the look-and-feel of the built-in numerical types like int, decimal, etc. I've overloaded the arithmetic operators, but the assignment remains...

Here's an example:

Number a = new Number(55); 
Number b = a; //I want to copy the value, not the reference

Is there a workaround for that issue?

Steve
  • 7,747
  • 5
  • 31
  • 44
  • 2
    Explain the issue (why you need it). – Shog9 Nov 15 '08 at 15:37
  • Sorry, maybe I should've been more specific. I'm doing a custom Number class for arithmetic operations with very large numbers and I want it to have the look-and-feel of the built-in numerical types like int, decimal, etc. I've overloaded the arithmetic operators, but the assignment remains... –  Nov 15 '08 at 16:13
  • Thanks - i've added your comment to the question itself. – Shog9 Nov 15 '08 at 16:15
  • Can you give a specific example of what you'd like to be able to write? – Jay Bazuzi Nov 15 '08 at 16:19
  • 1
    Here's an example: Number a = new Number(55); Number b = a; And I want it to copy the value, not the reference. –  Nov 15 '08 at 16:21
  • structs are value based by all i know. can't you use "struct" for your numbers? – Johannes Schaub - litb Nov 15 '08 at 16:28
  • @litb: That's an idea, but I think I have too much functionality in the Number class to make it a struct. Thanks, anyway :) –  Nov 15 '08 at 16:39
  • In what way do you think you have "too much functionality [...] to make it a struct"? Do you mean it's too large in terms of memory usage? – Jon Skeet Nov 15 '08 at 16:40
  • @Jon Skeet: No, just too many methods in there... Kind of doesn't feel like a struct. –  Nov 15 '08 at 16:45
  • 2
    Look at DateTime and TimeSpan. Plenty of methods there. If it's effectively an atomic value, then it should probably be a struct. Numbers almost always fall into this category. – Jon Skeet Nov 15 '08 at 17:10
  • @Jon Skeet: Yes, you pretty much have me convinced. Thanks! –  Nov 15 '08 at 17:46

7 Answers7

42

you can use the 'implicit' keyword to create an overload for the assignment:

Suppose you have a type like Foo, that you feel is implicitly convertable from a string. You would write the following static method in your Foo class:

public static implicit operator Foo(string normalString)
{
    //write your code here to go from string to Foo and return the new Foo.
}

Having done that, you can then use the following in your code:

Foo x = "whatever";
Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • 2
    Cool, this answers a question I was going ot ask ;) – Sam Salisbury Apr 20 '10 at 14:35
  • 3
    Doesn't that only work if you are trying to assign one type to another? It's an implicit cast, after all. I don't think that would help if you wanted to assign `Number` to `Number`. – Drew Noakes Jun 05 '10 at 06:51
  • @Jonathan Wood - That was exactly what i was looking for! Much appreciated! – Steven Magana-Zook Jun 10 '11 at 23:45
  • @drew-noakes: If you want to assign Number to Number, and Number is a struct, you get the desired effect with the default assignment operator anyway: Assign by value. – Nilzor Aug 03 '11 at 11:21
  • @Nilzor, in such a case you get a copy of the value as you say, but the OP wants to control that process somehow with their own code. I don't think that's possible unless you're adapting between two types. So it's slightly different to what you can do in C++. – Drew Noakes Aug 03 '11 at 11:44
  • @jonathon wood - thanks. what i was after too. very helpful in 3D math –  Feb 22 '12 at 09:24
29

It's still not at all clear to me that you really need this. Either:

  • Your Number type should be a struct (which is probable - numbers are the most common example of structs). Note that all the types you want your type to act like (int, decimal etc) are structs.

or:

  • Your Number type should be immutable, making every mutation operation return a new instance, in which case you don't need the data to be copied on assignment anyway. (In fact, your type should be immutable whether or not it's a struct. Mutable structs are evil, and a number certainly shouldn't be a mutable reference type.)
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yes, maybe the struct idea is good. And I like the immutability option - it was suggested in another answer. Thanks, I'll try what you propose. –  Nov 15 '08 at 17:03
  • 1
    Mutable structs might be evil but the C# team thought they were ok for Enumerators. I think it's more correct to say it as a general rule. – Razor May 16 '11 at 04:57
  • 3
    Mutability is not evil, if you can prevent object sharing. This can be done by implementing linear types which requires some form of assignment overloading. http://en.wikipedia.org/wiki/Linear_type_system. – cdiggins Apr 13 '12 at 15:30
6

You won't be able to work around it having the C++ look, since a = b; has other semantics in C++ than in C#. In C#, a = b; makes a point to the same object like b. In C++, a = b changes the content of a. Both has their ups and downs. It's like you do

MyType * a = new MyType();
MyType * b = new MyType(); 
a = b; /* only exchange pointers. will not change any content */

In C++ (it will lose the reference to the first object, and create a memory leak. But let's ignore that here). You cannot overload the assign operator in C++ for that either.

The workaround is easy:

MyType a = new MyType();
MyType b = new MyType();

// instead of a = b
a.Assign(b);

Disclaimer: I'm not a C# developer

You could create a write-only-property like this. then do a.Self = b; above.

public MyType Self {
    set {
        /* copy content of value to this */
        this.Assign(value);
    }
}

Now, this is not good. Since it violates the principle-of-least-surprise (POLS). One wouldn't expect a to change if one does a.Self = b;

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Maybe I could do it that way. But what I meant was - a workaround that gives me the real look-and-feel of assignment operator overloading, so I ca literally say: MyType a = new MyType(); MyType b = new MyType(); a = b; and it performs some custom assignment logic. –  Nov 15 '08 at 15:54
  • The description of why it doesn't work is spot-on. As you suggest, though, the Self property idea is not so good, – Charlie Nov 15 '08 at 16:09
  • "In C++, a = b changes the content of a." - thats not entirely correct. it depends on whether a and b are pointers or not. a=b could make a point to b or it could call your = overload and copy the contents to a. –  Feb 22 '12 at 09:47
  • 1
    @MickyDuncan Eh.. it is entirely correct. What do you think the content of a and b IS if they are pointers? In what sense does "only copying the pointer" differ from "copying the content" when the content IS a pointer? – The Dag Nov 06 '12 at 10:32
  • @TheDag c++ = assignment can either make a pointer point to the same object OR change the contents depending on the variable type. One did not establish we were talking about just pointers –  Nov 07 '12 at 02:54
3

Instead of making a copy of the data when passing the reference you could make the class immutable. When the class is immutable having multiple references to it isn't a problem since it can't be changed.

Operations that changes the data would of course return new instances.

Cristian Libardo
  • 9,260
  • 3
  • 35
  • 41
2

An earlier post suggested this:

public static implicit operator Foo(string normalString) { }

I tried this approach... but to make it work you need this:

public static implicit operator Foo(Foo original) { }

and the compiler won't let you have an implicit conversion function from your exact type, nor from any base type of yourself. That makes sense since it would be a backdoor way of overriding the assignment operator, which C# doesn't want to allow.

DMC
  • 71
  • 1
  • 3
  • "The implicit keyword is used to declare an implicit user-defined type conversion operator. Use it to enable implicit conversions between a user-defined type and another type, ..." - MSDN –  Feb 22 '12 at 09:50
0

Here is a solution that worked for myself :

public class MyTestClass
{
   private int a;
   private string str;

   public MyTestClass()
   {
      a = 0;
      str = null;
   }

   public MyTestClass(int a, string str)
   {
      this.a = a;
      this.str = str;
   }

   public MyTestClass Clone
   {
      get
      {
         return new MyTestClass(this.a, this.str);
      }
   }
}

Somewhere else in the code :

MyTestClass test1 = new MyTestClass(5, "Cat");
MyTestClass test2 = test1.Clone;
ttrixas
  • 1
  • 1
-4

Maybe what you're looking for can be solved using C# accessors.

http://msdn.microsoft.com/en-us/library/aa287786(v=vs.71).aspx

TDot
  • 47
  • 1
  • 1