[Mono-devel-list] Incorrect interpretation of operator arguments?
arhra at whiprush.org
arhra at whiprush.org
Tue Jun 8 15:45:21 EDT 2004
Anders Gustafsson wrote:
> This might be a general C# language question; if that is so, I am sorry
> in beforehand that I have posted the issue in this forum :-)
> To follow-up on my bug report (#59694, see
> http://bugzilla.ximian.com/show_bug.cgi?id=59694), I made a simple test
> program, TestClass.cs:
> Upon compiling (mcs TestClass.cs or gmcs TestClass.cs) and running (mono
> TestClass.exe) it, I get the following output (Mono 1.0 Beta 2, Fedora
> Core 2, Linux kernel 2.6.5-1.358):
> t1 is 30, should be 10
> t2 is 20, should be 20
> t3 is 30, should be 30
> i.e. t1 is modified in the addition operation t3 = t1 + t2 !
> As far as I understand, arguments are passed by value if not explicitly
> specified otherwise (ref, out; see e.g. ECMA-334 C# Language
> Specification, secs. 8.3 and 17.5.1). In fact, according to ECMA-334
> sec. 17.9, operator parameters are required to be value parameters.
> And, even if the arguments were passed by reference, I would have
> expected the statement
> TestClass ret = iLhs;
> in the operator+ method to make a copy of iLhs's value (ECMA-334, sec.
> 14.13.1), not make a reference to the same memory location.
> Now, this problem is not confined to Mono. I have compiled the same
> source code with Microsoft.NET compiler csc (through Borland C#
> Builder), and I get the same result as with Mono!
> So, maybe I am missing something fundamental here. I am grateful for any
> answer or comment that could shed light on issue.
You are indeed missing something fundamental - the c# type-system is
split down the middle between value types (structs, enums) and reference
types (classes, interfaces, arrays, delegates). While 17.9 says that
operator parameters must be values, that includes reference types, and
careful reading of the standard will reveal: (section 11.2: Reference types)
"A reference type value is a reference to an instance of the type, the
latter known as an object."
Also remember that assignment between reference types is done by value,
as well, so the line: TestClass ret = iLhs; in your sample in fact
assigns the _value_ of iLhs to ret, which, as mentioned above, is a
reference to the original object created during iLhs' construction.
So what you're seeing is completely correct and per the spec - if you
want value semantics, change it from a class to a struct (which also
implies that it will be created on the stack and automatically
deallocated when it goes out of scope). If you don't want value
semantics, then the best method is probably to implement a method (or
constructor) that provides a deep copy of your object. The BCL has the
ICloneable interface, which provides a Clone() method, but it's somewhat
troublesome, since it doesn't guarantee any semantics (ie, it could be
either a deep or shallow copy), and in addition, it returns an object,
which necessitates ugly casts in client code.
More information about the Mono-devel-list