0

In the event that a Conditional method is compiled away, the arguments to each invocation are still type-checked at compile time. What is the motivation for this? Example:

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) { }
    }
}

To be clear, the conditional symbol "condition" is not defined in this context, so the method invocations are omitted from the MSIL resulting from compilation. This is on par with the spec defined here, so no surprises there. Imagine a more complex scenario:

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
    }
}

Invocations of 'ConditionalMethod' will only be included in the resulting compilation when the conditional symbol "condition" is defined. In that scenario, however, Bar CAN in fact be upcast to Foo. If the compiler knows that invocations to 'ConditionalMethod' will be compiled away, shouldn't it also be aware that in the event that we care about invocations of this method, this code will be legit? Yes, this is a contrived and hair-splitting example, but it helps illustrate my question. I'm asking out of benign curiosity, as this has irked me for quite some time now. Please help, Jon Skeet. :)

Nesh
  • 11
  • 3
  • 1
    If the argument types don't match the definition of the conditional method, how is the compiler to know you meant the method with that name and signature? – hatchet - done with SOverflow Apr 06 '17 at 23:39
  • If the method is not overloaded, then there is no ambiguity, and the compiler de facto should know which method you're referencing. But this is a good case that I hadn't thought of, and seems reasonable for the overloaded case. – Nesh Apr 07 '17 at 16:08
  • Your proposal here is that the language have one set of rules if the method group has one element and another set of rules if it does not. This is a really bad idea. It means that making small changes to a program can cause large changes in the overload resolution outcomes, which works against writing programs that are easy to modify. – Eric Lippert Apr 08 '17 at 21:36
  • More generally: C# is not a "guess what you meant and hope for the best" language. It is not a "try to figure out any way that the program could work, and make it work" language. C#'s attitude is **you want the compiler to find your bugs**, and if a program looks like a bug, **then it should tell you**. – Eric Lippert Apr 08 '17 at 21:37
  • @EricLippert, thanks for the responses! I also read your blog post in regards to conditional compilation vs the ConditionalAttribute. The overload resolution part makes a lot of sense, I appreciate it. – Nesh Apr 10 '17 at 17:59

1 Answers1

2

Imagine this code

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) { }
}

A method's signature is a critical part of associating a call to the method that should be called. That association must be made before it is known whether the Conditional attribute applies. In the example above, the call does not match any of the methods. The compiler would have to make a leap of faith to guess that you meant the Blah(int,string). But it would be a guess, because the the signature doesn't match (the argument types are in the wrong order). Blah(string,int,int) is also pretty close - you just forgot an argument. And Blahh(string,int) is close too - you just made a typo in the name.

For a similar example, see this blog post by Eric Lippert (who knows this stuff).

Therefore the arguments must be well-defined at the point of the call, even if the call is going to be removed. In fact, the call cannot be removed unless the arguments are extant!

and later

The effect of a conditional compilation directive happens at lex time; anything that is inside a removed #if block is treated by the lexer as a comment. It’s like you simply deleted the whole contents of the block and replaced it with whitespace. But removal of call sites depending on conditional attributes happens at semantic analysis time; everything necessary to perform that semantic analysis must be present.

  • This doesn't make sense IMO. What do you mean about an unsafe assumption? You referenced the method 'Blah' so the compiler doesn't assume you made a typo and try to correct the code for you - it takes your code at face value. I don't think I understand the point you're making. Care to elaborate? :) – Nesh Apr 07 '17 at 16:36