Reflection 2 Reflection Objectives Objectives The objective of this module is to provide an introduction into the Reflection capabilities of the common language runtime of Microsoft .NET. Reflection allows applications to explore their own structure at runtime. Many internal mechanisms inside the .NET Framework are driven by this structural information (called “Metadata”) and, thus, Reflection is one of the most fundamental aspects of the Common Language Runtime environment that you need to understand to explore the advanced features of .NET. What You Will Learn The core concepts of Reflection and .NET Metadata The System.Type class The member information classes of System.Type Creating code with System.Reflection.Emit Reflection 3 Section 1: Overview Section 1: Overview In this first section we will look at what type information is available and how it is made available in the “unmanaged world” of COM. We will also look at some of the lessons that have been learned from the experience with this model and how this led in the new Metadata model of .NET Looking back: Automation What’s wrong with that? .NET Reflection Core Concepts 4 Reflection Section 1: Overview Looking Back: Automation Reflection is all about Metadata and the structural composition of applications. Of course, this concept is not new. Therefore we will first look back at how type description and type exploration could be performed in the COM world. Type libraries contain type information In COM, most type information is contained in type libraries. A type library is a binary file, which is provided alongside with or embedded in the server component. Type libraries can be explored through the ITypeLib and ITypeInfo interfaces and are generated by the MIDL compiler. Attributes describe behavior of elements The type information contained in these libraries is attributed. Each interface, method or argument can be augmented with attributes, which describe their behavior. These are for instance the [in] or [out] attributes for method arguments, which define the directionality of marshaling. Other attributes allow to distinguish between simple instance methods and methods that are used to set or gets property values. The interface identifier is also associated with the interface declaration using an attribute. Dynamic Invocation using IDispatch The COM "automation" model allows late binding through the IDispatch interface. The IDispatch interface allows the dynamic expiration of type descriptions, which the server may either retrieve and expose from a type library or will dynamically create at runtime. The type information that Reflection 5 is exposed through the IDispatch interface is structurally identical to the information model of ITypeInfo. Type system supports most popular simple types The type system that can be used with COM and through the ITypeInfo, ITypeLib and IDispatch interfaces supports the most popular simple data types, but is very limited in supporting complex types. Because COM is a binary standard, types that require a certain in-memory organization (like strings) are COM-native and need special handling or care by the server and client implementers. Dynamic types using VARIANT Dynamic types are available through the VARIANT data type. Variants are COM's universal containers, which can transport values of all native COM data types. Arrays passed using SAFEARRAY Arrays of values are past using the SAFEARRAY type. The SAFEARRAY is special structure, which allows passing multidimensional blocks of homogeneous types between COM components. Strings expressed as BSTR COM strings are expressed as BSTR types, which is a zero terminated string with two lead bytes that indicate the length of the string. 6 Reflection Section 1: Overview What's wrong with that? Automation client model favors VisualBasic The main problem with automation is that the client model clearly favors Visual Basic another rapid application development languages, where the underlying runtime can hide much of the complexity. Automation server model favors C/C++ The server side, however, is better supported by low-level languages such as C and C++, because only those allow a level control that lets you leverage all features of the COM automation infrastructure which can then be exposed to and consumed by automation clients. This is in line with original vision of automation, where Visual Basic was thought to be used as a language that could control but not necessarily implement large COM servers. This has of course changed or while ago, but the bias in the COM infrastructure is still visible. ITypeInfo and IDispatch are way too complicated Exploration of the ITypeInfo and IDispatch information is a very complicated task. There are a few dozen of structures to deal with the navigation through the type information is neither is straightforward nor easy. So although quite a few aspects of attributed programming as it is now promoted in the .NET world, is possible in COM with type libraries and custom attributes that can be added to those libraries. But because evaluating this type information is a complicated and was never really seen a serious option for most developers. A good example for this extensibility are the custom type library attributes that are available for COM+ and the Microsoft Transaction Reflection 7 Server, which allow you to flag components as transactional in the IDL file, and when imported into COM+ these attributes are being read and the components are configured correctly. Information model is limited, no support for complex types Another problem is that the information model is too limited. While it has been proven millions of times, that the COM information and type model is sufficient for most scenarios, the lack of ability to marshal complex types through the dynamic binding interfaces has often lets the implementation of wrapper interfaces around structures, which should technically have been marshaled by value and made available natively to the client application. Doing so without implementing custom marshaling through IMarshal (which is not a widespread skill or dare) will result in objects being exposed by reference and increased traffic between client and server. SAFEARRAYs are a pain from C/C++ The SAFEARRAY type, which is COM's native array, needs a lot of very special attention in C and C++ programs through a special API, which can only be described as being a pain in terms of usability. BSTRs are difficult to manage from C/C++ The BSTR string type is also problematic and a common source of bugs in COM applications, because it looks suspiciously like any other zero-terminated string but requires special allocation and de-allocation functions. Type information separate from code Another issue is that the type information is separate from the code. Just as you have "DLL hell" with multiple versions of the same components on the same machine, there exists a very related problem with multiple type libraries for these different versions. 8 Reflection Section 1: Overview .NET Reflection Core Concepts .NET reflection and its core concepts build on the experience of the COM type library model. Applications are able to evaluate the type information at runtime and invoke services in a late bound fashion. The two core elements that enable this are "Metadata" and .NET's dynamic common type system. Metadata Metadata provides a single location for the type information and code. In .NET assemblies of the entire structural information about application is bundled together with a description of its runtime behavior, expressed in the Microsoft intermediate language. The structural information describes the overall shape of assemblies and modules into detail each data type and class. Class methods and all other active entities are stored with their call signature and return value types and their MSIL code. MSIL is therefore really part of the description of any method, because the actual code that is executed at runtime is a compiled representation of the MSIL in native machine code. While the COM type library information is restricted to those types for which inclusion in the library was explicitly requested, Metadata is available and accessible for each and every type that is being used in .NET managed code. Every .NET types, from simple scalar types such as a plain integer to complex classes can be queried for its type. At runtime, data types are represented by special .NET runtime class, System.Type. This class allows close inspection of every aspect of a runtime data type. The Reflection 9 framework's ability to make type information available at runtime through this class is what is called "reflection". Dynamic Type System The universal exploration of type information is enabled by the dynamic common type system (CTS). All languages that target the .NET framework share the CTS. The language compilers automatically create the type information stored in the Metadata, which can later be read by applications written in any other language. Because the underlying type system is now independent of the implementation language used, the restricted type model that we know from COM no longer applies and the exchangeable type information is equivalent to the type information used to build applications. Under the hood, the type information exposed through reflection is sometimes even richer than what can be expressed using the intrinsic language elements of languages like C#. The "Emit" part of reflection, which is covered in the second part of this module allows types the extended (by inheritance) or be entirely build at runtime. This allows for the on the fly creation of new assemblies and modules and in fact .NET compilers use .NET to emit .NET code. 10 Reflection Section 2: Exploring Metadata Section 2: Exploring Metadata Before we start drilling into the basics of Reflection, a word of advice especially for those who will actively present this topic to an audience: Reflection is one of the most fundamental technologies of the .NET platform and, yet, at the same time it is very simple to use on the surface. This section is likely one of the longest discussions of a single class, System.Type, among all modules in this kit, but because of its fundamental importance it really deserves the space and detail of the discussion. System.Type should be just as present in the mind of every developer as System.Object, because Reflection opens the doors to the power of attributed programming and type-driven functionality. Who are you? What are you? Anything special about you? Now tell me what you have! Types and Instances Reflection 11 Section 2: Exploring Metadata Metadata: Type Info at Runtime The illustration in the associated slide unravels much of the mystery of Reflection. Essentially, once you are able to grasp what the slide is demonstrating, you have already entirely understood what Reflection is all about. In the illustration you see that the code in the method “Save” invokes the method “GetType()” on the object‟s “this” self-reference. This method is always available on any .NET class, since it is a member of the fundamental System.Object class that is the base for any managed code class, regardless of whether you explicitly declared that inheritance relationship. All .NET compilers will establish that relationship automatically “under the hood”. The method “GetType()” returns an instance of the System.Type class, which encapsulates access to all Metadata of the object you “asked”. The System.Type instance will allow you to traverse into all details of the type or class (excluding, specifically, the IL code) and also explore the environment like the implementing module and container assembly. The following discussion will center on System.Type and look at the capabilities of this single class in very much detail. 12 Reflection Section 2: Exploring Metadata Who are you? Accessing metadata: System.Object.GetType() To sum up what you could see on the diagram: Because everything is essentially a class in .NET and all classes are derived from System.Object, which exposes the Reflection “gateway” GetType() as one of the most fundamental mechanisms in .NET, you can reflect every single type in .NET, including the simple, scalar types such as plain integers. Explicit language support for type metadata Although this mechanism is intrinsic to every object, the .NET languages have additional keywords and helpers to use and access Metadata though Reflection. The issue with GetType() is, of course, that it only works on active instances and not on class expressions as such. In C# and JScript.NET you can therefore use the intrinsic typeof(classname) expression to obtain the Metadata for the class without having to create an instance. The alternative for Managed C++ and Visual Basic.NET is of course to use the “System.Type.GetType(„classname‟)” method, but at the price that you need to express the class with it‟s fully qualified namespace while you can just use a scoped expression in C# or Jscript.NET (that is, without the namespace qualifiers). In Visual Basic.NET you can use the “TypeOf” operator as part of an “If … Then … Else” expression to test whether an arbitrary variable expression is of a certain class type. Reflection 13 Determining Type Identity Each type that you declare has a unique identity across any assembly. Because of .NET‟s ability to assign “strong names” to assemblies to make their definition space globally unique, each type indirectly also gains this uniqueness quality. Furthermore, the mandatory namespace use in .NET further eliminates the collision potential. If you want to determine whether two expressions are of identical type, you can therefore directly compare their type objects. 14 Reflection Section 2: Exploring Metadata System.Type The core of all things in Reflection is really System.Type Access to metadata for any .NET type Every instance of System.Type is a dynamically created wrapper around the Metadata of certain class. When you invoke “GetType()” on an object, the runtime will gather the requested Metadata, stuff it into a fresh System.Type instance and hand it to you. Returned by System.Object.GetType() Instances of the System.Type class are most prominently returned by the System.Object.GetType() method as this has been shown previously, but as you descend into the depths of the .NET framework you will find that Type objects are used quite often throughout the lower-levels of the Framework hierarchy. Allows drilling down into all facets of a type The System.Type class will allow you to explore all facets of a type like the type “category”, a class‟ or structure‟s methods, constructors and their parameters and return values. Furthermore, of course, all other aspects of such types like properties, fields, events and delegates and, of course, the namespace the class is located in can be explored. Reflection 15 Section 2: Exploring Metadata What are you? When we zoom in on the System.Type class you will find that we can ask quite a few very precise and very simple questions through its methods as this is shown on the slide. Covers all possible properties of a managed type These very plain calls indeed expose all core information of a managed type. Very intuitive API, no "Parameter Hell" The bottom line here is simplicity. You do not need to haggle around with flags buried in bit-fields, but can just ask a very readable and clear question and get a “yes” or “no” response. Especially to C/C++ developers this may look a bit too verbose at first, but if you really go back into your own code you will find that while you typically retrieve such information encoded into a single bit-field (like a 32bit unsigned integer), for instance when using the Win32 API, you mostly end up testing the values individually by isolating them from the set. 16 Reflection Section 2: Exploring Metadata Anything special about you? Here you have some of the more advanced “Is questions” for the System.Type class. Special Memory Layout? For P/Invoke and COM/Interop purposes, structures may be declared with special memory layout rules that define exactly how the data of a structured is rendered into a memory block when it is passed into and from unmanaged code. The three properties “IsAutoLayout”, “IsExplicitLayout” and “IsLayoutSequential” will tell you whether the in-memory layout is automatic (managed) or if any special rules apply. Typically, there is little reason for you to query these properties, because you will very likely not be able to make too much use of that information. The existence of these methods does, however, demonstrate how essential Remoting is for the Framework itself. When an object is passed to managed code, the P/Invoke and COM/Interop layers will indeed use the information obtained through Reflection to properly format the outgoing and incoming binary data blocks. COM Objects and Unmanaged Types? You can also tell whether an object is really a proxy to a COM object. There are a set of special rules such as restrictions in implementation inheritance when integrating COM classes into .NET applications or you may want to explicitly release and discard the COM object from its Runtime Callable Wrapper (RCW) and this is a way to tell whether this applies to a certain object. Reflection 17 Section 2: Exploring Metadata Now tell me what you have! Complex types have much more to offer and tell than their name and fundamental properties. The “Get*” methods of the System.Type provide access to the fields, properties and methods of a managed type. Finding and Exploring Members For generic access to all members, independent of their exact role, you can either use the “GetMembers()” method to retrieve a complete array of MemberInfo structures (to be explained in detail a bit later) or seek for a certain member by name using the “FindMembers” method. If you want to get only the fields or methods, you can use one of the role-dependent access methods: Exploring Fields, Properties, Constructors, Methods and Events To get an enumerable array of members arranged by the role they play in the context of a class you can call the respective function. If you want to have all fields, you call “GetFields()”, for properties “GetProperties()” , etc. Exploring attributes, determining implemented interfaces, enumerating nested types, … Likewise, there are methods that give you access to the attributes that are declared on a type and you can also retrieve a list of implemented interfaces and types that are declared within the scope of the current type. Summary: Everything you may ever want to know In short, every structural element that you can express in a language like C# is available through reflection. 18 Reflection Section 2: Exploring Metadata Type and Instances Type Safety First! Type checking at runtime In summary, the information available through Reflection allows for more type-safe programming resulting in more solid code. You are now able to do runtime type-checks throughout the .NET platform by actively checking types before you execute an action instead of catching typecast exceptions (or worse) after you have attempted to execute something on a non-matching type. The C# and Visual Basic.NET languages, like other languages available for the .NET platform, exposes this capability through an extra language keyword. If C# you can test an instance of a class using a “object is class” expression and in VB.NET you can use the more verbose “If TypeOf object Is class Then …” expression. Dynamic Invocation through Reflection Reflection will not only let you walk through types to obtain information about them, but will also let you make calls to class instances, if you use Reflection to implement late bound behavior. Methods can be invoked through Invoke() on the MethodInfo objects returned by GetMethods() and fields can be set with the SetValue() method on the FieldInfo objects returned by GetFields() or on the PropertyInfo returned by GetProperties(). Reflection 19 Section 3: Detail Information Section 3: Detail Information The System.Type class returns a lot of information by using helper classes such as the MemberInfo class. In this section we are taking a closer look at these classes and also how to obtain information on the larger context of a reflected class. You will also learn about Reflection‟s unmanaged sibling, the Metadata API, which makes .NET Metadata natively available to unmanaged code. MemberInfo FieldInfo, PropertyInfo ConstructorInfo, MethodInfo The Bigger Picture The Unmanaged Spy: Metadata API 20 Reflection Section 3: Detail Information MemberInfo Base class for all "member" element descriptions The MemberInfo class is the base class of all Reflection information classes for subordinate elements of System.Type. If you enumerate all members of a type using the System.Type.GetMembers() method, an array of MemberInfo objects will be returned that will provide you with basic information about every subordinate element and you can indeed use Reflection itself to test whether a given MemberInfo is of type MethodInfo and do a safe type-cast to explore the specific capabilities. Provides member kind, name and declaring class The MemberInfo base class provides information about the element‟s name, its “kind” (that is whether it is indeed a method, field or on of the other kinds) and which its declaring class is. Reflection 21 Section 3: Detail Information FieldInfo, PropertyInfo FieldInfo The FieldInfo class contains the member information for a certain field of a class (or structure) including its name and data type and attributes that have been declared on the field inside the structure of class. You will also be able to find out whether the field is an instance field or class field (static) and determine its protection level. Lastly, you can also manipulate a field‟s value through Reflection, which is essential for tasks like interception in .NET Remoting. PropertyInfo The PropertyInfo provides you with detail information about a certain property. In addition to the information that is available for fields as discussed above, it also contains the method information for the associated “set” and “get” methods, parameter information if the property is an indexer and you are, of course, able to invoke the “set” and “get” methods directly through the PropertyInfo class. 22 Reflection Section 3: Detail Information MethodInfo, ConstructorInfo MethodInfo The MethodInfo class gives you all details about the signature, attributes and return values of a certain method. All parameter descriptions are accessible in order through the GetParameters() method and low-level details about the embedded IL code are available through a flag-field that can be obtained through a call to the method GetMethodImplementationFlags(). The method can also be invoked directly through the MethodInfo class, given an object to execute the call on. ConstructorInfo The ConstructorInfo class, derived from the same base class as MethodInfo (MethodBase), is very similar to the MethodInfo class, but reflects the very specialized initialization methods (constructors) for classes. These should not be called except at construction time and are therefore separated from the methods. Reflection 23 Section 3: Detail Information Attributes Custom attributes are the killer-app for Reflection! Custom attributes are the real killer application for Reflection and something that you should really consider looking at very closely. The Reflection features that we highlighted up to here, like all the detailed member information are certainly useful for implementing late-bound applications and for creating more solid code, but attributes indeed enable a whole new development paradigm: attribute-driven programming. Attributes enable declarative behavior Attributes are special elements (classes) in the .NET Framework that allow you to augment any structural element of .NET classes and structures with auxiliary information that is not immediately part of the class‟s runtime behavior but serves to provide additional information that is either being evaluated by the runtime and the .NET Framework or your own frameworks. An example for this is the [serializable] attribute, which tells the Serialization framework that it is allowed to serialize a class‟s state as-is and using the class Metadata for Remoting or persistent storage. If the attribute is not present, the class implementer essentially denies that permission and either provides an own implementation for serialization through the ISerializable interface or does not permit serialization and remote marshaling at all. As such, simple attributes will only be instantiated if they are explored via Reflection. They are indeed specifically created for this purpose. 24 Reflection Attributes allow data augmentation Another possible use is the augmentation of data elements with additional hints. The shown example assumes a (imaginary) persistency framework that uses attributes as hints to match class fields to database columns. This is the type of information that had previously been expressed using macro-driven “maps”, which associate members with numbers or text expressions in frameworks like the ATL or MFC class libraries. With attributes, all this information can now be placed where it belongs: At the field declaration itself. Reflection 25 Section 3: Detail Information The Bigger picture Types know their Module, Modules know their types Another strong point of the Metadata is that it allows navigating the entire context of a type. A module, reflected by the System.Reflection.Module class, knows all of the types it defines and implements and each types knows its implementing module. Modules know their Assembly and vice versa Likewise, all modules know and make their container assembly accessible and the assembly can list all modules it contains. Code can browse and search its entire context Finally, the Application Domain (AppDomain) class of the “current” application knows all loaded assemblies, so that you have a full information model about an application‟s Metadata at any time and from wherever you need it. 26 Reflection Section 3: Detail Information The Unmanaged Spy: Metadata API Unmanaged (COM) Version of Reflection While the Reflection API is accessible only from managed code, tool vendors that want to produce managed code applications from their IDEs or applications that want to interact with managed code may indeed be largely implemented in COM, like the initial release of Microsoft VisualStudio.NET. For these scenario, the .NET Framework provides an alternate implementation of the full functionality of the managed Reflection API as a COM version, which allows you to explore then content and full type information of managed code assemblies from within COM based applications. Used by VisualStudio.NET and Compilers VisualStudio.NET and the .NET compilers, which are mostly unmanaged code in their initial releases (one reason is that bootstrapping the entire environment in one step would be just too difficult in terms of complexity – even for Microsoft) use the COM based version of Reflection to reflect on and emit managed code. Full Access to all Reflection Information for Tools The COM “Metadata API” is in no way a second-class implementation, but has to be seen as a side-by-side sibling to the managed Reflection API. Fully documented in the "Tool Developer's Guide" The complete reference documentation for the “Metadata API” is located in the “Tool Developer‟s Guide “portion of Reflection 27 the Framework SDK and is automatically installed with the kit. Buddy: Assembly Metadata API The Metadata API‟s “buddy” is the Assembly Metadata API, which is also documented in the Tools section and provides navigable access to the Assembly manifest and allows you to produce such manifests from code. 28 Reflection Section 4: Building Types at Runtime Section 4: Building Types at Runtime In this section we are going to cover “the other side” of Reflection, the “Emit” namespace. The informational and navigational capabilities of reflection are already fascinating, but the “Emit” namespace and its classes take this even a step further by enabling you to create .NET applications from within .NET programmatically and without having to go through the complexity of writing binary formats yourself. With this, System.Reflection.Emit is in fact democratizing automatic code generation and lets developers wanting to create code or Metadata from within their applications focus on just that and not on details of the operating system‟s loaders. Introducing System.Reflection.Emit Why? Some scenarios … Dynamic Modules and Assemblies Creating Types and Classes Writing IL Reflection 29 Section 4: Building Types at Runtime Introducing System.Reflection.Emit Full representation of physical structure The classes in the System.Reflection.Emit namespace reflect the entire physical and logical structure of an assembly and its embedded modules and types. Allows building modules and assemblies at runtime With this, you can create full functional assemblies and modules from within .NET managed code applications (or through the equivalent functionality in the COM Metadata API) at runtime. This allows you to create transient assemblies that reside only in memory for as long as the application is active and to create assemblies that you can persist into files and which can later be used just like any other assembly. Create classes, types and emit IL Inside any assembly‟s modules you can create classes, types and emit code expressed in the Microsoft Intermediate Language (IL) for which you can find the full documentation and specifications in the Tool Developer Section of the Framework SDK. Used by .NET compilers to build .NET apps The .NET compilers and runtime components to generate .NET code use the “Emit” part of Reflection. Managed code uses the managed System.Reflection.Emit portion and unmanaged code uses the unmanaged COM API. 30 Reflection Section 4: Building Types at Runtime Why? Some Scenarios… So why would you want to generate code programmatically instead of writing C# (or any other language) code and compile it by “traditional” means? The list of scenarios is indeed endless and only limited by your imagination. The following is really just a very short collection of ideas what you could do: Build classes dynamically from script-like code You can use Reflection to create classes dynamically from script-like code. In .NET, the Regular Expression compilers is an actual example of a not-really-a-completeprogramming-language programming language that scans and parses input text data and emits managed code directly from the parse tree via Reflection. If your application exposes user-definable formulas or scripting like features, you can use Reflection.Emit to turn these scripts into actual IL code and consequentially into machine code at execution time. Generate code from visual development tools If you use CASE-like tools that express class framework skeletons or even more in-depth specifications in, for instance, UML, you can write code generators that emit the full Metadata for the base classes that business-logic application developers must derive from to implement the logic using your own code-generators that read the UML (or XMI) model and produce the code via Reflection.Emit. Create dynamic wrappers for existing code You may also want to create dynamic wrappers for existing, unmanaged code. A tool that scans C header files or a COBOL copy-section and produces a managed Reflection 31 code assembly that uses P/Invoke to interface with such existing code-bases could, for instance, produce such dynamic wrappers that let you leverage existing codebases natively. Transfer code-chunks to remote machines Reflection.Emit can also be used in conjunction with Remoting to transfer and regenerate code on remote machines to execute tasks there. Possible scenarios for this include distributed numeric processing mirroring the SETI@home project where the processing code could be sent along with the data payload and emitted and run at the destination. The .NET security model‟s cascaded sand-boxing that let‟s you control the permissions for any code that are calling prevents malicious use of these features. 32 Reflection Section 4: Building Types at Runtime Building Assemblies System.Reflection.Emit.AssemblyBuilder The AssemblyBuilder class is at the root of every “Emit” process in that it allows to create new assemblies, which are required to host modules and their classes and associated code. Dynamically create new assemblies The AssemblyBuilder will enable you to create the full manifest for an assembly, associate new modules and resources and specify attributes on the assembly like requested security permissions that apply when the assembly is loaded into an application domain. Can be persisted as files or held in memory, act and behave like any other assembly The AssemblyBuilder state may be saved into a file or just held in memory. If the state is saved, a full features Assembly file with all its subordinate modules is written to the requested location that is in no way different than the files produced by, for instance, the Visual C# compiler on disk or at runtime. Reflection 33 Section 4: Building Types at Runtime Building Modules System.Reflection.Emit.ModuleBuilder The ModuleBuilder allows for the complete programmatic definition of a module, which is .NET‟s concept of a codecontainer. Modules contain types, classes and code Modules contain types, classes and code for one or multiple namespaces including their intermediate language code. All of them can be built with the TypeBuilder, MethodBuilder, EnumBuilder classes and their siblings, which can all be created and accessed via the ModuleBuilder class. Allows creating fully functional modules with code The ModuleBuilder allows you to define complete, dynamic modules with all features. Not only can they contain type and class declarations and class implementations but you can also add advanced information that can be used for diagnostic purposes. Can associate source code You can, for instance, associate references to source code documents that were used to produce the output. Emit debug symbols Likewise, you can also emit full symbolic debug information and actually enable .NET debuggers to debug your applications although the code was generated based on your own custom language. 34 Reflection Acts like any module created by any .NET compiler In the end, the Reflection.Emit produced modules can act exactly like any other module in terms of functionality. Reflection 35 Section 4: Building Types at Runtime Writing IL Emit accepts IL (Intermediate Language) code The MethodBuilder exposes the method “GetILGenerator” which returns an instance of the ILGenerator classes that eases emitting IL code. The IL code is automatically associates with the method the generator was requested for. Same code as emitted by C#, VB.NET, Eiffel# Since all of the .NET compilers (with a partial exception being C++) produce IL as well, the output from this can be exactly the same as from any of these languages. IL is optimized and translated into machine code The IL is then optimized fro the target machine when it is translated into machine code by one of .NET‟s JIT compilers. Reflection.Emit puts you "on-par" Essentially, Reflection.Emit can put you on par with all other development tools out there in that it makes codegeneration more accessible, allows you to use the same optimizing back-ends as any other .NET compiler. In .NET programming languages are very smart and syntax aware scanners and parsers that can translate the expressions into terms of the IL. For more info see System.CodeDOM For more information on compiler support you should also take a look at the System.CodeDOM namespace. 36 Reflection Section 5: Putting it Together Section 5: Putting It Together In this last section, we want to take a look at two examples for how Reflection is used within VisualStudio.NET and ASP.NET VisualStudio.NET and Reflection What ASP.NET really does with a page Reflection 37 Section 5: Putting it Together VisualStudio.NET and Reflection VisualStudio.NET is certainly the busiest user of Reflection to-date. By looking at the information available in the VisualStudio environment once a project has been loaded, it can easily be derived that VisualStuido.NET obtains a major portion of the information presented from all sorts of assemblies by ways of Reflection. This becomes indeed most obvious in the localized, nonEnglish versions of VisualStudio.NET, since a large portion of the information indeed remains to be expressed in English, because it is pulled from Metadata. The Properties window for components or controls shown in the .NET designers will show browsable properties of .NET components based on attribute settings that it finds via Reflection. The component Metadata will also drive the way the designer windows that are hidden behind the “triple dot” (…) buttons are being launched and associated. The Toolbox window will know what can be displayed in the same way. 38 Reflection Section 5: Putting it Together What ASP.NET does with a Page ASP.NET is also heavily relying on Reflection. The process for creating the ASP.NET assemblies that are executed at runtime can be quickly explained as follows: First the ASP.NET runtime will check whether a requested file does actually exist. The file to be checked for is the actual ASPX text file. Second it will check whether a matching compiled assembly exists that has the same timestamp as the text file. If this is not the case, the ASP.NET compiler will compile the file into a Page class (expressed in C# or the page language) and explores the code-behind assembly via Reflection to be able to match up the page expressions against the code-behind class. In a second step, it invokes the page language compiler to translate the codegenerated Page into an assembly. The third step is to execute the Page class‟ handling code and return the result to the client. Reflection 39 Summary Summary Reflection = System.Type + GetType() At the heart of Reflection are the method GetType() that is available on each and every object in .NET and the System.Type class that it returns. If you could only remember a single thing about Reflection, this should be it. Explore Type Information for everything at Runtime The System.Type class and the additional classes in the System.Reflection namespace that accessible through it let you explore every structural element of .Net applications at runtime without extra effort at development or compile time. Enables Attribute-driven programming Reflection also enables the powerful Attribute-driven programming model that lets you add declarative behavior to code. Use Emit Classes to Produce .NET Assemblies You can use the System.Reflection.Emit namespace classes to create .NET Assemblies in much the same way as the .NET toolsets do it. Bottom line: Fully Self-Contained Structural Model To sum it all up: With Reflection, .NET provides and contains an entirely self-describing and self-contained structural model that does not require any external tools to create .NET compliant components.
Pages to are hidden for
"Reflection Reflection 2 Reflection Objectives Objectives The objective of this module"Please download to view full document