2

Here's the code

using System; public class WrappedInt { private int Value { get; } public WrappedInt(int value) { Value = value; } // Comparison in Main works when this is defined: public static implicit operator int(WrappedInt wInt) => wInt.Value; // But doesn't work with this INSTEAD: // public static implicit operator WrappedInt(int value) => new WrappedInt(value); // Error: Operator '==' cannot be applied to operands of type 'int' and 'WrappedInt' } public class Program { public static void Main() { var a = 2; var b = new WrappedInt(2); var areSame = a == b; Console.WriteLine(areSame); } } 

Switching a == b to b == a doesn't resolve the error, so it's not about the order. I guess, it has something to do with int being a primitive type.

Post-thought 1

Interestingly, the code below where I compare WrappedInt1 with WrappedInt2 works, even though those objects are implicitly convertible only to int. It's like the == operator converts those wrapper-objects to ints and then compares ints.

using System; public class WrappedInt1 { private int Value { get; } public WrappedInt1(int value) { Value = value; } public static implicit operator int(WrappedInt1 wInt) => wInt.Value; } public class WrappedInt2 { private int Value { get; } public WrappedInt2(int value) { Value = value; } public static implicit operator int(WrappedInt2 wInt) => wInt.Value; } public class Program { public static void Main() { var a = new WrappedInt1(2); var b = new WrappedInt2(2); var areSame = a == b; Console.WriteLine(areSame); } } 

Post-thought 2

int indeed seems special when it comes to the == operator because the code below, where I try to compare WrappedInts having only implicit casts to and from another type of WrappedInt, doesn't compile.

using System; public class WrappedInt1 { public int Value { get; } public WrappedInt1(int value) { Value = value; } public static implicit operator WrappedInt2(WrappedInt1 wInt) => new WrappedInt2(wInt.Value); public static implicit operator WrappedInt1(WrappedInt2 wInt) => new WrappedInt1(wInt.Value); } public class WrappedInt2 { public int Value { get; } public WrappedInt2(int value) { Value = value; } public static implicit operator WrappedInt2(WrappedInt1 wInt) => new WrappedInt2(wInt.Value); public static implicit operator WrappedInt1(WrappedInt2 wInt) => new WrappedInt1(wInt.Value); } public class Program { public static void Main() { var a = new WrappedInt1(2); var b = new WrappedInt2(2); var areSame = a == b; Console.WriteLine(areSame); } } 
4
  • 2
    When you're wrapping a value, the conversion operator that returns the wrapped value is a narrowing operation so it should be explicit. Conversions should only have an implicit operator going one way (if any), as that's the widening operation so it has the chance to be safe to implicitly convert. Currently, your implementation does not apply best practices for conversion operators. Commented Jul 3, 2023 at 17:17
  • @madreflection Huh, that's brilliant! Thank you Commented Jul 3, 2023 at 17:19
  • @madreflection Although, it still doesn't explain the issue. I added another code example where I compare different classes wrapping an integer. Apparently, the compiler is fine with implicitly casting WrappedIntegers to ints. And removing implicit operator from either of those WrappedIntegers causes the same "can't be compared" compile error. Commented Jul 3, 2023 at 17:30
  • That still doesn't fix the design problem of implicitly narrowing, so you should take a step back and deal with that before continuing. Commented Jul 3, 2023 at 17:33

1 Answer 1

3

Your alternative doesn't work because your class doesn't define the == operator. The compiler cannot find a "WrappedInt x WrappedInt" version of ==, only one for "int x int", and "object x object".

To make it work as you want, you have a few options:

  1. Implement the == operator yourself in the WrappedInt class. (e.g. with no null checking... note that it will also prompt you to implement != as well):
 public static bool operator ==(WrappedInt a, WrappedInt b) { return a.Value == b.Value; } public static bool operator !=(WrappedInt a, WrappedInt b) { return a.Value != b.Value; } 
  1. Convert WrappedInt into a record class (or record struct for that matter), which implements the operator for you
Sign up to request clarification or add additional context in comments.

3 Comments

Huh, so obvious! You're right. I wonder how C# chooses the type to which to cast when both classes implement ==
@m_ocean: It follows the rules of the standard, e.g. github.com/dotnet/csharpstandard/blob/draft-v7/standard/…: "If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator «op» implementations, including their lifted forms, become the set of candidate operators for the operation."
@JonSkeet Indeed, if I implement those two classes "symmetrically", then this error occurs: Operator '==' is ambiguous on operands of type 'WrappedInt1' and 'WrappedInt2'

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.