C # practice with net Chapter 7 reflection, later binding and attribute excerpt

Source: Internet
Author: User
Tags object serialization reflector
Document directory
  • 7.1 reflection
  • 7.2 later binding
  • 7.3 attribute
  • 7.3.5. NET Framework
  • 7.3.6 example of custom attribute


We have discussed metadata (metadata) and its physical storage methods in the Assembly in section 2.2.2. This chapter describes how they form the basis of reflection and attribute mechanisms.

7.1 reflection

The reflection mechanism represents the use of the type metadata of an assembly during the execution period. Generally, this Assembly is explicitly loaded when another assembly is executed, but it can also be dynamically generated.

The word reflection is used to indicate that we use an image of an assembly (like an image in a mirror ). The image consists of the type metadata of the Assembly. We sometimes use the introspection term to express reflection.

7.1.1 when reflection is required

We have collected some usage categories of reflection mechanisms and will discuss them in more detail in the subsequent sections of this chapter. Reflection mechanisms can be used in the following scenarios.

  • During application execution, we can use the Dynamic Analysis of Type metadata to explore types in the Assembly. For example, the ildasm. EXE and reflector tools explicitly load modules in an assembly and analyze their contents (see Section 2.3 ).
  • Use the later binding period. This technique requires a class in the Assembly, which is unknown during the compilation period. Later binding technology is usually used in interpreted languages, such as scripting languages.
  • When we want to use the information in attribute.
  • When we want to remove non-public members from the external response class of the class. Of course, this behavior should be avoided as much as possible, but sometimes it is necessary to use it. For example, when writing a unit test that cannot be completed without a non-public member.
  • During the Dynamic Construction of the Assembly. To use classes in a dynamically constructed Assembly, we must explicitly use the post-binding technology.

CLR and framework use the reflection mechanism in some cases. For example, in the default Implementation of the Value Type equals () method, use reflection to compare the fields in two instances one by one.

CLR also uses reflection during Object serialization to determine which field needs to be serialized. Even the Garbage Collector uses it to construct the reference tree during the collection process.

7.1.2. Net reflection

The underlying principle of the reflection mechanism is not a new concept. We were able to dynamically analyze an executable program long ago, especially by using self-description information. The TLB format (see Section 8.4) was conceived for this purpose. The data in the TLB format comes from the Interface Definition Language (IDL) format. The IDL Language can also be considered as a self-describing language .. The reflection mechanism in net takes a step further than the TLB and IDL formats.

  • With the help of some basic classes, it is easier to use.
  • It is more abstract than the TLB and IDL languages. For example, it does not use a physical address, which means it can play a role on both 32-bit and 64-bit machines.
  • Compared with TBL metadata,. Net metadata is always included in the module it describes.
  • The description data is much more detailed than the TLB format. Specifically, we can obtain all possible information of any type declared in a set of Programs (for example, the type of a parameter of a method in a class ).

. Net reflection can describe such detailed content, thanks.. NET Framework. You can extract and use various types of metadata from a program contained in appdomian. Most of these classes can be found in the system. Reflection namespace, and each type of element in the Assembly has a class corresponding to it.

  • There is a class instance that represents the Assembly (system. reflection. Assembly );
  • There is a class instance that represents the class and structure (system. type );
  • An instance of the class represents the method (system. reflection. methodinfo );
  • An instance of the class represents the field (system. reflection. fieldinfo );
  • An instance of a class represents the method parameter (system. reflection. parameterinfo ).


Finally, we would like to remind you that these classes only provide a method to logically view all types of metadata. However, what we see is not exactly consistent with the physical metadata of all types, and some elements used for the internal organization of the Assembly are not displayed.

All classes in the system. Reflection namespace are connected in a logical way. For example, you can obtain a list of system. Type instances from an instance of system. reflection. assembly. You can obtain a system. reflection. methodinfo instance table from a system. Type instance. From a system. reflection. Method-Info instance, you can obtain a system. reflection. parameterinfo instance table. All of these 7-1 are shown.

Figure 7-1 interaction between reflection classes

You may find that we cannot go deep into the Il command level, but only to the level of the byte table. The Byte table contains the Il body of a method. Therefore, to view il commands, you may want to use some libraries, such as Cecil (developed by Jean-Baptiste evain) and ilreader (developed by Lutz roeder) or rail (developed by Coimbra University ). In. Net 2, reflection knows how to handle generic types, generic methods, and constraints on parameter types (see section 13.10.2 ).

