14

I was looking through some code I wrote a while ago and realized I made an assumption about the assignment operator in C#. Here is the line of code in question (it works as expected):

pointsChecked = pointsChecked ?? new List<Point>();

pointsChecked is a List specified as a parameter to a recursive function. It is a default parameter with default value null. What I want to do is initialize it once and then build a collection of points that I have already checked, so it should only be initialized during the first iteration.

My assumption was that C# is guarded against self-assignment in the same way that C++ operator= should provide a guard when overloaded (ie, if(this == &rightHandSide) return *this;). However, I haven't been able to find any resources that explicitly state that this is true for C#.

The closest example I found was this question about the null-coalescing operator, where it appears that the object is assigned back to itself if it is not null. No one said anything about the self-assignment in that example, but I want to be certain that this isn't a bad practice and that there are no negative side effects.

Searching on MSDN I also found that (paraphrasing based on my understanding) the value on the right hand side is copied over to the value on the left hand side and returned. So I am, again, unsure if it is a bad thing to do a self-assignment.

I know I could do the following to be safer:

if(pointsChecked == null)
{
    pointsChecked = new List<Point>();
}

But I would rather understand what is actually happening with self-assignment.

Community
  • 1
  • 1
E. Moffat
  • 3,165
  • 1
  • 21
  • 34
  • Trying to optimize self assignment on field (not normal local variables) might have some non trivial effects on multi-threaded code. Not sure if the CLR memory model allows that optimization. – CodesInChaos Sep 15 '13 at 18:12
  • 2
    C# is not C++. The whole idea of guarding against self-assignment in an overloaded assignment operator makes no sense whatsoever. Objects cannot mutate when a variable holding them is assigned to, it's either a reference to an object being made to point someplace else, or the contents of a value type are overwritten, as-is, by the contents of another instance of that type. – millimoose Sep 15 '13 at 18:20
  • 1
    @millimose I realize that. I'm not asking about guarding against self-assignment, you can't even overload the assignment operator in C#. I was saying I made the assumption that a similar construct was in place behind the scenes and wanted some clarification on what was actually happening. – E. Moffat Sep 15 '13 at 18:23
  • @prettycooldevguy Behind the scenes the bucket for the `pointsChecked` variable will either hold its original value, or the object handle for the `List` if the original value was `0`. It doesn't matter if the original value is self-assigned to itself or not because it's just a pointer or an opaque object reference. Whatever goes on shouldn't really matter - were this not completely transparent to you, it'd be a bug in the C# implementation as long as the concept of self-assignment shouldn't exist in C#. – millimoose Sep 15 '13 at 18:24

1 Answers1

11

Assignment copies the reference to an object, not the object contents. No customizable code runs as part of an assignment to a variable holding an object reference, ever. This is also true for structs.

In C++ assignment is customizable, in C# it is not.

It is safe to assign the same object reference to a variable already holding it:

object someRef = new object();
someRef = someRef; //always does nothing

This is as safe as assigning any other value:

int someVal = 123;
someVal = someVal; //always does nothing

Note, that no general way exists to clone/copy an object. Any explanation relying on the presence of such a mechanism must be wrong.

Self-assignment is safe because it translates to the following approximate IL instructions:

ldloc someRef
stloc someRef

This has clearly defined semantics. It first loads someRef onto the stack, then stores whatever is on the stack to someRef.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Interesting, can you go in to more detail about what happens when you overload the `operator =` on a struct and how it may (or can't) effect this? – Scott Chamberlain Sep 15 '13 at 18:34
  • 2
    The assignment operator cannot be overloaded. Neither for ref types nor for structs. A struct copy is a designated IL instruction that directs the runtime to perform a bitwise copy. – usr Sep 15 '13 at 18:35
  • Ah, I see. Goes to show you how often I attempt to overload operators. – Scott Chamberlain Sep 15 '13 at 18:36
  • +1. Note that "C++ assignment is customizable" is not complete - C++ - assignment to pointer can't be overloaded (and pointers to objects represent what `someVar` in `SomeNonValueType someVar` essentially is)... – Alexei Levenkov Sep 15 '13 at 20:32
  • Thank you, I guess I was just over-thinking it. Its nice to know for sure and I haven't seen another explanation on SO yet so hopefully this helps someone else as well! – E. Moffat Sep 15 '13 at 21:03