Подтвердить что ты не робот

Почему IEnumerable (of T) не принимается в качестве приемника метода расширения

Задайте вопрос перед кодом:

Почему IEnumerable<T> where T : ITest не принимается в качестве приемника метода расширения, который ожидает this IEnumerable<ITest>?

И теперь код:

У меня есть три типа:

public interface ITest { }
public class Element : ITest { }
public class ElementInfo : ITest { }

И два метода расширения:

public static class Extensions
{
    public static IEnumerable<ElementInfo> Method<T>(
        this IEnumerable<T> collection) 
        where T : ITest
    {
→        return collection.ToInfoObjects();
    }

    public static IEnumerable<ElementInfo> ToInfoObjects(
        this IEnumerable<ITest> collection)
    {
        return collection.Select(item => new ElementInfo());
    }
}

Ошибка компилятора, которую я получаю (по выделенной строке):

CS1929: 'IEnumerable<T>' не содержит определения для 'ToInfoObjects', а лучший метод перегрузки 'Extensions.ToInfoObjects(IEnumerable<ITest>)' требует приемника типа 'IEnumerable<ITest>'

Почему это так? Получателем метода расширения ToInfoObjects является IEnumerable<T> и ограничение общего типа, T должно реализовать ITest.

Почему тогда приемник не принят? Мое предположение - это ковариация IEnumerable<T>, но я не уверен.

Если я изменю ToInfoObjects, чтобы получить IEnumerable<T> where T : ITest, тогда все будет в порядке.

4b9b3361

Ответ 1

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

public struct ValueElement : ITest { }

и это:

IEnumerable<ValueElement> collection = ...
collection.Method(); //OK, ValueElement implement ITest, as required.
collection.ToInfoObjects() //Error, IEnumerable<ValueElement> is not IEnumerable<ITest>
                           //variance does not work with value types.

Таким образом, для ToInfoObjects также допускается не каждый тип, разрешенный для Method. Если вы добавите ограничение class в T в Method, тогда ваш код будет скомпилирован.

Ответ 2

Вы можете сделать следующее:

    public static IEnumerable<ElementInfo> Method<T>(
        this IEnumerable<T> collection)
        where T : ITest
    {
        return collection.ToInfoObjects();
    }

    public static IEnumerable<ElementInfo> ToInfoObjects<T>(
        this IEnumerable<T> collection)
    {
        return collection.Select(item => new ElementInfo());
    }

Уведомление о ToInfoObjects.