7.1.3 reflection on the Assembly loaded with appdomain

The following example shows how to use the class analysis type metadata in the system. Reflection namespace. In fact, here we show an assembly that analyzes its own type metadata. To obtain an assembly class instance that represents the Assembly, we use the static method assembly. getexecutingassembly ().

Example 7-1

The program output is as follows:

7.1.4 obtain information from metadata

The purpose of this section is to display a small program that uses the reflection mechanism to display a set of exception classes contained in the system and mscorlib assembly. We are based on the fact that all Exception classes inherit from system. Exception. The exception type that is not directly inherited from system. Exception is marked with an asterisk.

We can easily modify the program to display all attribute classes in the framework based on the fact that all attribute classes inherit from the system. Attribute Class.

Example 7-2

Note the Assembly. reflectiononlyload () method used in the previous example. This method tells CLR that the loaded assembly is only used for reflection. Therefore, CLR will not allow code execution of the Assembly loaded in this way. Similarly, loading programs in reflection only mode is a little faster because CLR does not need to complete any security verification. The Assembly class provides the bool reflectiononly {Get;} attribute to determine whether an assembly is loaded using this method.

7.2 later binding

Before starting this section, we recommend that you have a good understanding of the basic content of object-oriented programming, especially the concept of polymorphism. For more information about this topic, see Chapter 12th.

7.2.1 Meaning of "binding a class"

First, we need to reach a consensus on the meaning of "binding a class. We will use the term "software layer" instead of "assembly" because the latter is also used in other technologies.

A class connection is established between the software layer of the class (instantiate the class and use the class instance) and the software layer of the defined class. Specifically, this relationship is the correspondence between the call of methods in the class in the software layer of the class and the physical addresses of these methods in the software layer of the defined class. It is through this connection that the thread can continue to execute when the class method is called.

In general, we divide the association of a class into three types: Early binding created completely during the compilation period, dynamic binding of some links created during the compilation period, and post-binding created during the execution period.

7.2.2 early binding and dynamic binding

Early binding contacts are created by the compiler during the creation of an Assembly Based on. NET source code. We cannot create an early binding for a virtual or abstract method. In fact, when a virtual or abstract method is called, The polymorphism mechanism determines the code to be executed based on the actual object of the called method during execution. In this case, the contact is regarded as dynamic binding. In other materials, dynamic binding is sometimes called implicit post-binding because they are created implicitly by the polymorphism mechanism and completed during the execution period.

Now let's take a closer look at the early bindings, which were created for methods in static methods or classes that are not virtual or abstract methods. If you strictly follow the definition of "class association" in the previous section, there is no early binding in. net. In fact, we must first wait for the JIT compiler to convert the method body to the machine language to know its physical address in the process address space. The compiler that creates the Assembly does not know the address of this method [1]. We can see in section 4.6 that to solve this problem, the compiler that creates an assembly inserts the metadata symbol (metadata token) corresponding to the called method in the Il code at the called position ). When the method body is compiled in real time (JIT), CLR internally stores the correspondence between the method and the physical address of the method body in the machine language. The information called the stub is physically saved to a method-related memory address.

