Вывод типа над IEnumerable <T> - PullRequest
2 голосов
/ 02 декабря 2009

Есть несколько сообщений в переполнении стека об этом, но не точно так же, поэтому заранее приносим извинения, если это то, на что уже был дан ответ.

Почему это не работает:

public class MyBase { }

public class MyUtils
{
    public bool Foo<T> (T myObject) { return true; }
    public bool Foo (MyBase myBaseObject) { return false; }

    public void Go<T> (IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            // this test fails
            Assert.IsFalse (Foo (item));
        }
    }
}

Если я вызову Go () выше и передам загрузку объектов MyBase, каждый вызов Foo вызовет универсальный Foo (), который возвращает true.

new MyUtils ().Go (new MyBase[] { new MyBase (), new MyBase () });      

Почему вместо этого она не вызывает специализированную версию MyBase? Если я вызываю Foo (new MyBase ()) напрямую, он правильно определяет, какой вызов сделать. Это из-за отсутствия ковариации для коллекций в C # 3, или я просто глуп, и делаю это неправильно?

Спасибо!

Isaac

Ответы [ 2 ]

1 голос
/ 06 декабря 2009

Он не вызывает «специализированный», потому что компилятор выбирает, какой метод вызывать, когда программа (в данном случае это функция Go) скомпилирована , не при запуске.

Когда компилятор компилирует функцию Go, единственная информация, которую он имеет, - это наличие некоторого объекта типа T. Он не имеет ни малейшего представления о том, что в какой-то более поздний момент вы можете снабдить его объектом типа MyBase. Единственная опция, которую он имеет, - это выбрать перегрузку Foo<T>, и она заполняет ее скомпилированной программой.

Если вы хотите, чтобы приложение выбирало перегрузки во время выполнения и выбирало наилучшую перегрузку, глядя на объект во время работы приложения, это называется «динамическая диспетчеризация» и используется только динамическими языками, такими как Ruby, Python, PHP и т. Д.

C # 3 полностью статичен и не поддерживает это. Вам нужно написать в своем коде оператор if, чтобы проверить тип, если вы хотите, чтобы он работал таким образом. C # 4, с другой стороны, имеет некоторую динамическую поддержку. Если бы вы писали этот код на C # 4, вы могли бы объявить функцию Go следующим образом:

 public void Go<T> (IEnumerable<dynamic> items)

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

0 голосов
/ 03 декабря 2009

Согласно спецификациям C # (7.4.3.2), неуниверсальный метод лучше, чем универсальный, поэтому его следует выбрать.

Но я думаю, что в вашем случае используется универсальный, потому что он вызывается в общем контексте вызова (цикл "foreach").

...