Почему несвязанные ссылки на интерфейсы c # можно сравнивать без ошибок компилятора? - PullRequest
13 голосов
/ 26 октября 2010

Недавно я с удивлением обнаружил, что компилятор явно не строг в сравнении ссылок на интерфейсы, и мне интересно, почему он работает таким образом.

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

class Program
{
    interface I1 {}
    interface I2 {}
    class C1 : I1 {}
    class C2 : I2 {}

    static void Main(string[] args)
    {
        C1 c1 = new C1();
        C2 c2 = new C2();

        I1 i1 = c1;
        I2 i2 = c2;

        bool x = c1 == c2;
        bool y = i1 == i2;
    }
}

Компиляторговорит, что я не могу сравнить c1 == c2, что следует.Типы абсолютно не связаны.Тем не менее, это позволяет мне сравнивать i1 == i2.Я ожидал бы, что здесь произойдет ошибка с ошибкой во время компиляции, но я был удивлен, обнаружив, что вы можете сравнить любой интерфейс с любым другим, и компилятор никогда не будет жаловаться.Я мог бы сравнить, например, (I1)null == (IDisposable)null и без проблем.

Разве интерфейсы не являются объектами?Это особый тип ссылки?Я ожидал бы, что == приведет либо к прямому эталонному сравнению, либо к вызову виртуального равенства Equals конкретного класса.

Чего мне не хватает?

Ответы [ 4 ]

13 голосов
/ 26 октября 2010

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

interface I1 {}
interface I2 {}
class C1 : I1, I2 {}

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

12 голосов
/ 26 октября 2010

Прежде всего, обратите внимание, что Ганс цитирует правильный раздел спецификации, но что издание спецификации, которую он цитирует, содержит опечатку, которая имеет отношение к вашему вопросу. Исправленная спецификация C # 4 гласит:

Предопределенное равенство типов ссылок операторы требуют одного из следующее:

(1) Оба операнда являются значением типа, известного как ссылочный тип или буквальный ноль. Кроме того, явное преобразование ссылки существует от типа любого операнда к типу другого операнда.

(2) Один операнд является значением типа T где T - это параметр типа, а другой операнд является буквальным нулем. Кроме того, T не имеет значения ограничение типа.

Если только один из них условия верны, время связывания возникает ошибка.

Это объясняет ваше наблюдение. Существует явное преобразование ссылок между любыми двумя интерфейсами, потому что любые два экземпляра двух различных интерфейсов могут ссылаться на один и тот же объект. Может существовать класс C3, который реализует как I1, так и I2, и вы можете проводить сравнительное сравнение одного и того же экземпляра C3, один из которых преобразован в I1, а другой - в I2.

4 голосов
/ 26 октября 2010

Это очень хорошо описано в Спецификации языка C #, глава 7.9.6 «Операторы равенства ссылочных типов»:

Предопределенное равенство типов ссылок операторы:

оператор bool == (объект x, объект y);
оператор bool! = (объект x, объект у);

Операторы возвращают результат сравнения двух ссылок за равенство или неравенство.

С предопределенное равенство ссылочного типа операторы принимают операнды типа объект, они применяются ко всем типам, которые не объявляйте применимый оператор == и оператор! = участники. Наоборот, любое применимое пользовательское равенство операторы эффективно скрывают предопределенное равенство ссылочного типа операторы.

Предопределенная ссылка операторы равенства типов требуют одного из следующее:
• Оба операнда значения ссылочного типа или литерал ноль. Кроме того, стандартный неявный преобразование (§6.3.1) существует из тип любого операнда к типу другой операнд.
• один операнд значение типа T, где T является параметр типа и другой операнд буквальный ноль. Кроме того, Т не имеет тип значения ограничение.

Если только один из них условия верны, время компиляции ошибка происходит. Заметные последствия эти правила:
• Это ошибка времени компиляции для использования предопределенное равенство ссылочного типа операторы для сравнения двух ссылок которые, как известно, отличаются в время компиляции. Например, если типы операндов во время компиляции два типа классов A и B, и если ни А, ни В не происходят из другой, то это было бы невозможно для два операнда ссылаются на одно и то же объект. Таким образом, операция считается ошибкой во время компиляции.

Последний абзац, почему вы получаете ошибку.

0 голосов
/ 26 октября 2010

Я ожидал бы, что == приведет либо к прямому сравнению ссылок, либо к вызову виртуального равенства Equals конкретного класса.

Это правда, но компилятор не 'не знаю этого.Это будет определено во время выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...