I've known about using the conditional attribute on methods for a while now, but I just found out that it can also be used on attribute classes, so I wrote some code to test it, but its not performing as expected.
This MSDN page shows how to use the conditional attribute on an attribute class down the bottom of the page: https://msdn.microsoft.com/en-us/library/4xssyw96%28v=vs.90%29.aspx.
I'm using the Unity engine by the way. I don't think that should matter, but it might I guess.
Here's the test code I wrote:
using System.Reflection;
using UnityEngine;
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public class TestAttribute : System.Attribute
{
public string text;
public TestAttribute(string text)
{
this.text = text;
}
}
public class NewBehaviourScript : MonoBehaviour
{
[Test("This shouldn't exist on android")]
public void Awake()
{
#if UNITY_EDITOR
Debug.Log("This only gets logged in the Unity Editor, not in an Android build");
#endif
Debug.Log("Begin Attribute Test");
{
object[] attributes = typeof(NewBehaviourScript).GetMethod("Awake").GetCustomAttributes(true);
for (int i = 0; i < attributes.Length; i++)
{
Debug.Log(attributes[i]);// This logs TestAttribute both in the editor and on android.
}
TestAttribute att = attributes[0] as TestAttribute;
Debug.Log(att.text);// This logs "This shouldn't exist on android" both in the editor and on android.
}
Debug.Log("End Attribute Test");
Debug.Log("");
Debug.Log("Begin Method Test");
{
Method();// This only gets called in the Unity Editor, as expected from the conditional attribute.
MethodInfo methodInfo = typeof(NewBehaviourScript).GetMethod("Method");
Debug.Log(methodInfo);// this logs "void Method()" both in the editor and on android.
}
Debug.Log("End Method Test");
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public void Method()
{
Debug.Log("This shouldn't exist on android either");
}
}
If the conditional attribute doesn't stop GetCustomAttributes() from getting the test attribute, what does it actually do?
Let me simplify your example:
[Conditional("DEBUG")]
class MyAttribute : Attribute {}
[MyAttribute]
class MyClass {}
When the Conditional attribute is applied to an Attribute-derived type, it causes the attribute to be removed from all marked symbols if the Conditional attribute's condition does not hold. So in the example above, MyClass is marked with MyAttribute in debug builds only; in Release builds, the DEBUG symbol is not defined (usually, at least), so the compiler removes [MyAttribute] from the declaration of MyClass. You can see that when using reflection, try to run the following code in debug and release builds:
private static void Main(string[] args)
{
var attribute = typeof(MyClass).GetCustomAttribute<MyAttribute>();
Console.WriteLine(attribute == null ? "missing" : "exists");
}
This will print "exists" in debug builds and "missing" in release builds. However, only the application of the attribute is removed; the attribute class itself still exists in the compiled assembly. This is similar to how Conditional works for methods: Only the method invocations are removed, the methods still exist and can be called via reflection, for instance.
Why can that be useful? One use case is described by JetBrains in a blog post (see section "The JetBrains.Annotations NuGet package"): They have a NuGet package called JetBrains.Annotations that contains various attributes that help their tool Resharper to analyze C# code. Adding this NuGet package to your project, however, would require you to ship that assembly with your product, even though you only really use it for coding; not at runtime. So what they do is: They annotate all attributes in the assembly with the Conditional attribute. This causes the compiler to strip out the attributes during compilation; it then notices that JetBrains.Annotations is never referenced, removing the reference from the compiled assembly. Consequently, you do not have to ship JetBrains' assembly with your product.
I have no idea, however, if this is working in Unity at all. Unity is known to use an old version of Mono and the C# compiler, so maybe there are bugs preventing all of this from working. Actually, your code seems to show that this is indeed not working correctly in Unity. But if you run my or your code in a standalone .NET application, it works. By the way, you can also use newer versions of the C# compiler to compile assemblies that Unity can then reference; that should allow you to work around this issue in Unity.
Update 2018-03-17: The latest versions of Unity now feature a newer Mono runtime and an updated C# compiler (and will update to Microsofts C# compiler soon). So any non-standard behavior regarding conditionally compiled-in attributes should be fixed in the latest versions of Unity.
According to this article, the Conditional Attribute prevents the compiler from emitting MSIL for a void returning function for which the stated condition does not exist.
I think that your first use of it is trying to tell the system that the TestAttribute attribute shouldn't exist if the condition isn't met, which doesn't seem to be what it's for.
When you are getting the MethodInfo for method Method later on, I think that the compiler has generated the call to Method, but it should not perform any action because there is no code to be performed. (i.e. the method should immediately return.)
You don't actually seem to be calling Method, just getting info about it, which I think could be misleading you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With