Open GrabYourPitchforks opened 5 years ago
Have you considered using if (myString is null)
?
@yaakov-h This isn't for my code; I can always jump through whatever hoops are necessary within my own library. It's for .NET consumers at large, most of whom are used to writing if (x == null)
and having it just work.
The ==
operator is supposed to be symmetric.
Writing foo == bar
should always give exactly the same result as bar == foo
.
I'd therefore suggest that you keep this operator:
public static bool operator ==(Utf8String left, Utf8String right);
and ditch this one:
public static bool operator ==(Utf8String left, string right);
Instead, implement IEquatable<string>
with bool Equals(string value)
and you get to have your 🍰 cake and eat it too.
Writing
foo == bar
should always give exactly the same result asbar == foo
.
@theunrepentantgeek Except one case. If bar
is a subclass of foo
, they won't give you the same result.
@theunrepentantgeek There was a symmetric operator ==
and corresponding operator !=
that I didn't list for brevity, so foo == bar
and bar == foo
should still give the same result. Recall: the purpose of this isn't for the code that I will be writing internal to the library. I'll perform whatever gyrations are necessary internally. The purpose is so that consumers of the library can write natural syntax and get the results they expect.
Overload resolution could prefer the matching type to resolve the ambiguity?
e.g.
// preferred for Utf8String == null or null == Utf8String
public static bool operator ==(Utf8String left, Utf8String right);
public static bool operator ==(Utf8String left, string right);
// preferred for string == null or null == string
public static bool operator ==(string left, string right);
public static bool operator ==(string left, Utf8String right);
And if there is no overload for ==(ThisType a, ThisType b)
then the ambiguity remains?
@yaakov-h not sure I understand; the ambiguity exists between the matching types and the alternative type. If there was only one there wouldn't be any ambiguity
If you had 2 equality operators of different types; then yes the ambiguity remains e.g.
public static bool operator ==(Utf8String left, string right);
public static bool operator ==(Utf8String left, ThisType right);
// Utf8String == null ambiguous
But then you could add a matching type and it would win the tie break
public static bool operator ==(Utf8String left, string right);
public static bool operator ==(Utf8String left, ThisType right);
public static bool operator ==(Utf8String left, Utf8String right);
// Utf8String == null; matches Utf8String == Utf8String
@benaadams's suggestion to prefer the Utf8String == Utf8String
overload was my natural reaction to this problem.
@yaakov-h I highly doubt that there are many types which overload T == U
and T == V
, but not T == T
.
@PathogenDavid possibly not, but I do love edge cases.
Also consider the Null obejct pattern and have a Utf8String.Null which can be leveraged although that won't stop the ambiguity which is caused by having two overloads and preventing generic null from being used without a cast.
You could drop the 2nd overload as that is what causes it in general but that won't get you to the overload you need...
Instead perhaps an implicit cast to string could be provided but I can't see how that wouldn't need to alloc.
Perhaps an implicit cast to ReadOnlySpan byte would be more suited and then combining that with the null object pattern works but I've jumped through hoops.
Tldr;
I really wish I could special case null in my operators explicitly rather than checking for them as it would lend itself to defaults I could define as well as solve issues like this..
public static explicit operator null(Type type) { return Type.Null; }
But I do this today with object.ReferenceEquals
Even without the Type overloads just the null explicitly would be useful there imho with type support being an enhancement later.
Would also be nice to have a way to override is and default as well but that's another story?
public static explicit operator default
public static explicit operator is boolean
You get around the generics by using the Type syntax above but I think it needs refinement for sure since you would want to control what your returning and when to do different things in different scenarios.... but then again... leveraging contravariance there could also be useful...
Another possibly simpler way to solve the problem originally described here would be to introduce an attribute [ReferentialNullEquality]
(did I mention I'm terrible at naming?) in the System.Runtime.CompilerServices namespace. Essentially, if the compiler sees a call to operator ==
on a type that is annotated with such an attribute, and if either argument to the equality operator is known to be null, then the compiler would emit a referential equality check instead of calling the operator ==
or operator !=
methods.
Problem
In the
Utf8String
prototype I've defined twooperator ==
methods:However, now simple equality / inequality comparisons against null will not properly compile, as the statement
if (myUtf8String == null)
is an ambiguous call between the two methods, since null matches both signatures.It would be nice if I could define an
operator ==
overload which would explicitly match null, so the compiler would no longer see an ambiguity given the code snippet above.There's an additional benefit to this proposal. Say that I didn't have the string-based overload of
operator ==
and there was no ambiguity to begin with. Even if one side of the operator were a literal null, the compiler still emits a call to the normaloperator ==
method, and it would still go through unoptimized code paths before determining that it can early exit. By having a null-specificoperator ==
overload, the type author can write the simplest possible implementation of the method. It'll give behavior similar to the special support the C# compiler has today forif (myString == null)
, but without requiring the compiler to special-case particular types.