I am writing two extension methods. One to work on a single object, and another to work on a collection of objects. When calling the extension method, the C# compiler seems to get confused as to which one to use, and fails compilation.
More surprisingly, if I move the extension methods to different namespaces, even if I include both namespaces in the callsite, compilation only fails if the namespaces are in a particular order alphabetically - switching the namespaces causes compilation to succeed.
Here is the code:
public static class DBObjectExtensions
{
    public static void PopulateRelations<T>(this T obj, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject
    {
        if (obj == null)
        {
            return;
        }
        obj.Transaction.PopulateRelations<T>(new[]{ obj }, relationsToPrefetch);
    }
    public static void PopulateRelations<T>(this IEnumerable<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject
    {
        var first = objects.FirstOrDefault();
        if (first == null)
        {
            return;
        }
        first.Transaction.PopulateRelations<T>(objects, relationsToPrefetch);
    }
}
This is the callsite line that fails compilation:
List<ITable> list = ... // ITable inherits from IDBObject
list.PopulateRelations(xxx);
Fails with error CS0311:
The type 'System.Collections.Generic.List' cannot be used as type parameter 'T' in the generic type or method 'Granta.MI.DBObjectExtensions.PopulateRelations(T, params Granta.MI.RelationToPrefetch[])'. There is no implicit reference conversion from 'System.Collections.Generic.List' to 'Granta.MI.IDBObject'.
Note that this line succeeds compilation if I delete the 2nd extension method.
Also note that writing trampoline methods (for every possible type of collection...) also works:
public static void PopulateRelations<T>(this List<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject
{
    ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch);
}
public static void PopulateRelations<T>(this IList<T> objects, params RelationToPrefetch[] relationsToPrefetch) where T : IDBObject
{
    ((IEnumerable<T>)objects).PopulateRelations(relationsToPrefetch);
}
Why can't the compiler figure out there is a matching extension method? And more confusingly, if I put one of the methods in a different namespace, and I include that namespace, why does compilation then succeed? Is there anything I can do to fix this?
Generic constraints are not part of the method signature, so the compiler choose PopulateRelations<T>(this T obj, params RelationToPrefetch[] relationsToPrefetch) because T is more derived than IEnumerable<T>.
Example, between these 2 methods:
public static void PopulateRelations(this List<ITable> obj, params RelationToPrefetch[] relationsToPrefetch)
{
    // Do something
}
public static void PopulateRelations(this IEnumerable<ITable> objects, params RelationToPrefetch[] relationsToPrefetch)
{
    // Do something
}
The first one is chosen when calling:
List<ITable> list;
PopulateRelations(list, something); // Not calling as extension method to more clear
Because list matches directly with List<ITable> 
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With