Компилятор С# выбирает неправильный метод расширения

Рассмотрим этот код:

using System.Linq;

namespace ExtensionMethodIssue
{
    static class Program
    {
        static void Main(string[] args)
        {
            var a = new[] { 1 };
            var b = new[] { 1, 2 }.Where(a.Contains).ToList();
            var c = new[] { 1, 2 }.Where(i => a.Contains(i)).ToList();
        }
    }
}

Код успешно компилируется. Затем я добавляю пакет nuget "itext7 7.0.4", и теперь компиляция терпит неудачу из-за:

//Error CS0122: 'KernelExtensions.Contains<TKey, TValue>(IDictionary<TKey, TValue>, TKey)' is inaccessible due to its protection level
var b = new[] { 1, 2, 3 }.Where(a.Contains).ToList();

// This is still ok.
var c = new[] { 1, 2, 3 }.Where(i => a.Contains(i)).ToList();

Причина в том, что библиотека itext7 имеет внутренний класс с методами расширения в глобальном пространстве имен (вот оно).

internal static class KernelExtensions {
    public static bool Contains<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) {
        return dictionary.ContainsKey(key);
}
}

По какой-то причине компилятор выбирает недопустимый метод расширения с несовместимой сигнатурой из глобального пространства имен вместо доступного метода расширения с совместимой сигнатурой из пространства имен LINQ.

Вопрос в следующем: такое поведение ожидается в терминах спецификации языка или это ошибка в компиляторе? И почему это происходит только в случае с "группой методов" и все еще работает с i => a.Contains(i)?

Ответы

Ответ 1

Это ошибка компилятора, недоступная функция не должна влиять на разрешение перегрузки.

Как общее обходное решение, используйте лямбды в качестве аргумента вместо групп методов, потому что разрешение перегрузки, похоже, отлично работает с ними. Не очевидно, что быстрее или эффективнее в вашем конкретном сценарии, при необходимости оптимизируйте его с помощью соответствующих показателей производительности.

В этом конкретном случае вы также можете использовать другие методы расширения, такие как Enumerable.Intersect() если вы работаете с наборами и не интересуетесь дубликатами, Enumerable.Join() или простой цикл.

Для получения дополнительной информации проверьте: