Preprocessing identifier Conditional attribute

Hey there,

I am using some compiler conditionals with platforms and saw in the documentation here that you can use the Conditional attribute like this. I can not seem to get it to work though, if I put it before the function it still compiles.

for example:

        [System.Diagnostics.Conditional("UNITY_ANDROID")]
        static void Test(string fileName)
        {
            string filePath = Application.persistentDataPath + "/" + fileName;
            WWW load = new WWW("test");
            System.IO.File.WriteAllBytes(filePath, load.bytes);
        }

Gives the error for the WriteAllBytes function when in web build mode in the editor

but this does work while in web platform in the editor without editors like it should

#if UNITY_ANDROID
        static void Test(string fileName)
        {
            string filePath = Application.persistentDataPath + "/" + fileName;
            WWW load = new WWW("test");
            System.IO.File.WriteAllBytes(filePath, load.bytes);
        }
#endif

Am I doing something wrong here?

1 Like

Yeah, it looks like the ConditionalAttribute only works for custom defines. It works as expected with my own defines, but when it comes to standard platform symbols like “UNITY_EDITOR”, I have no luck neither. Kind of a bummer.

I just realized after reading this: c# - #if DEBUG vs. Conditional("DEBUG") - Stack Overflow that it’s actually expected behaviour. You see, only calls are stripped, but methods are still compiled into IL. And at this point your System.IO.File.WriteAllBytes() simply doesn’t exist. It doesn’t matter that the method is never going to be called in the resulting code. The compiler doesn’t know. Still a bummer though.

2 Likes

By the way, the solution is obvious (for anyone who reads this later). Just wrap the inside of the problematic method in #if directive like this:

[System.Diagnostics.Conditional("UNITY_ANDROID")]
static void Test(string fileName)
{
#if UNITY_ANDROID
       string filePath = Application.persistentDataPath + "/" + fileName;
       WWW load = new WWW("test");
       System.IO.File.WriteAllBytes(filePath, load.bytes);
#endif
}

And you can call Test() anywhere else without worries.

1 Like

In this case the Conditional attribute looses the purpose

4 Likes

Not really, it saves you the overhead of calling an empty method when the platform, in this specific example, isn’t android.

No, maybe your understanding is wrong for the “purpose of conditional attribute” or you only need the preprocessor define.

The conditional attribute has no effect on the callee side, it works on the caller side.

The code for the method will compile and will be included in the IL level, however, calls to the method will be omitted if the preprocessor define is not set on the caller side.

TLDR:

If you do not want the code to compile to the IL use the following:

#if UNITY_ANDROID
    //----
#endif

If you want the code to compile to the IL but execute it in some places and not execute it in other places conditionally use the following:

[System.Diagnostics.Conditional("UNITY_ANDROID")]
1 Like

Yeah, that defies the purpose.
Now you have to pepper #ifdefs throughout your code.
It could, COMPLETELY LEGALLY, compile, and then be stripped on the IL level.
But no. We cannot have nice things.
(or, you know, UnityEditor could just be a stub Assembly for builds)
(especially in ECS baking, this is so densely mixed, and you can’t just put your Authoring Behaviours into Editor folders, because Unity made that Maliciously illegal as well - can’t have editor scripts on game objects, even though there are about a dozen very concrete use cases for them)

Good News: We just tested the ConditionalAttribute today, using Unity’s built-in defines (UNITY_EDITOR)… and it works now!