Assert.Equal trying to cast ushort? to 'string'

140 views Asked by At

When a ushort? is passed to Assert.Equal it is trying to cast the two argumants to string. It works fine for int?. If I change the code to ushort test = 10; or Assert.Equal((ushort)10, test); or Assert.Equal(10, (ushort)test); it works fine. Whats the reason for that? Can it be because of ushort and both char is 2 bytes and Assert.Equal is implicitly trying to treat the ushort? as a string?

enter image description here

1

There are 1 answers

5
Sweeper On

Note that although there is an implicit conversion from 10 to ushort?

ushort? foo = 10;

This only works because the constant expression 10 is within the range of ushort, and an implicit constant expression conversion is applied.


The Equal method group has generic methods, like the one that you are trying to call. So before overload resolution can begin, type inference occurs.

Type inference occurs as part of the binding-time processing of a method invocation and takes place before the overload resolution step of the invocation. When a particular method group is specified in a method invocation, and no type arguments are specified as part of the method invocation, type inference is applied to each generic method in the method group. [...] If type inference for a particular method fails, that method does not participate in overload resolution.

In this case, type inference actually fails for all the generic overloads, so they don't take part in overload resolution. Equal(string, string) is one of the overloads that do. That's why the error message is about the string overload.

Why does type inference fail? Because there is no implicit conversion from int to ushort? or from ushort? to int. Type inference only cares about the types of the arguments, the parameters, and the type parameters. It doesn't care about whether the arguments are constant expressions or anything like that, so the aforementioned implicit constant expression conversion is not applicable. I encourage you to follow through the type inference process in the spec. It's great fun :)

See the references to "type of Ei" here:

For each of the method arguments Ei:

  • [...]
  • Otherwise, if Ei has a type U and xi is a value parameter then a lower-bound inference is made from U to Ti.
  • [...]

Compare this to how it refers to "argument" rather than "type of argument" in overload resolution:

For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical to the parameter passing mode of the corresponding parameter, and

  • for a value parameter or a parameter array, an implicit conversion exists from the argument to the type of the corresponding parameter, or
  • [...]

So it seems that overload resolution does look at the arguments' expressions, which is why it would have worked if you made the method non-generic, accepting two ushort?. In that case it does not participate in type inference, only overload resolution.

The reason why casting the 10 to ushort or byte works is because there does exist an implicit conversion between from ushort to ushort? and from byte to ushort?. This is done via an implicit nullable conversion.