Почему компилятор C # допускает явное приведение между IEnumerable <T>и TAlmostAnything? - PullRequest
59 голосов
/ 08 февраля 2012

Следующий код выдает ошибку компилятора, как и следовало ожидать:

List<Banana> aBunchOfBananas = new List<Banana>();

Banana justOneBanana = (Banana)aBunchOfBananas;

Однако при использовании IEnumerable<Banana> вы просто получаете ошибку времени выполнения.

IEnumerable<Banana> aBunchOfBananas = new List<Banana>();

Banana justOneBanana = (Banana)aBunchOfBananas;

Почему компилятор C # позволяет это?

Ответы [ 4 ]

48 голосов
/ 08 февраля 2012

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

С другой стороныС другой стороны, компилятор знает, что List<T> нельзя явно привести к Banana.

Кстати, хороший выбор примеров!

Добавление примера для пояснения. Может быть, у нас был бы какой-нибудь "перечислимый", который должен всегда содержать не более один Banana:

public class SingleItemList<T>:Banana, IEnumerable<T> where T:Banana {
    public static explicit operator T(SingleItemList<T> enumerable) {
        return enumerable.SingleOrDefault();
    }

    // Others omitted...
}

Тогда вы могли бы на самом делесделайте это:

IEnumerable<Banana> aBunchOfBananas = new SingleItemList<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

Это то же самое, что написать следующее, что компилятору очень нравится:

Banana justOneBanana = aBunchOfBananas.SingleOrDefault();
29 голосов
/ 08 февраля 2012

Когда вы говорите Y y = (Y)x;, это приведение говорит компилятору: "Поверьте мне, что бы ни было x, во время выполнения его можно привести к Y, так что просто сделайте это, хорошо?"

Но когда вы говорите

List<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

компилятор может посмотреть определения для каждого из этих конкретных классов (Banana и List<Banana>) и увидеть, что не определено static explicit operator Banana(List<Banana> bananas) (помните, явное приведение должно быть определено в любом типе, который приводится или тип, к которому производится приведение, это из спецификации, раздел 17.9.4). Во время компиляции он знает, что то, что вы говорите, не может быть правдой. Поэтому он кричит на тебя, чтобы ты перестал лгать.

Но когда вы говорите

IEnumerable<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

хорошо, теперь компилятор не знает. Вполне может случиться так, что независимо от того, что aBunchOfBananas окажется во время выполнения, его конкретный тип X мог бы определить static explicit operator Banana(X bananas). Таким образом, компилятор доверяет вам, как вы и просили.

16 голосов
/ 08 февраля 2012

Это может быть потому, что компилятор знает , что Banana не расширяет List<T>, но есть вероятность, что некоторый объект, который реализует IEnumerable<T>, может также расширить Banana и сделать это действительный актерский состав.

0 голосов
/ 15 февраля 2012

В соответствии со спецификацией языка (6.2.4) «Явными ссылочными преобразованиями являются: Из любого типа класса S в любой интерфейсный тип T при условии, что S не запечатан, и при условии, что S не реализует T ... Явная ссылкапреобразования - это преобразования между ссылочными типами, которые требуют проверки во время выполнения, чтобы убедиться, что они правильные ... "

Таким образом, компилятор не проверяет реализацию интерфейса во время компиляции.Это делает CLR во время выполнения.Он проверяет метаданные, пытаясь найти реализацию в классе или среди своих родителей.Я не знаю, почему он так себя ведет.Вероятно, это займет много времени.Таким образом, этот код компилируется правильно:

public interface IInterface
{}

public class Banana
{
}

class Program
{
    static void Main( string[] args )
    {
        Banana banana = new Banana();

        IInterface b = (IInterface)banana;
    }
}

С другой стороны, если мы пытаемся привести банан к классу, компилятор проверяет его метаданные и выдает ошибку:

 FileStream fs = (FileStream)banana;
...