Ambiguous call between two C# extension generic methods one where T:class and other where T:struct

后端 未结 3 1620
小蘑菇
小蘑菇 2020-11-30 21:59

Consider two extension methods:

public static T MyExtension(this T o) where T:class
public static T MyExtension(this T o) where T:struct
         


        
3条回答
  •  独厮守ぢ
    2020-11-30 22:34

    EDIT: I've now blogged about this in more detail.


    My original (and I now believe incorrect) thought: generic constraints aren't taken into account during the overload resolution and type inference phases - they're only used to validate the result of the overload resolution.

    EDIT: Okay, after a lot of going round on this, I think I'm there. Basically my first thought was almost correct.

    Generic type constraints only act to remove methods from a candidate set in a very limited set of circumstances... in particular, only when the type of a parameter itself is generic; not just a type parameter, but a generic type which uses a generic type parameter. At that point, it's the constraints on the type parameters of the generic type which are validated, not the constraints on the type parameters of the generic method you're calling.

    For example:

    // Constraint won't be considered when building the candidate set
    void Foo(T value) where T : struct
    
    // The constraint *we express* won't be considered when building the candidate
    // set, but then constraint on Nullable will
    void Foo(Nullable value) where T : struct
    

    So if you try to call Foo(null) the above method won't be part of the candidate set, because Nullable value fails to satisfy the constraints of Nullable. If there are any other applicable methods, the call could still succeed.

    Now in the case above, the constraints are exactly the same... but they needn't be. For example, consider:

    class Factory where TItem : new()
    
    void Foo(Factory factory) where T : struct
    

    If you try to call Foo(null), the method will still be part of the candidate set - because when TItem is object, the constraint expressed in Factory still holds, and that's what's checked when building up the candidate set. If this turns out to be the best method, it will then fail validation later, near the end of 7.6.5.1:

    If the best method is a generic method, the type arguments (supplied or inferred) are checked against the constraints (§4.4.4) declared on the generic method. If any type argument does not satisfy the corresponding constraint(s) on the type parameter, a binding-time error occurs.

    Eric's blog post contains more detail on this.

    提交回复
    热议问题