dotNET Rant [modified]
-
That's an interesting oddity, to be sure. I don't see why the second case is not boxing. Isn't any literal integer a value type by definition? So then, if the two examples are different, then what is boxed in the first case? A reference to the iOne, iTwo variables? Or are their respective values boxed? And then what is the second case called, if not boxed? Consider the following code.
void func(out object one, out object two)
{
int iOne = 1;
one = iOne;two = 2;
}
what is in 'one' and what is in 'two' after the return of this function? I submit they're both boxed. Of course, I admit I'm no expert and I'm fully ready to be instructed.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/In Scott's first case, a new Int32 object is created with a value. Assigning this object to another object causes boxing, because the RHS value type is not 'compatible' with the LHS ref type. In his second case,
object one = 1;
, once again a new Int32 object is created, but I assume that because it was created 'right there', buy the CLR, it can be directly assigned to the RHS. How far off the mark am I guys? -
but in code that is comparing boxed values, you don't know what the boxed types are so it's not as simple as casting to a known type.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/ahmed zahmed wrote:
you don't know what the boxed types
I think that is a key point, and maybe why '==' doesn't attempt a value comparison.
-
Well, it seems that it would given my remembered(and corrected/reminded) understanding. So 'same' would be false. (Haven't typed it into a compiler yet so I don't really 'know' -- lol). In any case, my rant was incorrectly focused. I was really venting about a larger issue and focused on this one small thing. Still, looking at it from a novice perspective (which I'm not -- even though it sure sounded like it yesterday --, but it helps sometimes to do that), it seems incongruous to have the result it does. Of course, once explained to said novice, it's understood. It's just programming after all, and each language/runtime has its 'quirks'. cheers.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/ahmed zahmed wrote:
Still, looking at it from a novice perspective (which I'm not -- even though it sure sounded like it yesterday --, but it helps sometimes to do that), it seems incongruous to have the result it does.
I suspect the same of the C# interpretation of the Laws for the pre- and post-decrement operators. I'll remember and post the experiment a bit later.
-
I dont think there is a reason, just a coin toss. Because everything inherits from object it might be easier to compare the pointer values. But they could just as easily have done a ToString() on each object and compared values. Maybe its to deal with null references, where ToString() would not work? But then they could always have had some pre defined rule as when two null references are compared they are equal, like if both null then they are equal otherwise false
Tim Yen wrote:
Maybe its to deal with null references, where ToString() would not work?
Aha! My morning is complete, and the nagging question of why they don't just call Equals() on both objects is answered.
-
Because keeping all 2^32 possible integers around until they are (maybe) reused would waste up to 48 GB of RAM (or 96 GB on 64-bit systems, as the overhead of objects is higher there). And I don't really want the Java behavior where all integers between -128 and 127 return the same object, but all other integers don't.
Fair call, I must admit I was mostly thinking of literals when I made my comment. These of course would be able to be handled at compile time.
-
This is because dotNET is Microsoft's version of Java, and that is how Java would do it. My guess is that you are coming from a C++ background where you expect it to call operator==(). The main difference between Java and the first dotNET was that Java lowercased the first letter of methods, while dotNET uppercased the first letter. dotNET has evolved at a much faster (almost unstable) pace since then. Disclosure: I am a Java programmer that is glad that MS created dotNET. Java would probably still not have enums if MS hadn't ditched Java and started competing directly against it.
.Net is the platform and C# is the language; in the Javaverse they're both called Java, which is highly confusing. As pointed out in one of the responses above the original code would behave differently in Java because Java caches Integer 0. I'm also a Java programmer and with the exception of generics I'm ambivalent about the language changes in 1.5 & 1.6
-
I dont think he was complaining about the 'ref' keyword, but rather the way == is overloaded on object to mean "compare" pointers. But, then when all you have is an 'object' what else can you do? Still it seems that the runtime knows that it's a boxed value-type and could do something 'smarter'. But I don't know the full implications of doing something like that, at least not now. Who knows what would break.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/Yes I wasn't saying the ref keyword didn't serve some purpose in the language as it is now, I was really leading to the fact that the designers of C# invented the ref keyword to deal with a particular problem and therefore they could have invented another way of making the == operator unambiguous. I imagine they could have invented a complex set of rules to avoid the keyword ref and potentially made the language more ambiguous which is what they have done with ==. Or they could have gone down the path of avoiding the overload. I think the language features are now cast in stone and it will take the next version of C# (maybe C##) to fix the silly bits.
-
What makes you think that a == b and a.Equals(b) should be the same? They are by definition different, and specifically used to check different things, both of which are necessary. Reference equals (the == operator, or the more explicit Object.ReferenceEquals method) checks to see if two variables (or pointers, since you are dealing with reference types) point to the same object in memory, e.g., do they have the same address. Using a.Equals(b) is the same as Object.Equals(a, b), and it compares the values of the two objects. This is very well defined behavior, and most programming languages discern the difference between reference and value comparison. Just because you have not understood the problem, or better yet, the solution to the problem, does not mean that it is a design flaw. To assert that it is a design flaw it to also assert that you have a complete understanding of the language and the specification, and clearly, you do not.
hang on
Sam Meacham wrote:
Using a.Equals(b) is the same as Object.Equals(a, b), and it compares the values of the two objects.
No it doesn't. http://msdn.microsoft.com/en-us/library/w4hkze5k.aspx[^] "The default implementation of Equals supports reference equality for reference types, and bitwise equality for value types" I don't think he has misunderstood the problem, I think you have misunderstood the original rant. The original rant is about they way equals works and him disagreeing with the way it works. He believes == should always do a value comparison and there should be some other way like ReferenceEquals() to do a reference comparison. I agree with him. The way == works now you have to know the type of the variables and then apply the rules. But the rules even then aren't 100% consistent, in the strange case of the string object == does a value comparison but all other objects it does a reference comparison. With the addition of the var keyword this type knowledge may not be available within the current function and possibly the current class or even current project. It may only be available in a referenced library. So you may not have access to the code and as such the type information until run time to do an == on a variable. After thinking about this for a while I think the reason they went down the current path is value comparison of two objects would require evaluating all branches of the object, which may be computationally expensive and potentially very expensive if your comparing say a list of lists of lists of objects. Still there could be other ways around this by generating compiler errors on "object == object" and developing other syntax like object.ReferenceEquals(object)
-
In Scott's first case, a new Int32 object is created with a value. Assigning this object to another object causes boxing, because the RHS value type is not 'compatible' with the LHS ref type. In his second case,
object one = 1;
, once again a new Int32 object is created, but I assume that because it was created 'right there', buy the CLR, it can be directly assigned to the RHS. How far off the mark am I guys?Yes, that's pretty much correct.
Scott Dorman
Microsoft® MVP - Visual C# | MCPD President - Tampa Bay IASA [Blog][Articles][Forum Guidelines]
Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai
-
That's an interesting oddity, to be sure. I don't see why the second case is not boxing. Isn't any literal integer a value type by definition? So then, if the two examples are different, then what is boxed in the first case? A reference to the iOne, iTwo variables? Or are their respective values boxed? And then what is the second case called, if not boxed? Consider the following code.
void func(out object one, out object two)
{
int iOne = 1;
one = iOne;two = 2;
}
what is in 'one' and what is in 'two' after the return of this function? I submit they're both boxed. Of course, I admit I'm no expert and I'm fully ready to be instructed.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/The second case is not boxing because you are assigning a literal value to the object. A literal value essentially takes on the type of the object to which it is assigned (not the real implementation details, but it should get the idea across). That would mean that a literal integer value is only a value type if it is assigned to a value type. In the second example, the literal values are assigned to
object
variables, so no boxing occurs. Any time a boxing operation occurs, the value of the value type being boxed is copied into the reference type. As for what the second case is called, there isn't a name since there is no boxing or unboxing operations. It is simply variable assignment. In yourfunc
example, you have a function with twoout object
parameters. In the body of the function, you are essentially doing two things:- You create an
int
value type and then assign it to anobject
argument. This is a boxing operation. - You assign a literal value
2
to anobject
argument. Since you are assigning a literal value, this is not a boxing operation.
Scott Dorman
Microsoft® MVP - Visual C# | MCPD President - Tampa Bay IASA [Blog][Articles][Forum Guidelines]
Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai
- You create an
-
The second case is not boxing because you are assigning a literal value to the object. A literal value essentially takes on the type of the object to which it is assigned (not the real implementation details, but it should get the idea across). That would mean that a literal integer value is only a value type if it is assigned to a value type. In the second example, the literal values are assigned to
object
variables, so no boxing occurs. Any time a boxing operation occurs, the value of the value type being boxed is copied into the reference type. As for what the second case is called, there isn't a name since there is no boxing or unboxing operations. It is simply variable assignment. In yourfunc
example, you have a function with twoout object
parameters. In the body of the function, you are essentially doing two things:- You create an
int
value type and then assign it to anobject
argument. This is a boxing operation. - You assign a literal value
2
to anobject
argument. Since you are assigning a literal value, this is not a boxing operation.
Scott Dorman
Microsoft® MVP - Visual C# | MCPD President - Tampa Bay IASA [Blog][Articles][Forum Guidelines]
Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai
Thanks, Scott. That's very enlightening. Now I understand. Don't know why there should be a distinction in these cases, but what you're saying makes sense.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/ - You create an
-
In Scott's first case, a new Int32 object is created with a value. Assigning this object to another object causes boxing, because the RHS value type is not 'compatible' with the LHS ref type. In his second case,
object one = 1;
, once again a new Int32 object is created, but I assume that because it was created 'right there', buy the CLR, it can be directly assigned to the RHS. How far off the mark am I guys?Thanks Brady, yours and Scott's explanation helped a lot.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/ -
What makes you think that a == b and a.Equals(b) should be the same? They are by definition different, and specifically used to check different things, both of which are necessary. Reference equals (the == operator, or the more explicit Object.ReferenceEquals method) checks to see if two variables (or pointers, since you are dealing with reference types) point to the same object in memory, e.g., do they have the same address. Using a.Equals(b) is the same as Object.Equals(a, b), and it compares the values of the two objects. This is very well defined behavior, and most programming languages discern the difference between reference and value comparison. Just because you have not understood the problem, or better yet, the solution to the problem, does not mean that it is a design flaw. To assert that it is a design flaw it to also assert that you have a complete understanding of the language and the specification, and clearly, you do not.
That's exactly my point. I'm disagreeing with the "by definition" they are different part. Yes, by definition they are different. But, by reading comprehension and mathematical definition they should be semantically the same. Semantics should *always* override ease of the runtime or compiler. Make the code easy to understand and read and there will be less errors. Reference comparison should be the odd-man out and should be called-out by a specific operator or method call. So yes, I think it a design flaw. Just my humble opinion. And one does not have to understand or know the entire design to see a flaw in one area. The current design in this one spot strikes me as a hack.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/ -
That's exactly my point. I'm disagreeing with the "by definition" they are different part. Yes, by definition they are different. But, by reading comprehension and mathematical definition they should be semantically the same. Semantics should *always* override ease of the runtime or compiler. Make the code easy to understand and read and there will be less errors. Reference comparison should be the odd-man out and should be called-out by a specific operator or method call. So yes, I think it a design flaw. Just my humble opinion. And one does not have to understand or know the entire design to see a flaw in one area. The current design in this one spot strikes me as a hack.
Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/Fair enough, and well stated. I'm glad you made your original rant in the first place anyway, because as Tim Yen pointed out, I was incorrect about what types of equality checks were used in certain situations anyway. I was wrong, and now that I've been corrected, I'm a little smarter for it (thanks Tim). I am tending to agree with you now, that it would be better if there were simply two explicit checks. A non-overloadable reference-equals check, and an overloadable value-equals check.
-
ok, this is not a programming question. It's a rant! given,
object one = 0;
object two = 0;
bool same = one == two;what would you expect the value of
same
to be? WRONG! it's false! Whoever thought that was a valid result, is cracked!:mad::mad::mad::mad::mad: [edit] so, after going home and resting my brain a bit. it seems as though i'm the one that was cracked. thanks for the refresher course everyone. it is of course doing a reference comparison. which is correct. you all know how it is when you struggle with something and get too close to the trees to see the forest. anyway thanks to everyone for being your normally brutally honest selves. cheers. :-D [/edit]Fight Big Government:
http://obamacareclassaction.com/
http://obamacaretruth.org/modified on Friday, May 7, 2010 1:08 AM
ahmed zahmed ranted about == not measuring underlying boxed types in: http://www.codeproject.com/Lounge.aspx?msg=3464288#xx3464288xx
The mechanism is explained in the thread, but the more interesting question is why Hejlsberg et al. would design the language this way. Certainly, there are other languages that just bundle value and reference comparison into one operator. My first guess would be performance since it could be costly to have the compiler generate reflection checks to see if every object was an underlying boxed value type. The other thought is that comparison operators are always static (and can't polymorph). Again, that makes sense from a performance perspective as well since there would be no need to deref to a jump table for every == operation. Since there are some great scenarios for polymorphing comparison, there is the .Equals on every object. More overhead, but the choice to use the abstraction is in the hands of the coder. There are also languages that only do this (and hence stuff == into some kind of table). Anyone see any other reason than performance? Maybe 'cause Java did it? :-)