Negative tive C # Principle 4: Use condition attributes instead of # If

Source: Internet
Author: User

You can use the # If/# endif block to generate different compilation (results) on the same source code. Most of the two versions are debug and release. But they are by no means a tool we like. Because # If/# endif is easily abused, it makes the written code difficult to understand and debug. Programmers have the responsibility to provide better tools for generating machine code in different runtime environments. C # provides the condition attribute to identify which methods can be called based on environment settings.

Attribute has two words in C #. One is property and the other is attribute. They do not mean anything, but they are generally translated as attributes when translated into Chinese. Property refers to the nature of an object, that is, the attribute in Item1. Attribute here refers to the attributes attached to. NET as a special class, method, or property. You can search for Attribute in msdn for more help. In short, note that attribute and property are totally different .)

This method is clearer than the Conditional compilation # If/# endif. The compiler can recognize the conditional attribute, so the compiler can do a great job when the condition attribute is applied. Condition attributes are used on methods, so you must write the code used under different conditions into different methods. When you want to generate different codes for different conditions, use the condition attribute instead of the # If/# endif block.

Many programming veterans use Conditional compilation in their projects to detect prerequisite (Per-conditions) and subsequent conditions (post-conditions ).

A prerequisite is a condition that must be met before a job can be completed, it is a condition that will be met after a job is completed. For example, if a function converts an object, it requires that the object cannot be empty. After the conversion, the object must be an integer. Then: Per-conditions indicates that the object cannot be empty, the post-conditions object is an integer. The example is not good, but you can understand these two concepts .)

You may write a private method to detect all classes and persistent objects. This method may be a Conditional compilation block so that it is only valid in debug mode.

