Вопрос

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

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

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, или я просто глуп и делаю это неправильно?

Спасибо!

Исаак

Это было полезно?

Решение

Он не вызывает «специализированный», поскольку компилятор выбирает, какой метод вызывать, когда программа (которая в данном случае является 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

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top