I have this extension method:
public static bool In<T>(this T source, params T[] list)
{
return list.Contains(source);
}
Now I need to use the above method for a ushort
. When I try
ushort p = 3;
if (p.In(1, 2, 3, 4, 5))
return;
The first line casts 3
to a ushort
well. But when 3
is passed as a parameter, I get the error
'ushort' does not contain a definition for 'In' and the best extension method overload 'Extensions.In(T, params T[])' has some invalid arguments.
But this works:
ushort p = 3;
if (Extensions.In(p, 1, 2, 3, 4, 5))
return;
which is weird.
Why does it work with the second example, but not the first?
What's a good alternative that would help me here? Since there are no literals for short
or ushort
I'm failing to find a simpler alternative than manually casting each integer like this:
ushort p = 3;
if (p.In((ushort)1, (ushort)2, (ushort)3, (ushort)4, (ushort)5))
return;
Why does it work with second example, but not first?
First, let's work out what the compiler infers T
to be. Some parameters are ushort
and some are int
. ushort
has an implicit conversion to int
and int
does not have an implicit conversion to ushort
, so T
is int
.
The key here is in section 7.6.5.2 of the C# 4 specification (emphasis mine):
An extension method Ci.Mj is eligible if:
- Ci is a non-generic, non-nested class
- The name of Mj is identifier
- Mj is accessible and applicable when applied to the arguments as a static method as shown above
- An implicit identity, reference or boxing conversion exists from expr to the type of the first parameter of Mj.
There is an implicit conversion from ushort
to int
, but not an identity, reference, or boxing conversion!
So, the following are legal:
Extensions.In<ushort>(p, 1, 2, 3, 4, 5);
Extensions.In<int>(p, 1, 2, 3, 4, 5); // implicit conversion allowed
Extensions.In(p, 1, 2, 3, 4, 5); // T is int
p.In<ushort>(1, 2, 3, 4, 5);
but the following are not:
p.In<int>(1, 2, 3, 4, 5); // implicit conversion not allowed
p.In(1, 2, 3, 4, 5); // T is int
Note that you can get the same error without generics if you define In
as
public static bool In(this int source, params int[] list)
{
return list.Contains(source);
}