views:

316

answers:

4

I read about the Conditional attribute today. According to MSDN:

Applying ConditionalAttribute to a method indicates to compilers that a call to the method should not be compiled into Microsoft intermediate language (MSIL) unless the conditional compilation symbol that is associated with ConditionalAttribute is defined.

OK. That's clear. So the call to the method will not be compiled. But what about side effects?

[Conditional("UndefinedCondition")]
static void f1(int x) { Console.WriteLine(x); }

static int a = 0;
static void f2() { f1(++a); }

So when f2 is called, the call to f1 should be removed. But why is ++a removed as well? This does't make any sense to me!

+1  A: 

I guess that's due to ease of compiler implementation.

I would be fearful of such code anyways (even if it worked as you expect it) and write it as

++a;
f1(a);

for clarity. You can always see what is executed and what isn't.

Martinho Fernandes
That would be nice. But if f1() was initially a normal method, then someone decided to make it conditional then this would break the application, without any compiler warning, wouldn't it? I would say that the compiler should execute the side effect, because "++a" itself is not conditional!
Hosam Aly
Otherwise, I believe that the compiler would be breaking consistency. Only code that I mark as conditional should be removed, but not any other code that I don't mark as so!
Hosam Aly
This way you have a choice. Move code you always wants executed out, leave it in if it should only be executed if the method call will be done. Yes, a programmer that adds this attribute can break things, but some responsibility is left for the programmers too.
Lasse V. Karlsen
It is not the function that is not-compiled, it's the call. However it seems your compiler removes the C# call, when it should remove the IL call. Perhaps you should report this behavior so they can fix it.
Martinho Fernandes
Now that I read it, if you don't recompile your application but someone Conditionals out some function in another assembly, it won't actually break. It will call it: it's the compiler that removes the call. The function is still there. (I didn't test this, I'm just interpreting the documentation).
Martinho Fernandes
Reporting the behavior is probably of no use, since people may have written code depending on this behavior. You're right about changing another assembly without recompilation, as the call is removed from the call site, not the callee.
Hosam Aly
+5  A: 

Yes, any calls required for arguments are removed too. This means that for the typical use-case (debug builds) you remove the entire expression, which is usually what is intended.

Basically, you need to be very careful when using either [Conditional] methods, or equally (in C# 3.0) partial methods - which have very similar behaviour if the other half of the partial method isn't implemented. As an example (for partial methods), see below. Note that the call to HasSideEffect() is removed (uncomment the other half of Bar to see it work):

using System;
partial class Foo {
    partial void Bar(string value);
    static void Main() {
        Foo foo = new Foo();
        foo.Bar(HasSideEffect());
    }
    static string HasSideEffect() {
        Console.WriteLine("hello");
        return "world";
    }
}

partial class Foo {
    /* uncomment this
    partial void Bar(string value) {
        Console.WriteLine(value);
    }*/ 
}
Marc Gravell
Thanks. You said they have "very similar" behavior. Are there differences?
Hosam Aly
Not really - other than the condition for inclusion is different; with [Conditional] it is a symbol - with partial methods it is whether the partial method is implemented.
Marc Gravell
+1  A: 

I also suspect that this behaviour is designed to be the same as C preprocessor macros, which typically are set up to not evaluate the arguments when not in use.

Rob Walker
+5  A: 

Expanding on Marc's answer.

This is definately "By Design". The best way to understand the rationalization for this is to think about what this code took the place of. This feature in many, and much cleaner ways, takes the way of conditionally compiled code.

For Example

#if DEBUG
f1(++a);
#endif

Or another version

#define f1(x) ...

In the non-debug case there clearly no side effects. This is same behavior for [Conditional] code. I agree it's definately not as clear as the first example but is as clear as the second one.

JaredPar