Currently for a reference equality check you have to cast back to object
or use ReferenceEquals
, e.g.
if (object.ReferenceEquals(a, b)) {
return true;
}
if (((object)a == null) || ((object)b == null)) {
return false;
}
I want to suggest ===
and !==
operators for reference equality, so the above code could be written like,
if (a === b) {
return true;
}
if (a === null || b === null) {
return false;
}
This pattern is not just specific to Equals
implementation, as you can see in the Roslyn codebase itself.
==
is overridden or not. And since ===
is not overridable, it won't be changed if you later override the ==
operator.These operators would be identical to Is
and IsNot
in VB.
Great! We don't have enough ways to compare objects in C#, let's add some more.
@stepanbenes It doesn't _add some more_ it just makes it easier and clears the intention. Because it's there, and it'll be useful.
Unfortunately I am not Stephen Toub. :) However, do not misinterpret me, I like your proposal, I just don't like if there is more than one way to express some operation in C#. I wish the ==
equality operator would not be implicitly defined on object, It just brings confusion when it is overriden.
Unfortunately I am not Stephen Toub. :)
:smile:
@stepanbenes Note that it's not the _same operation_. If you're going to question the ability to override the equality operator in C# then all of our lives have no meaning!
@stephentoub I didn't even notice :smile:
@alrz It's syntactic sugar for static method ReferenceEquals, isn't it? I don't question the ability to override the equality operator, on the contrary, I question the existence of its implicit implementation resulting in reference comparison.
@alrz You don't have to edit your comments, they are funny sometimes. :)
@stepanbenes (Now I double checked, anyway)
I question the existence of its implicit implementation resulting in reference comparison.
You mean reference comparison should have been the default? Then what if I override the ==
operator?
@alrz No, I mean that using ==
operator on reference types without explicit user implementation of ==
operator should result in compile time error. However, it is too late for this discussion...
@alzr I mean that reference comparison and data content comparison should be strictly separated.
reference comparison and data content comparison should be strictly separated.
That's what this proposal is about, you still can do the same with ==
though, but if we had another operator, then it wouldn't be idiomatic, and you will know that what was the intention of the developer.
@alzr Yes, I support your proposal. It could be combined with some analyzer that finds the misuses of ==
operator and offers to convert them to new ===
operator
@alrz On the other hand, it brings new potential source for bugs. Simple typo that can be easily overlooked can lead to very different comparison semantics, that is hard to debug, same as in present days.
:-1:
object.ReferenceEquals()
is already crystal clear. We don't need another operator for a rarely used operation.
@MgSam Re "rarely used"
Just 936 cast backs to object
and more than 800 for ReferenceEquals
in Roslyn, doesn't that count?
Since it doesn't make sense to compare value types to null
, as @CyrusNajmabadi pointed out, it'd be nice to make it a compiler error if T
were a value type.
搂7.10.6
The
x == null
construct is permitted even thoughT
could represent a value type, and the result is simply defined to befalse
whenT
is a value type.
I'll update the opening post to mention this.
I would definitely like to see this. It's really unpleasant today to get reference equality. As we can see from our own code it would be quite nice to be able to have a way to explicitly state this with simple syntax (though it's not sure this is important enough).
As for the "Simple typo" argument. My anecdotal experience is that this isn't a problem. Specifically, in Javascript/TypeScript ==
and ===
are both potential operators (though, imo, only one of them is actually a sensible operator). From a syntactic perspective it was not a problem having both of these and it was trivial to catch when someone used the wrong operator.
So, overall i approve of this facility. Though i'm not sure it's important enough to warrant making the change. While unpleasant, the two workarounds we have today are probably good enough.
@alrz Two points:
Equals
methods? @CyrusNajmabadi I totally disagree. Every time I see JavaScript code that hasn't gone through a linter/compiler ==
is mistakenly used all over the place. It is incredibly easy to make this typo and have it be a silent bug just waiting to rear its head.
I'm a full time C# dev and I can probably count on one hand the number of times I've seen code that does reference equality outside of auto-generated Equals or EqualityComparer implementations (which don't need to be simplified- they're already auto-generated).
@MgSam (1) Perhaps less than other places. (2) If you're going to question that, I'd say ADTs are not quite useful for a "typical" project either and Roslyn itself would be a major beneficiary.
I'd be much better served by an analyzer which would warn if I accidentally used ==
within an overloaded ==
operator. Honestly that's probably the only time I break out object.ReferenceEquals
and I've been bitten once or twice by null
checks that end up in infinite recursion.
@HaloFour I didn't want to address this particular problem with ==
, what I'm proposing is a dedicated operator for reference equality, which already exists in VB.
I think the code
if (object.ReferenceEquals(a, b)) {
return true;
}
if (((object)a == null) || ((object)b == null)) {
return false;
}
usually can be replaced to
return Object.Equals(a, b);
or just
return Equals(a, b);
and this is simpler and looks better for me than new operator.
In cases when I need comparing object references (not rarely!) I use ReferenceEquals(x, y)
and it great in my view.
I think It simple to mix up ===
and ==
in code review and debugging.
In C# 7, if you're just checking for null, you'll be able to write
if (a is null || b is null)
I think I prefer ReferenceEquals(a, b)
to a === b
. I use ReferenceEquals
not infrequently and I think the code reads very clearly this way. If typing is the issue, a code snippet could help.
I just don't want to be forever squinting at ==
and ===
and second guessing. Or C# to feel like JavaScript. 馃槅
@gafter If that's the intented usage, I think there is no room for this proposal. Nobody wants three ways of doing the same thing. Perhaps we could just produce warnings on invalid usage of ReferenceEquals e.g. with value types.
Specifically, in Javascript/TypeScript == and === are both potential operators (though, imo, only one of them is actually a sensible operator). From a syntactic perspective it was not a problem having both of these and it was trivial to catch when someone used the wrong operator.
@CyrusNajmabadi
The problem is that in your opinion, in that of many (most?) JavaScript experts ===
is in fact the only sensible operator. Therefore it is, as you say trivially easy to catch these errors because I ban ==
in my TypeScript/JavaScript. Whenever I read code in these languages that uses ==
, I have to think very carefully about changing it. For example Dates
in JavaScript are an absolute nightmare scenario.
But in C# we have operator overloading which lets us raise the level of abstraction. Of course we have static type safety and analysis tools that error/warn on incorrect usage and, while we would not lose that by adding the proposed ===
== ReferenceEquals
shorthand, we would bring the two forms closer syntactically together. It wouldn't be a disaster but I do not see the value.
@stepanbenes
No, I mean that using == operator on reference types without explicit user implementation of == operator should result in compile time error. However, it is too late for this discussion...
Yeah I suppose the ship has sailed, but I don't think that would really be desirable. What would be desirable, IMO, is to be able to specify a virtual ==
. This is a huge can of worms but it maintains the syntactic, analogic abstraction of ==
and would allow its behavior to be customized symmetrically.
What would be desirable, IMO, is to be able to specify a virtual
==
How would that be different from ==
invoking a virtual Equals
?
@jnm2 it would be exactly the same as invoking Equals
. I don't want to derail this discussion
```C#
((object)1).Equals((object)1) != 1 == 1;
@gafter
> In C# 7, if you're just checking for null, you'll be able to write
>```C#
>if (a is null || b is null)
>```
Just so long as you don't write
```C#
null is whatever
Because that brings down Visual Studio without fail. I assume this has been reported.
@aluanhaddad Just curious what that gains you that you can't already achieve by having ==
actually call Equals
.
In C# 7, if you're just checking for null, you'll be able to write
if (a is null || b is null)
@gafter Is there any benefit to doing that? Is it not equivalent to if(a == null || b == null)
?
@MgSam It's equivalent to (object)e == null
, I suppose.
@alrz Does that have any semantic difference to e == null
?
@MgSam It does merely reference comparison, while e == null
calls the operator.
Right, so in any sane implementation of ==
there is no difference.
Right, so in any sane implementation of == there is no difference.
Unless you really only want to test null
and want to avoid unnecessary unused additional comparison costs. @gafter suggests value is null
will do just this in C#, so this thread is basically moot.
@whoisj Note that is
won't work when you want to compare two expressions for reference equality, e.g. e1 is e2
because expressions are not allowed in patterns. Therefore, you still need to write (object)e1 == (object)e2
or object.ReferenceEquals(e1, e2)
. In both cases there would be no compiler-time warning if either e1
or e2
is of a value type.
@alrz interesting point. I'd be happy to have a de-reference operator for comparisons, or a compare references/address operator. Either way, having something to avoid needlessly calling into costly compare routines when all one cares about is reference equality.
Not sure if there is any ambiguity in allowing simple expressions (e.g. variable ref or property access) in patterns. In that case we can reuse is
for this purpose and it covers almost all use cases for reference equality check.
if (a is b) {
return true;
}
if (a is null || b is null) {
return false;
}
This is basically VB syntax for the same operation.
I think a is b
is the best possible choice over anything I've seen, if it's still open to us.
I think that would be ambiguous. What if b is a type and a value?
@aluanhaddad Currently it is actually allowed but rhs requires to be a constant. The compiler simply prefer the type to retain backward compatibility.
class C {
bool M(object o) {
const int C = 5;
return o is C; // this is a type check
}
}
Renaming the type would cause it to be bound to the constant. However, there is a chance that constant patterns would be disallowed in the rhs of the is operator (#15539).
Anyways, so it is possible for the compiler to resolve the ambiguity in case of an identifier.
@alrz that makes complete sense, thank you very much for the explanation.
Most helpful comment
:-1:
object.ReferenceEquals()
is already crystal clear. We don't need another operator for a rarely used operation.