C#扩展方法编译/兼容性检查基于命名空间的顺序失败

Mic*_*ker 5 c# generics extension-methods

我正在写两种扩展方法.一个用于处理单个对象,另一个用于处理对象集合.在调用扩展方法时,C#编译器似乎对使用哪一个感到困惑,并且编译失败.

更令人惊讶的是,如果我将扩展方法移动到不同的命名空间,即使我在调用点中包含两个命名空间,只有在命名空间按特定顺序按字母顺序进行编译时才会失败 - 切换命名空间会导致编译成功.

这是代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是编译失败的callsite行:

List<ITable> list = ... // ITable inherits from IDBObject
list.PopulateRelations(xxx);
Run Code Online (Sandbox Code Playgroud)

失败,错误CS0311:

类型'System.Collections.Generic.List'不能用作泛型类型或方法'Granta.MI.DBObjectExtensions.PopulateRelations(T,params Granta.MI.RelationToPrefetch [])'中的类型参数'T'.没有从'System.Collections.Generic.List'到'Granta.MI.IDBObject'的隐式引用转换.

请注意,如果我删除第二个扩展方法,此行将成功编译.

另请注意,编写trampoline方法(对于每种可能的集合类型......)也有效:

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);
}
Run Code Online (Sandbox Code Playgroud)

为什么编译器无法找出匹配的扩展方法?更令人困惑的是,如果我将其中一个方法放在不同的命名空间中,并且我包含该命名空间,为什么编译会成功呢?有什么办法可以解决这个问题吗?

Mat*_*ili 2

通用约束不是方法签名的一部分,因此编译器选择它是PopulateRelations<T>(this T obj, params RelationToPrefetch[] relationsToPrefetch)因为T比 更派生IEnumerable<T>

例如,这两种方法之间:

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
}
Run Code Online (Sandbox Code Playgroud)

调用时选择第一个:

List<ITable> list;
PopulateRelations(list, something); // Not calling as extension method to more clear
Run Code Online (Sandbox Code Playgroud)

因为list直接匹配List<ITable>