Почему аргументы к условному методу всегда проверяются типом?

В случае, если условный метод компилируется, аргументы каждого вызова все еще проверяются на тип во время компиляции. Какова мотивация для этого? Пример:

using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int x = 2;
            string st = "";
            // this invocation compiles fine
            ConditionalMethod(x, st);
            // this invocation won't compile
            ConditionalMethod(st, x);
        }

        [Conditional("condition")]
        public static void ConditionalMethod(int x, string st) { }
    }
}

Чтобы было ясно, условный символ "условие" не определен в этом контексте, поэтому вызовы метода опускаются из MSIL в результате компиляции. Это на уровне спецификации, определенной здесь, так что никаких сюрпризов. Представьте себе более сложный сценарий:

using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            ConditionalMethod(new Bar());
        }

        [Conditional("condition")]
        public static void ConditionalMethod(Foo foo) { }

        public class Foo { }

#if condition
        public class Bar : Foo { }
#else
        public class Bar { }
#endif
    }
}

Вызовы "ConditionalMethod" будут включены в результирующую компиляцию только тогда, когда определен условный символ "условие". В этом случае, однако, Бар МОЖЕТ быть фактически передан в Foo. Если компилятор знает, что вызовы ConditionalMethod будут скомпилированы, разве не следует также знать, что в случае, если мы заботимся о вызовах этого метода, этот код будет допустимым? Да, это надуманный и потрясающий пример, но он помогает проиллюстрировать мой вопрос. Я прошу из любезного любопытства, поскольку это раздражало меня в течение довольно долгого времени. Пожалуйста, помогите, Джон Скит.:)

1 ответ

Решение

Представьте себе этот код

class Program
{
    static void Main(string[] args)
    {
        int x = 2;
        string st = "";
        // this invocation compiles fine
        Blah(x, st);
        // this invocation won't compile
        Blah(st, x);
    }

    [Conditional("condition")]
    public static void Blah(int x, string st) { }

    public static void Blah (string st, int x, int y) { }

    public static void Blahh(string st, int x) { }
}

Подпись метода является важной частью связи вызова метода, который должен быть вызван. Эта связь должна быть сделана до того, как станет известно, применяется ли атрибут Conditional. В приведенном выше примере вызов не соответствует ни одному из методов. Компилятор должен сделать прыжок в веру, чтобы догадаться, что вы имели в виду Blah (int, string). Но это было бы предположение, потому что подпись не совпадает (типы аргументов в неправильном порядке). Blah (string, int, int) также довольно близок - вы просто забыли аргумент. И Blahh (string, int) тоже близко - вы только что сделали опечатку в названии.

Для подобного примера, см. Этот пост Эрика Липперта (который знает это).

Поэтому аргументы должны быть четко определены в точке вызова, даже если вызов будет удален. Фактически, вызов не может быть удален, если аргументы не существуют!

и позже

Эффект директивы условной компиляции происходит в течение времени lex; все, что находится внутри удаленного блока #if, рассматривается лексером как комментарий. Как будто вы просто удалили все содержимое блока и заменили его пробелами. Но удаление сайтов вызовов в зависимости от условных атрибутов происходит во время семантического анализа; все необходимое для выполнения этого семантического анализа должно присутствовать.

Другие вопросы по тегам