The above understanding is very important, because in a language like C ++, when a method is not a virtual method or an abstract method (that is, a pure virtual function in C ++, the compiler can calculate the physical address of the method in the machine language. Then, the compiler inserts a pointer to the memory address at each location where this method is called. This difference gives. net a great advantage, because the compiler does not need to consider technical details such as memory performance. The IL code is completely independent of the physical layer it runs.

In dynamic binding, almost everything works in the same way as early binding. In the Il code, the compiler inserts metadata symbols corresponding to the called virtual (or abstract) method at the location where the method is called. The metadata symbol we mentioned here belongs to the method defined in the reference type, and the reference type is the type that will invoke the method. Then, the CLR will determine the method to jump to based on the specific implementation of the referenced object during execution.

Insert a type metadata symbol, which is used by the compiler to create dynamic bindings and early bindings. It is mainly used in the following three cases.

  • When the code contained in the module calls a method in the same module.
  • When the code contained in the module calls a method in different modules of the same assembly.
  • When the code contained in one module of an assembly calls a method defined in another Assembly referenced during compilation. During runtime, if the Assembly is not loaded when the instant compilation method is called, the CLR will implicitly load it.

7.2.3 later binding

The Code of Assembly A can be instantiated and used to define a type in assembly B, which may not be referenced during compilation of Assembly. We describe this type of contact as post-binding. We use the word "Post" in the sentence because the binding is completed during the code execution period rather than the compilation period. This type of binding is also explicit, because the name of the called method must be explicitly specified using a string.

Later binding to Microsoft's development world is not a new concept. The automation mechanism in COM technology is an example. It uses the idispatch interface as a work ing method, and allows scripting or weak types of languages such as VB to use later binding. The concept of later binding also exists in Java.

Later binding is one of the concepts that developers accustomed to C ++ can hardly understand. In fact, only early and dynamic binding exists in C ++. The reason is that we know that the necessary information (metadata symbol) required to create a binding is in assembly B where the called class is located, however, we cannot understand why developers cannot use the compiler capabilities. during compilation of A, they create early and dynamic bindings by referencing assembly B. There are the following explanations:

  • The most common reason is that some languages do not have a compiler at all! In a script language, commands are interpreted one by one. In this case, only later binding exists. By using post-binding, classes in the Assembly compiled by the interpreted language can be used. The fact that post-binding technology can be easily used in. Net makes it easier to create a proprietary interpretation/Dynamic Language (such as ironpython language http://www.ironpython.com /).
  • We may want to use post-binding technology in programs written in compiled languages such as C. The reason is that later binding can bring some flexibility to the general architecture of the application. This technology is actually a recently popular design model called plug-in. We will introduce it further in this chapter.
  • Some applications need to call the code in the unobtained assembly. A typical example is the open-source tool nunit, which can call any assembly method to test its code. We will discuss this topic further when constructing a custom attribute later.
  • If assembly B does not exist during assembly a compilation, we must use post-binding between the code in a and the class in B. In this case, we will discuss how to dynamically construct an Assembly later.

Some people prefer to use post-binding instead of polymorphism. In fact, during the call, only the method name and signature are taken into account, but they are irrelevant to the type of the object in which the called method is located, therefore, you only need to provide methods with proper names and signatures when implementing objects. However, I personally do not recommend this practice because it is too restrictive and cannot encourage application developers to properly design and use abstract interfaces.

In addition to the aforementioned reasons, you do not need to explicitly use post-binding. Do not use post-binding in applications for fun, because:

  • The benefit of syntax verification completed by the compiler is lost.
  • The performance of later binding is far inferior to that of early or dynamic binding methods. (Even if the optimization method mentioned later is used .)
  • Cannot create post-binding for the obfuscated class. In fact, in the obfuscation process, the name of the class contained in the program is changed. Therefore, the later binding mechanism cannot find the appropriate class correctly.
7.2.4 how to instantiate an unknown class during C # compilation to Il

If a class or structure is unknown during compilation, it cannot be instantiated using the new operator. Fortunately, the. NET Framework does provide classes that can be used to create instances of classes that are unknown during compilation.

1. refine a type

Now let's take a look at the various technologies that can be used when specifying a type.

  • Some methods of some classes accept a string containing the full type name (including the namespace.
  • Other methods accept an instance of the system. Type class. In an appdomain, each instance of the system. Type class represents one type, and no two instances represent this type at the same time.
    Several methods to obtain an instance of the system. Type class:
  • In C #, we usually use the typeof () keyword, which accepts a type as a parameter and returns the corresponding system. Type instance.
  • You can also use an overloaded version of The GetType () Static Method in the system. Type class.
  • If a type is encapsulated in another class, you can use the non-static method getnestedtype () or getnestedtypes () of the system. Type class (). You can also use the non-static methods GetType (), gettypes (), or getexportedtypes () of the system. reflection. Assembly class ().
  • You can also use the non-static methods GetType (), gettypes (), or findtypes () of the system. relection. module class ().

Now let's assume that the following program is compiled as the foo. dll assembly. We will demonstrate several methods for creating an nmfoo. calc class instance. These methods allow creation in a set of programs that do not reference Foo. dll.

Example 7-3 Foo. dll assembly code

2. UseSystem. ActivatorClass

The system. activator class provides two static methods: createinstance () and createinstancefrom (). You can use these methods to create an instance of a class that is unknown during compilation. For example:

Example 7-4

Both methods provide some overloaded versions and even generic versions. The following parameters are used.

  • Used to represent a string class or an instance of system. type;
  • The name of the Assembly containing the class. This parameter is optional;
  • List of constructor parameters. This parameter is optional.

If the Assembly containing classes does not appear in appdomain, calling the createinstance () or createinstancefrom () method will cause the assembly to be loaded. Depending on whether the createinstance () method or the createinstancefrom () method is called, The system. appdomain. Load () or system. appdomain. loadfrom () method is called internally to load the assembly. The CLR selects a constructor of the Class Based on the provided parameters, and returns an instance of the objecthandle class containing an encapsulated object. In Chapter 22nd of Introduction to. Net remoting, we will demonstrate another usage of these methods in a distributed application environment.

Using an instance of the system. Type class to specify the type of createinstance () overload version will directly return an instance of the object.

System. activator also has a createcominstancefrom () method, which is used to create an instance of a COM object and a GetObject () method used to create a remote object.

3. Use the system. appdomain class

System. the appdomain class has four non-static methods: createinstance (), createinstanceandunwrap (), createinstance-from (), and createinstancefromandunwrap (). You can use these methods to create an instance of classes unknown during compilation, for example:

Example 7-5

These methods are similar to those mentioned in system. activator. However, you can select the appdomain in which the object is created. In addition, the "andunwarp ()" version returns a direct reference to the object, which is obtained from an instance of the objecthandle class.

4. UseSystem. reflection. constructorinfoClass

An instance of the system. reflection. constructorinfo class references a constructor. The invoke () method of this class creates a later binding for the constructor internally and calls the constructor through this binding. Therefore, you can create an instance of the type to which the constructor belongs. For example:

Example 7-6

5. UseSystem. TypeClass

You can use the non-static method invokemember () of the system. Type class to create an instance of unknown classes during compilation. You only need to use the createinstance value in the bindingflags Enumeration During the call. For example:

Example 7-7

6. Special Cases

The method described above allows you to create almost any type or structure of instances. The following are two special cases.

  • To create an array, you must call the static method createinstance () in the system. array class ().
  • To create a delegate object, you must call the createdelegate () method in the system. Delegate class.
7.2.5 use post-binding

Now we know how to create instances of unknown classes during compilation. To use these instances, let's take a look at the later binding process between these types of members. There are also several methods to achieve this.

1. type. invokemember () method

Let's go back to the type. invokemember () method. Previously we used it to create an instance of this unknown type during compilation by calling a constructor of an unknown type. In the internal implementation, this method completes the following three tasks.

  • It looks for Members corresponding to the provided information on the type of its call.
  • If the member is found, create a later binding for it.
  • Use this Member (it is called by method, it is an instance of an object created by the constructor, it is a field that reads or sets a value, it is an attribute that executes set or get accessors, and so on ).

The following example shows how to call the sum () method on an instance of the nmfoo. calc class (note that the debugger can enter the method body bound later during debugging ).

Example 7-8

The most common version of the type. invokemember () method is:

The invokeattr parameter is a binary flag that specifies the type of member to be searched. For the search method, we use the bindingflags. invokemethod flag. For more information about the flag spaces, see the article "bindingflags enumeration" on msdn.

The binder parameter is a binder-type object that indicates how the invokemember () method searches. In most cases, this parameter can be set to null to indicate that you want to use the default value, that is, system. type. defaultbinder. Objects of the binder type provide the following types of information:

  • It indicates the type of conversion the parameter will accept. In the previous example, we can provide two double parameters. Because defaultbinder supports the conversion from double to int, the method can still be successfully called.
  • It indicates whether optional parameters are used in the parameter list.

All of these (especially the type conversion table) NamesType.Defaultbinder PropertyFor more details. We can also create our own binder object by inheriting the binder class. However, in most cases, it is enough to use a defaultbinder instance.

If an exception is thrown during later member binding, invokemember () intercepts the exception and throws a system. reflection. targetinvocation exception type exception. Naturally, an exception thrown in a method is referenced by the innerexception attribute in the thrown exception.

Finally, you cannot access non-public members when creating a later binding. Otherwise, system. Security. securityexception is thrown. However, if the typeinformation flag of system. Security. permissions. reflectionpermissionflags is set to true (you can access it through an instance of the system. Security. permissions. reflectionpermission class), you can access non-public members. If the memberaccess flag is set to true, you can access the non-visible type (that is, encapsulated in another type in non-public mode) [2].

2. One binding and multiple calls

We can see that a constructorinfo instance can be used to create a later binding to call a constructor. In the same way, a system is used. reflection. A methodinfo instance can also create a later binding and call any method. The advantage of using the methodinfo class instead of the type. invokemember () method is that it can save time for searching members for each call, resulting in some performance optimization. As shown in the following example.

Example 7-9

3. How does VB. NET bind after you create it?

Let's add some additional notes for VB. NET and observe how the language secretly uses post-binding after the strict option is set to off. For example, the following VB. NET program ......

Example 7-10 VB. NET and later binding

...... Equivalent to the following C # program:

Example 7-11

7.2.6 Using Interfaces: use the correct method for later binding

To use a class that is unknown during compilation, in addition to the post-binding method we have introduced, there is another completely different method. This method has a great advantage, because it has almost no performance loss compared with early or dynamic binding. However, to use this "secret", you must force yourself to follow certain specifications (in fact, the design pattern named plug-in ).

Our idea is to ensure that an interface is implemented for an unknown type during compilation, which is known to the compiler. Therefore, we have to create a third assembly to host this interface. Let's use three sets to rewrite calc:

Example 7-12 code of the Assembly containing the interface (interfaceasm. CS)

Example 7-13 code of the Assembly containing the target class (classasm. CS)

Example 7-14 code of the customer assembly whose target class is unknown during the compilation period (programasm. CS)

Note: You can call the sum () method by explicitly converting the objects returned by the createinstanceandunwrap () method to the ic1c type. We can also use the generic overload version activator. createinstance <ic1c> () to avoid type conversion.

Figure 7-2 summarizes the organizational structure of the three sets and their relationships.


Figure 7-2 plug-in design mode and Assembly Organization Structure

According to the idea behind the plug-in design pattern, the data necessary for creating an instance in the createinstanceandunwrap () method ("foo. DLL and nmfoo. calcwithinterface) is usually saved in the configuration file. In this way, you can select a new implementation by modifying the configuration file without re-compiling.

A variant of the plug-in design pattern is the use of abstract classes instead of interfaces.

Finally, you should know that you can also use the delegate to create a post-binding method. Although this method is more efficient than methodinfo, we generally prefer the plug-in design mode.

7.3 what is attribute7.3.1 attribute?

An attribute is the information of an element in the Code. This element can be a class or a method. For example,. NET Framework provides system. obsoleteattribute, which can be used to mark a method, as shown below (note the [] brackets syntax ):

The FCT () method is marked with the system. obsoleteattribute information, which will be inserted into the Assembly during compilation and can be used by the C # compiler in the future. When this method is called, the compiler sends a warning prompting you to avoid calling the discard method, because this method may disappear in future versions. If there is no attribute, you have to use a suitable document to express the fact that the FCT () method is currently obsolete. The disadvantage of this practice is that, it cannot be guaranteed that the customer will read the document to know that this method is currently obsolete.

7.3.2 When attribute is required

The advantage of using attribute is that the information contained in it is inserted into the assembly, which can be used for different purposes at different times:

  • Attribute can be used by the compiler. The system. obsoleteattribute just introduced is a good example. Some standard attributes specific to compilers are not saved in the Assembly. For example, serializationattribute does not directly add a specific tag to a type, but only tells the compiler that the type can be serialized. Therefore, the compiler sets some flag on the specific type that will be used by CLR during execution. Attributes such as serializationattribute are also known as pseudo attribute.
  • Attribute can be used during CLR execution. For example,. NET Framework provides system. threadstatic-attribute. When a static field is marked with this attribute, CLR ensures that only one version of this field is available in each thread during execution.
  • Attribute can be used during debugger execution. Therefore, you can use system. Diagnostics. debuggerdisplay-attribute to customize the display content of an element (such as the status of an object) in the code during debugging ).
  • Attribute can be used by a tool. For example, the. NET Framework provides system. runtime. InterOP-services.comvisibleattribute. when a class is labeled with attribute, the tlbexp.exe tool will generate a file for the class so that the class can be used as a COM object.
  • Attribute can be used during code execution. In this case, you need to use the reflection mechanism to access attribute information. For example, it is interesting to use attribute to verify the value of a field in the class. A field must be in a certain range, and a reference field must not be blank. A string field can contain a maximum of 100 characters ,...... The reflection mechanism makes it easy to write code to verify the status of any marked field. Later, we will show an example of using attribute in code.
  • Attributecan be used when you use tools such as ildasm.exe or reflector to analyze the assembly. Therefore, we can imagine that an attribute will assign a character string to an element in the code that describes its features. Because the string is included in the Assembly, we can directly view the annotations without accessing the source code.
7.3.3 what attribute should know
  • An Attribute must be defined by a Class inherited from system. attribute.
  • An instance of Attribute Class is instantiated only when accessed by the reflection mechanism. According to its usage, an attribute class may not be instantiated (attributes like system. obsoleteattribute do not need to be used by reflection mechanisms ).
  • . NET Framework has some built-in attributes for use. Some attributes are specifically provided for CLR, while others are used by compilers or Microsoft tools.
  • You can create your own attribute classes, but they can only be used by your program because you cannot modify the compiler or CLR.
  • Traditionally, the name of an attribute class is suffixed with attribute. However, in C #, an attribute named xxxattribute can be expressed by xxxattribute or XXX When marking elements in code.

In the chapter devoted to generics, we discussed the overlapping rules between attribute and generic concepts in section 13.10.3.

7.3.4 code elements that can be applied to attribute

Attribute will be applied to various elements in the source code. The following are all elements that can be marked with attribute. They are defined by the attributetargets enumerated values.


7.3.5. NET Framework

To better understand an attribute, you must first understand its application scenarios. Therefore, each standard attribute will be described in the section specifically referring to them.

For some security-related attributes, see section 6.6.

For some attributes related to the P/invoke mechanism, see section 8.1.

For some attributes related to the use of COM in. NET applications, see section 8.4.3 of the original book.

For some attributes related to the serialization mechanism, see section 22.3.

For some attributes related to XML serialization, see section 21.9.2 of the original book.

System. runtime. remoting. contexts. synchronizationattribute that allows a synchronization mechanism can be found in section 5.9.

To enable the prompt compiler to perform Conditional compilation for certain methods, see section 9.3.2.

For threadstaticattribute used to modify thread behavior for static fields, see section 5.13.1 of the original book.

The clscompliantattribute used to indicate whether the compiler must perform some verification. For details, see section 4.9.2.

For paramarrayattribute used to implement the Params C # keyword, see section 4.9.2.

For more information about categoryattribute, see Section 18.5.

7.3.6 example of custom attribute

A custom attribute is an attribute that you create for yourself by defining a class that inherits from system. attribute. Similar to the field validation attribute we mentioned at the beginning of this section, we can imagine that in many cases we can benefit from using custom attributes. The example we will show is inspired by the nunit open-source tool.

The nunit tool can be used to test any method in any assembly. Since there is no need to test every method in an assembly, nunit only executes the methods marked with testattribute.

To implement a simplified version of this feature, we will make the following constraints.

  • If no uncaptured exception is thrown, this method is considered to pass the test.
  • We have defined a testattribute that can only be applied to methods. This attribute can be used to configure the number of times the method must be executed (using the testattribute. ntime attribute of the int type ). This attribute can also be used to ignore a labeled method (through the testattrie. Ignore attribute of the bool type ).
  • The program. testassembly (assembly) method allows execution of all methods included in the Assembly labeled with testattribute, and the Assembly is passed into the method as a parameter. For simplicity, we assume that these methods are public, non-static, and do not accept any parameters. We must also use post-binding to access these labeled methods.

The following program satisfies these constraints.

Example 7-15

Program output:

Let's make some annotations.

  • We marked an attributeusage attribute for the testattriage class. We use the attributetarget enumerated value to tell the compiler that testattribute can only be applied to methods.
  • Set the allowmultiple attribute of the attributeusage class to false to indicate that a method will not accept multiple attributes of the testattribute type. Note the special syntax used to initialize the allowmultiple attribute. We call allowmultiple a famous parameter.
  • When marking testattrie for the foo. crashbutignore () method, we also use the syntax of the famous parameter.
  • When an exception is thrown and is not captured during the execution of a method, because the method is called through later binding, therefore, the method that calls this method will generate a targetinvocationexception type exception that overwrites the original exception, and the original exception is referenced by the innerexception attribute that overwrites its exception.
  • To avoid code spreading across multiple sets of programs, the program will perform self-testing (in fact, only methods in the foo class are tested because they are labeled with testattribute ). Figure 7-3 shows the assembly structure that will appear after the code is dispersed.

Figure 7-3 Assembly Structure

Figure 7-3 is similar to Figure 7-2. In both cases, it is through an intermediary that both know during compilation (in this example, it is an attribute, and in the previous example it is an interface) to use an element that is unknown during compilation.

7.3.7 condition attribute

C #2 introduces the concept of condition attribute. A condition attribute is considered by the compiler only after a symbol is defined. The following example shows how to use the condition attribute in a project generated by three files.

Example 7-16

Example 7-17

Example 7-18

In the example in the previous section, condition attribute is used to Generate debug and release versions from the same piece of code. In section 9.2.2, we introduce another use of conditional attribute.

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.