private void CheckState( ){// The Old way:#if DEBUG  Trace.WriteLine( "Entering CheckState for Person" );  // Grab the name of the calling routine:  string methodName =    new StackTrace( ).GetFrame( 1 ).GetMethod( ).Name;  Debug.Assert( _lastName != null,    methodName,    "Last Name cannot be null" );  Debug.Assert( _lastName.Length > 0,    methodName,    "Last Name cannot be blank" );  Debug.Assert( _firstName != null,    methodName,    "First Name cannot be null" );  Debug.Assert( _firstName.Length > 0,    methodName,    "First Name cannot be blank" );  Trace.WriteLine( "Exiting CheckState for Person" );#endif}

With the # If and # endif compilation options (pragmas), you have compiled an empty Method for your release (release. This checkstate () method will be called in all versions (debug and release. In release, it does nothing, but it must be called. Therefore, you have to pay a small part of the cost for calling it in routine business.

In any case, the above practice can work correctly, but it will lead to a small bug that only appears in release. The following is a common error that tells you what will happen during Conditional compilation:

public void Func( ){  string msg = null;#if DEBUG  msg = GetDiagnostics( );#endif  Console.WriteLine( msg );}

All of this works normally in debug mode, but the output in release is empty. The release mode is happy to output a blank line to you, but this is not what you expected. It's dumb, but the compiler won't help you. The basic code in your Conditional compilation block is indeed like this logic. Some scattered # If/# endif blocks make it rare to diagnose your code under different compilation conditions (diagnose ).

C # Better choice: This is the condition attribute. With the condition attribute, You can discard some functions of a class in the specified compiling environment, but whether a variable is defined or a variable has a clear value. The most common usage of this function is to make your code have a declaration available for debugging .. The. NET Framework library provides you with basic generic functions. This example shows you how to use the compatibility debugging feature of the. NET Framework library, how condition attributes work, and when to add them:

When you create a person object, you add a method to verify the unchanged data (invariants) of the object ):

private void CheckState( ){  // Grab the name of the calling routine:  string methodName =    new StackTrace( ).GetFrame( 1 ).GetMethod( ).Name;  Trace.WriteLine( "Entering CheckState for Person:" );  Trace.Write( "\tcalled by " );  Trace.WriteLine( methodName );  Debug.Assert( _lastName != null,    methodName,    "Last Name cannot be null" );  Debug.Assert( _lastName.Length > 0,    methodName,    "Last Name cannot be blank" );  Debug.Assert( _firstName != null,    methodName,    "First Name cannot be null" );  Debug.Assert( _firstName.Length > 0,    methodName,    "First Name cannot be blank" );  Trace.WriteLine( "Exiting CheckState for Person" );}

In this method, you may not need to use too many library functions. Let me simplify it. The stacktrace class obtains the name of the call method through reflection. This is expensive, but it does simplify the work, such as generating information about the program process. Here, the name of the method called by checkstate is determined. The determining method is a part of the system. Diagnostics. debug class, or a part of the system. Diagnostics. Trace class. The degbug. Assert method is used to test whether the condition is met and terminates the application when the condition is false. The remaining parameters define the messages to be printed after the assertion fails. Trace. writeline outputs diagnostic messages to the debugging console. Therefore, this method outputs messages to the debugging console when the person object is invalid and terminates the application. You can call this method on all public methods or attributes as a prerequisite or a successor condition.

public string LastName{  get  {    CheckState( );    return _lastName;  }  set  {    CheckState( );    _lastName = value;    CheckState( );  }}

When someone tries to assign a null value or null to lastname, The checkstate will immediately trigger an asserted. Then, you can modify your property configurator to verify the lastname parameter. This is what you want.

However, such additional detection exists in each routine task. You want to perform additional verification only in the debugging version. At this time, the condition property came into being:

[ Conditional( "DEBUG" ) ]private void CheckState( ){      // same code as above}

The conditional attribute tells C # compiler that this method is called only when the environment variable debug is defined. At the same time, the conditional attribute does not affect the code generated by the checkstate () function, but modifies the call to the function. If the debgu mark is defined, you can get this:

public string LastName{  get  {    CheckState( );    return _lastName;  }  set  {    CheckState( );    _lastName = value;    CheckState( );  }}


If not, you get the following:

public string LastName{  get  {    return _lastName;  }  set  {    _lastName = value;  }}

The checkstate () function body is the same regardless of the environment variable status. This is just an example. It tells you why you need to understand the differences between. Net compilation and JIT. Whether or not the debug environment variables are defined, the checkstate () method is always compiled and stored in the Assembly. This may seem inefficient, but it only occupies some hard disk space. The checkstate () function will not be loaded into the memory, nor will it be loaded into the jited: here, jited refers to the real compilation of machine code) unless it is called. It does not exist in assembly files. Such a policy is to enhance (Program) scalability, and this is only a little negligible performance overhead. You can get a deeper understanding by viewing the debug class in the. NET Framework library. On any machine that has installed the. NET Framework library, the system. dll Assembly contains code for all methods of the debug class. It is up to the environment variables to determine whether to allow callers to call them during compilation.

You can also write a method to make it lazy with more than one environment variable. When you apply multiple environment variables to control the condition attributes, they are tied in the form of or. For example, the checkstate of the following version will be called when debug or trace is true:

[ Conditional( "DEBUG" ),  Conditional( "TRACE" ) ]private void CheckState( )

To generate an and parallel condition attribute, you must use the preprocessing command in the Code to define a tag:

#if ( VAR1 && VAR2 )#define BOTH#endif

Yes. To create a conditional routine that is lazy with the preceding environment variables, you have to return to the # If practice you used at the beginning. # If generates a new flag for us, but avoid adding any executable code to the compilation option.

The conditional attribute can only be used on the entity of a method. In addition, it must be a method with a return type of void. You cannot use conditional on a code block in a method, or use the conditional attribute on a method with a returned value. Instead, you need to carefully construct a condition method and discard the condition attribute behavior on those methods. You still need to review the methods with condition attributes to see if they have side effects on the object state. However, the placement of the conditional attribute is much better than that of # If/# endif. When using the # If/# endif block, you may mistakenly remove an important method call or some configuration.

The previous example uses a pre-defined debug or trace tag, but you can use this technique to extend it to any symbol you want. The conditional attribute can be flexibly controlled by definition tags. You can define it on the Compilation command line, in the system environment variables, or in the source code compilation selection.

Using the conditional attribute can generate more efficient il code than using # If/# endif. When it specifically targets functions, it is more advantageous, And it will force you to use a better structure in the conditional code. The compiler uses the conditional attribute to help you avoid common errors caused by # If/# endif. Condition attributes provide better support for distinguishing condition codes than preprocessing.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.