Общий параметр T неявно присваивается объекту - универсальный метод, вызывающий неуниверсальный перегруженный метод - PullRequest
0 голосов
/ 04 июня 2019

Почему System.Collections.Generic.IEnumerable<T> нельзя присвоить типу параметра System.Collections.Generic.IEnumerable<object>, учитывая, что object является базовым классом C #?


Я наткнулся на это любопытство, когда делал что-то похожее на следующий код. Это универсальный метод, вызывающий перегруженный неуниверсальный метод.

void Main()
{
    List<object> objects = new List<object>();

    Method(objects); // This calls the method with IEnumerable, as expected

    MethodCaller(objects); 
}

void MethodCaller<T> (IEnumerable<T> objects) 
{
    Method(objects); // Calls method overload 2, with object as parameter - why?

    // Why is the following line required to call the method overload 1? 
    // Can't C# do this automatically, given that object is the ultimate base class for everything?
    IEnumerable<object> casted = (IEnumerable<object>) objects; 
    Method(casted); // Calls method overload 1
}

void Method (IEnumerable<object> param) 
{
    // Method overload 1
    Console.WriteLine("Method overload 1 - with IEnumerable<object> as parameter");
}

void Method (object param) 
{
    // Method overload 2
    Console.WriteLine("Method overload 2 - with object as parameter");
}

Я не понимаю, почему универсальный метод не должен вызывать первую перегрузку вместо второй. Я думал, что компилятор должен быть в состоянии сказать, что любой <T> может быть неявно приведен к object, поэтому IEnumerable<T> должен быть неявно приведен к IEnumerable<object>.

Другими словами:

IEnumerable<object> casted = (IEnumerable<object>) objects; 

Почему эта строка требуется для вызова перегрузки метода 1? Разве C # не может сделать это автоматически, учитывая, что этот объект является основным базовым классом?

Это потому, что C # предполагает, что я могу передать <T>, который не совместим с типом object - даже если все на самом деле object?

Ответы [ 2 ]

5 голосов
/ 04 июня 2019

Давайте возьмем разрешение перегрузки из уравнения здесь.Это примерно общая дисперсия .В частности, вы ожидаете, что будет неявное преобразование из IEnumerable<T> в IEnumerable<object>.

, которое не работает, потому что универсальная дисперсия работает только тогда, когда известно, что аргументы типа являются ссылочными типами.Из связанной документации:

Дисперсия применяется только к ссылочным типам;если указать тип значения для параметра типа варианта, этот параметр типа будет инвариантным для результирующего составного типа.

Так, например, это нормально:

IEnumerable<string> strings = ...;
IEnumerable<object> objects = strings;

Ноэто терпит неудачу:

IEnumerable<int> ints = ...;
IEnumerable<object> objects = ints;

В вашем общем случае T может быть любого типа , включая тип значения .Вот почему это не удается.Если вы ограничите T ссылочным типом, используя ограничение where T : class, это нормально.

Итак, если быть конкретным, это недопустимо:

static void Foo<T>(IEnumerable<T> ts)
{
    IEnumerable<object> objects = ts;
}

Но это действительно так:

static void Foo<T>(IEnumerable<T> ts) where T : class
{
    IEnumerable<object> objects = ts;
}
0 голосов
/ 04 июня 2019

IEnumerable <объект> не является суперклассом IEnumerable (каким бы ни был T). Вы не можете назначить второй переменной первого типа, так как они считаются совершенно разными. Вам нужно привести тип вашего IEnumerable к объекту, прежде чем вы получите соответствующую подпись, например:

void MethodCaller<T> (IEnumerable<T> objects) 
{
    Method(objects.Cast<object>()); 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...