Why can't the C# compiler use the implicit cast of T to T? and call my extension method? [duplicate]

Implicit conversions are allowed for the target of extension method invocations, but they're restricted. From section 7.5.6.2 of the ECMA C# 5 standard:

An extension method Ci.Mj is eligible if:

  • ...
  • An implicit identity, reference or boxing conversion exists from expr to the type of the first parameter of Mj.

In your case, the conversion involved is not an identity, reference or boxing conversion, so the method is not eligible.

We use eligible mutations almost every time we use LINQ. For example:

List<string> names = new List<string> { "a", "b" };
IEnumerable<string> query = names.Select(x => x.ToUpper());

Here the target of the method is IEnumerable<T>, but the argument type is List<string>. T is inferred to be string, but there's still a conversion required from List<string> to IEnumerable<string>. That's permitted though, because it's a reference conversion.

I can see why the rule exists for reference types, at least. Suppose we had a mutable reference type X with an implicit conversion to another mutable reference type Y. An extension method targeting Y that mutated it would be very confusing, because it would presumably not mutate the original X. Extension methods are meant to "feel" like they're acting on the original value, and that isn't the case when conversions other than the ones listed are permitted.

Even boxing conversions feel slightly dubious to me. Here's an example, using a mutable struct that implements an interface:

using System;

public interface IMutable
{
    void Mutate();
}

public static class MutationExtensions
{
    public static void CallMutate(this IMutable target)
    {
        target.Mutate();
    }
}

public struct MutableStruct : IMutable
{
    public int value;

    public void Mutate()
    {
        value++;
    }
}

class Program
{
    static void Main()
    {
        MutableStruct x = new MutableStruct();
        Console.WriteLine(x.value); // 0
        x.Mutate();
        Console.WriteLine(x.value); // 1
        x.CallMutate();
        Console.WriteLine(x.value); // 1
    }
}

That last result (1, not 2) is because the value was boxed to be an IMutable, and only the box was modified - not the x variable.

I suspect corner cases like this were deemed "acceptably nasty" bearing in mind the benefit of being able to write extension methods for other interfaces that value types might implement, such as IFormattable. (Admittedly a generic method with a constraint on the type parameter would probably be a better idea there anyway.)