Conditional
attributeThe Conditional
attribute enables the definition of conditional methods. The Conditional
attribute indicates a condition in the form of a pre-processing identifier. Calls to a conditional method are either included or omitted depending on whether this symbol is defined at the point of the call. If the symbol is defined, then the method call is included if the symbol is undefined, then the call is omitted.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class ConditionalAttribute: System.Attribute { public ConditionalAttribute(string conditionalSymbol) {…} public string ConditionalSymbol { get {…} } }
A conditional method is subject to the following restrictions:
Conditional
attribute is specified on an interface method.void
.override
modifier. A conditional method may be marked with the virtual
modifier. Overrides of such a method are implicitly conditional, and must not be explicitly marked with a Conditional
attribute.Also, a compile-time error occurs if a conditional method is used in a delegate-creation-expression. The example
#define DEBUG class Class1 { [Conditional("DEBUG")] public static void M() { Console.WriteLine("Executed Class1.M"); } } class Class2 { public static void Test() { Class1.M(); } }
declares Class1.M
as a conditional method. Class2
's Test
method calls this method. Since the pre-processing symbol DEBUG
is defined, if Class2.Test
is called, it will call M
. If the symbol DEBUG
had not been defined, then Class2.Test
would not call Class1.M
.
It is important to note that the inclusion or exclusion of a call to a conditional method is controlled by the pre-processing identifiers at the point of the call. In the example
// Begin class1.cs class Class1 { [Conditional("DEBUG")] public static void F() { Console.WriteLine("Executed Class1.F"); } } // End class1.cs // Begin class2.cs #define DEBUG class Class2 { public static void G { Class1.F(); // F is called } } // End class2.cs // Begin class3.cs #undef DEBUG class Class3 { public static void H { Class1.F(); // F is not called } } // End class3.cs
the classes Class2
and Class3
each contain calls to the conditional method Class1.F
, which is conditional based on the presence or absence of DEBUG
. Since this symbol is defined in the context of Class2
but not Class3
, the call to F
in Class2
is actually made, while the call to F in Class3
is omitted.
The use of conditional methods in an inheritance chain can be confusing. Calls made to a conditional method through base
, of the form base.M
, are subject to the normal conditional method call rules. In the example
class Class1 { [Conditional("DEBUG")] public virtual void M() { Console.WriteLine("Class1.M executed"); } } class Class2: Class1 { public override void M() { Console.WriteLine("Class2.M executed"); base.M(); // base.M is not called! } } #define DEBUG class Class3 { public static void Test() { Class2 c = new Class2(); c.M(); // M is called } }
Class2
includes a call the M
defined in its base class. This call is omitted because the base method is conditional based on the presence of the symbol DEBUG
, which is undefined. Thus, the method writes to the console only "Class2.M executed
". Judicious use of pp-declarations can eliminate such problems.