1. Trang chủ
  2. » Công Nghệ Thông Tin

Pro C# 2008 and the .NET 3.5 Platform, Fourth Edition phần 5 pptx

140 275 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 140
Dung lượng 4,26 MB

Nội dung

Displaying Various Odds and Ends Last but not least, you have one final helper method that will simply display various statistics (indi- cating whether the type is generic, what the base class is, whether the type is sealed, and so forth) regarding the incoming type: // Just for good measure. static void ListVariousStats(Type t) { Console.WriteLine("***** Various Statistics *****"); Console.WriteLine("Base class is: {0}", t. BaseType); Console.WriteLine("Is type abstract? {0}", t. IsAbstract); Console.WriteLine("Is type sealed? {0}", t. IsSealed); Console.WriteLine("Is type generic? {0}", t. IsGenericTypeDefinition); Console.WriteLine("Is type a class type? {0}", t. IsClass); Console.WriteLine(); } Implementing Main() The Main() method of the Program class prompts the user for the fully qualified name of a type. Once you obtain this string data, you pass it into the Type.GetType() method and send the extracted System.Type into each of your helper methods. This process repeats until the user enters Q to termi- nate the application: static void Main(string[] args) { Console.WriteLine("***** Welcome to MyTypeViewer *****"); string typeName = ""; bool userIsDone = false; do { Console.WriteLine("\nEnter a type name to evaluate"); Console.Write("or enter Q to quit: "); // Get name of type. typeName = Console.ReadLine(); // Does user want to quit? if (typeName.ToUpper() == "Q") { userIsDone = true; break; } // Try to display type. try { Type t = Type.GetType(typeName); Console.WriteLine(""); ListVariousStats(t); CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING532 8849CH16.qxd 10/9/07 4:27 PM Page 532 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com ListFields(t); ListProps(t); ListMethods(t); ListInterfaces(t); } catch { Console.WriteLine("Sorry, can't find type"); } } while (!userIsDone); } At this point, MyTypeViewer.exe is ready to take out for a test drive. For example, run your appli- cation and enter the following fully qualified names (be aware that the manner in which you invoked Type.GetType() requires case-sensitive string names): • System.Int32 • System.Collections.ArrayList • System.Threading.Thread • System.Void • System.IO.BinaryWriter • System.Math • System.Console • MyTypeViewer.Program Figure 16-2 shows the partial output when specifying System.Math. Figure 16-2. Reflecting on System.Math CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 533 8849CH16.qxd 10/9/07 4:27 PM Page 533 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Reflecting on Generic Types When you call Type.GetType() in order to obtain metadata descriptions of generic types, you must make use of a special syntax involving a “back tick” character ( `) followed by a numerical value that represents the number of type parameters the type supports. For example, if you wish to print out the metadata description of List<T>, you would need to pass the following string into your application: System.Collections.Generic.List`1 Here, we are using the numerical value of 1, given that List<T> has only one type parameter. However, if you wish to reflect over Dictionary<TKey, TValue>, you would supply the value 2: System.Collections.Generic.Dictionary`2 Reflecting on Method Parameters and Return Values So far, so good! Let’s make one minor enhancement to the current application. Specifically, you will update the ListMethods() helper function to list not only the name of a given method, but also the return value and incoming parameters. The MethodInfo type provides the ReturnType property and GetParameters() method for these very tasks. In the following code, notice that you are building a string type that contains the type and name of each parameter using a nested foreach loop: static void ListMethods(Type t) { Console.WriteLine("***** Methods *****"); MethodInfo[] mi = t.GetMethods(); foreach (MethodInfo m in mi) { // Get return value. string retVal = m.ReturnType.FullName; string paramInfo = "("; // Get params. foreach (ParameterInfo pi in m.GetParameters()) { paramInfo += string.Format("{0} {1} ", pi.ParameterType, pi.Name); } paramInfo += ")"; // Now display the basic method sig. Console.WriteLine("->{0} {1} {2}", retVal, m.Name, paramInfo); } Console.WriteLine(); } If you now run this updated application, you will find that the methods of a given type are much more detailed. Figure 16-3 shows the method metadata of the System.Globalization. GregorianCalendar type. CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING534 8849CH16.qxd 10/9/07 4:27 PM Page 534 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 16-3. Method details of System.Globalization.GregorianCalendar The current implementation of ListMethods() is helpful, in that you can directly investigate each parameter and method return value using the System.Reflection object model. As an extreme shortcut, be aware that each of the XXXInfo types (MethodInfo, PropertyInfo, EventInfo, etc.) have overridden ToString() to display the signature of the item requested. Thus, we could also imple- ment ListMethods() as follows: public static void ListMethods(Type t) { Console.WriteLine("***** Methods *****"); MethodInfo[] mi = t.GetMethods(); foreach (MethodInfo m in mi) { // Could also simply say "Console.WriteLine(m)" as well, // as ToString() is called automatically by WriteLine(). Console.WriteLine(m.ToString()); } Console.WriteLine(); } Interesting stuff, huh? Clearly the System.Reflection namespace and System.Type class allow you to reflect over many other aspects of a type beyond what MyTypeViewer is currently displaying. As you would hope, you can obtain a type’s events, get the list of any generic parameters for a given member, and glean dozens of other details. Nevertheless, at this point you have created a (somewhat capable) object browser. The major limitation, of course, is that you have no way to reflect beyond the current assembly ( MyTypeViewer) or the always accessible mscorlib.dll. This begs the question, “How can I build applications that can load (and reflect over) assemblies not referenced at compile time?” ■Source Code The MyTypeViewer project can be found under the Chapter 16 subdirectory. CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 535 8849CH16.qxd 10/9/07 4:27 PM Page 535 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Dynamically Loading Assemblies In the previous chapter, you learned all about how the CLR consults the assembly manifest when probing for an externally referenced assembly. However, there will be many times when you need to load assemblies on the fly programmatically, even if there is no record of said assembly in the manifest. Formally speaking, the act of loading external assemblies on demand is known as a dynamic load. System.Reflection defines a class named Assembly. Using this type, you are able to dynamically load an assembly as well as discover properties about the assembly itself. Using the Assembly type, you are able to dynamically load private or shared assemblies, as well as load an assembly located at an arbitrary location. In essence, the Assembly class provides methods (Load() and LoadFrom() in particular) that allow you to programmatically supply the same sort of information found in a client-side *.config file. To illustrate dynamic loading, create a brand-new Console Application named External AssemblyReflector. Your task is to construct a Main() method that prompts for the friendly name of an assembly to load dynamically. You will pass the Assembly reference into a helper method named DisplayTypes(), which will simply print the names of each class, interface, structure, enumeration, and delegate it contains. The code is refreshingly simple: using System; using System.Reflection; using System.IO; // For FileNotFoundException definition. namespace ExternalAssemblyReflector { class Program { static void DisplayTypesInAsm(Assembly asm) { Console.WriteLine("\n***** Types in Assembly *****"); Console.WriteLine("->{0}", asm.FullName); Type[] types = asm.GetTypes(); foreach (Type t in types) Console.WriteLine("Type: {0}", t); Console.WriteLine(""); } static void Main(string[] args) { Console.WriteLine("***** External Assembly Viewer *****"); string asmName = ""; bool userIsDone = false; Assembly asm = null; do { Console.WriteLine("\nEnter an assembly to evaluate"); Console.Write("or enter Q to quit: "); // Get name of assembly. asmName = Console.ReadLine(); CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING536 8849CH16.qxd 10/9/07 4:27 PM Page 536 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com // Does user want to quit? if (asmName.ToUpper() == "Q") { userIsDone = true; break; } // Try to load assembly. try { asm = Assembly.Load(asmName); DisplayTypesInAsm(asm); } catch { Console.WriteLine("Sorry, can't find assembly."); } } while (!userIsDone); } } } Notice that the static Assembly.Load() method has been passed only the friendly name of the assembly you are interested in loading into memory. Thus, if you wish to reflect over CarLibrary. dll , you will need to copy the CarLibrary.dll binary to the \bin\Debug directory of the External AssemblyReflector application to run this program. Once you do, you will find output similar to Figure 16-4. Figure 16-4. Reflecting on the external CarLibrary assembly If you wish to make ExternalAssemblyReflector more flexible, you can update your code to load the external assembly using Assembly.LoadFrom() rather than Assembly.Load(). By doing so, you can enter an absolute path to the assembly you wish to view (e.g., C:\MyApp\MyAsm.dll). Essentially, Assembly.LoadFrom() allows you to programmatically supply a <codeBase> value. ■Source Code The ExternalAssemblyReflector project is included in the Chapter 16 subdirectory. CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 537 8849CH16.qxd 10/9/07 4:27 PM Page 537 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Reflecting on Shared Assemblies The Assembly.Load() method has been overloaded a number of times. One variation allows you to specify a culture value (for localized assemblies) as well as a version number and public key token value (for shared assemblies). Collectively speaking, the set of items identifying an assembly is termed the display name. The format of a display name is a comma-delimited string of name/value pairs that begins with the friendly name of the assembly, followed by optional qualifiers (that may appear in any order). Here is the template to follow (optional items appear in parentheses): Name (,Version = major.minor.build.revision) (,Culture = culture token) (, PublicKeyToken= public key token) When you’re crafting a display name, the convention PublicKeyToken=null indicates that bind- ing and matching against a non–strongly named assembly is required. Additionally, Culture="" indicates matching against the default culture of the target machine, for example: // Load version 1.0.982.23972 of CarLibrary using the default culture. Assembly a = Assembly.Load( @"CarLibrary, Version=1.0.982.23972, PublicKeyToken=null, Culture="""); Also be aware that the System.Reflection namespace supplies the AssemblyName type, which allows you to represent the preceding string information in a handy object variable. Typically, this class is used in conjunction with System.Version, which is an OO wrapper around an assembly’s version number. Once you have established the display name, it can then be passed into the over- loaded Assembly.Load() method: // Make use of AssemblyName to define the display name. AssemblyName asmName; asmName = new AssemblyName(); asmName.Name = "CarLibrary"; Version v = new Version("1.0.982.23972"); asmName.Version = v; Assembly a = Assembly.Load(asmName); To load a shared assembly from the GAC, the Assembly.Load() parameter must specify a PublicKeyToken value. For example, assume you wish to load version 2.0.0.0 of the System.Windows. Forms.dll assembly provided by the .NET base class libraries. Given that the number of types in this assembly is quite large, the following application only prints out the names of public enums, using a simple LINQ query: using System; using System.Reflection; using System.IO; using System.Linq; namespace SharedAsmReflector { public class SharedAsmReflector { private static void DisplayInfo(Assembly a) { Console.WriteLine("***** Info about Assembly *****"); Console.WriteLine("Loaded from GAC? {0}", a.GlobalAssemblyCache); Console.WriteLine("Asm Name: {0}", a.GetName().Name); Console.WriteLine("Asm Version: {0}", a.GetName().Version); Console.WriteLine("Asm Culture: {0}", a.GetName().CultureInfo.DisplayName); Console.WriteLine("\nHere are the public enums:"); CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING538 8849CH16.qxd 10/9/07 4:27 PM Page 538 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com // Use a LINQ query to find the public enums. Type[] types = a.GetTypes(); var publicEnums = from pe in types where pe.IsEnum && pe.IsPublic select pe; foreach (var pe in publicEnums) { Console.WriteLine(pe); } } static void Main(string[] args) { Console.WriteLine("***** The Shared Asm Reflector App *****\n"); // Load System.Windows.Forms.dll from GAC. string displayName = null; displayName = "System.Windows.Forms," + "Version=2.0.0.0," + "PublicKeyToken=b77a5c561934e089," + @"Culture="""; Assembly asm = Assembly.Load(displayName); DisplayInfo(asm); Console.WriteLine("Done!"); Console.ReadLine(); } } } ■Source Code The SharedAsmReflector project is included in the Chapter 16 subdirectory. At this point you should understand how to use some of the core types defined within the System.Reflection namespace to discover metadata at runtime. Of course, I realize despite the “cool factor,” you likely will not need to build custom object browsers at your place of employment. Do recall, however, that reflection services are the foundation for a number of very common pro- gramming activities, including late binding. Understanding Late Binding Simply put, late binding is a technique in which you are able to create an instance of a given type and invoke its members at runtime without having hard-coded compile-time knowledge of its exis- tence. When you are building an application that binds late to a type in an external assembly, you have no reason to set a reference to the assembly; therefore, the caller’s manifest has no direct list- ing of the assembly. At first glance, it is not easy to see the value of late binding. It is true that if you can “bind early” to a type (e.g., set an assembly reference and allocate the type using the C# new keyword), you should opt to do so. For one reason, early binding allows you to determine errors at compile time, rather than at runtime. Nevertheless, late binding does have a critical role in any extendable application you may be building. You will have a chance to build such an “extendable” program at the end of this chapter in the section “Building an Extendable Application”; until then, we need to examine the role of the Activator type. CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 539 8849CH16.qxd 10/9/07 4:27 PM Page 539 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com The System.Activator Class The System.Activator class is the key to the .NET late binding process. Beyond the methods inher- ited from System.Object, Activator defines only a small set of members, many of which have to do with the .NET remoting API. For our current example, we are only interested in the Activator. CreateInstance() method, which is used to create an instance of a type à la late binding. This method has been overloaded numerous times to provide a good deal of flexibility. The simplest variation of the CreateInstance() member takes a valid Type object that describes the entity you wish to allocate on the fly. Create a new Console Application named LateBindingApp, and update the Main() method as follows (be sure to place a copy of CarLibrary.dll in the project’s \bin\Debug directory): // Create a type dynamically. public class Program { static void Main(string[] args) { Console.WriteLine("***** Fun with Late Binding *****"); // Try to load a local copy of CarLibrary. Assembly a = null; try { a = Assembly.Load("CarLibrary"); } catch(FileNotFoundException e) { Console.WriteLine(e.Message); return; } // Get metadata for the Minivan type. Type miniVan = a.GetType("CarLibrary.MiniVan"); // Create the Minivan on the fly. object obj = Activator.CreateInstance(miniVan); Console.WriteLine("Created a {0} using late binding!", obj); Console.ReadLine(); } } Notice that the Activator.CreateInstance() method returns a System.Object rather than a strongly typed MiniVan. Therefore, if you apply the dot operator on the obj variable, you will fail to see any members of the MiniVan type. At first glance, you may assume you can remedy this problem with an explicit cast; however, this program has no clue what a MiniVan is in the first place (and if you did, why use late binding at all)! Remember that the whole point of late binding is to create instances of objects for which there is no compile-time knowledge. Given this, how can you invoke the underlying methods of the MiniVan object stored in the System.Object variable? The answer, of course, is by using reflection. Invoking Methods with No Parameters Assume you wish to invoke the TurboBoost() method of the MiniVan. As you recall, this method will set the state of the engine to “dead” and display an informational message box. The first step is to obtain a MethodInfo type for the TurboBoost() method using Type.GetMethod(). From the resulting CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING540 8849CH16.qxd 10/9/07 4:27 PM Page 540 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com MethodInfo, you are then able to call MiniVan.TurboBoost using Invoke(). MethodInfo.Invoke() requires you to send in all parameters that are to be given to the method represented by MethodInfo. These parameters are represented by an array of System.Object types (as the parameters for a given method could be any number of various entities). Given that TurboBoost() does not require any parameters, you can simply pass null (meaning “this method has no parameters”). Update your Main() method as follows: static void Main(string[] args) { // Get metadata for the Minivan type. Type miniVan = a.GetType("CarLibrary.MiniVan"); // Create the Minivan on the fly. object obj = Activator.CreateInstance(miniVan); Console.WriteLine("Created a {0} using late binding!", obj); // Get info for TurboBoost. MethodInfo mi = miniVan.GetMethod("TurboBoost"); // Invoke method ('null' for no parameters). mi.Invoke(obj, null); Console.ReadLine(); } At this point you are happy to see the message box in Figure 16-5. Figure 16-5. Late-bound method invocation Invoking Methods with Parameters To illustrate how to dynamically invoke a method that does take some number of parameters, assume you have updated the MiniVan type created in the previous chapter with a new method named TellChildToBeQuiet(): // Quiet down the troops public void TellChildToBeQuiet(string kidName, int shameIntensity) { for(int i = 0 ; i < shameIntensity; i++) MessageBox.Show(string.Format("Be quiet {0} !!", kidName)); } TellChildToBeQuiet() takes two parameters: a string representing the child’s name and an integer representing your current level of frustration. When using late binding, parameters are packaged as an array of System.Objects. To invoke the new method, update the Main() method as follows: CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 541 8849CH16.qxd 10/9/07 4:27 PM Page 541 www.free-ebooks-download.org Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... Members w 56 4 This property gets the timestamp associated with the process that has terminated (represented with a DateTime type) Handle This property returns the handle associated to the process by the OS HandleCount This property returns the number of handles opened by the process Id This property gets the PID for the associated process MachineName This property gets the name of the computer the associated... PROCESSES, APPDOMAINS, AND OBJECT CONTEXTS Meaning in Life PriorityBoostEnabled This property determines whether the OS should temporarily boost the process if the main window has the focus PriorityClass This property allows you to read or change the overall priority for the associated process ProcessName This property gets the name of the process (which, as you would assume, is the name of the application... associated process is running on MainModule This property gets the ProcessModule type that represents the main module for a given process MainWindowTitle MainWindowHandle MainWindowTitle gets the caption of the main window of the process (if the process does not have a main window, you receive an empty string) MainWindowHandle gets the underlying handle (represented via a System.IntPtr type) of the associated... 17-1 Select Members of the System.Diagnostics Namespace Process-Centric Types of the System.Diagnostics Namespace Meaning in Life The Process class provides access to local and remote processes and also allows you to programmatically start and stop processes ProcessModule This type represents a module (*.dll or *.exe) that is loaded into a particular process Understand that the ProcessModule type can... Running Processes fr To illustrate the process of manipulating Process types (pardon the redundancy), assume you have a C# Console Application named ProcessManipulator that defines the following static helper method within the Program class (be sure you import the System.Diagnostics namespace): w w w static void ListAllRunningProcesses() { // Get all the processes on the local machine Process[] runningProcs... ProcessThreadCollection Provides a strongly typed collection of ProcessThread objects do w nl oa d o rg Process ee -e bo o ks - The System.Diagnostics.Process type allows you to analyze the processes running on a given machine (local or remote) The Process class also provides members that allow you to programmatically start and terminate processes, view a process’s priority level, and obtain a list of active threads and/ or... memory, the OS creates a separate and isolated process for use during its lifetime Using this approach to application isolation, the result is a much more robust and stable runtime environment, given that the failure of one process does not affect the functioning of another Now, every Win32 process is assigned a unique process identifier (PID) and may be independently loaded and unloaded by the OS as... the process does not have a main window, the IntPtr type is assigned the value System IntPtr.Zero Modules This property provides access to the strongly typed ProcessModuleCollection type, which represents the set of modules (*.dll or *.exe) loaded within the current process 8849CH17.qxd 10/9/07 4:29 PM Page 56 5 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com CHAPTER 17 s PROCESSES,... theProc = null; try { theProc = Process.GetProcessById(987); } catch // Generic catch for used simplicity { Console.WriteLine("-> Sorry bad PID!"); } } w 56 6 Investigating a Process’s Thread Set The Process class type also provides a manner to programmatically investigate the set of all threads currently used by a specific process The set of threads is represented by the strongly typed ProcessThreadCollection... Console.WriteLine("************************************\n"); ee -e bo o } As you can see, the Threads property of the System.Diagnostics.Process type provides access to the ProcessThreadCollection class Here, you are printing out the assigned thread ID, start time, and priority level of each thread in the process specified by the client Thus, if you update your program’s Main() method to prompt the user for a PID to investigate, as follows: . Code The MyTypeViewer project can be found under the Chapter 16 subdirectory. CHAPTER 16 ■ TYPE REFLECTION, LATE BINDING, AND ATTRIBUTE-BASED PROGRAMMING 53 5 8849CH16.qxd 10/9/07 4:27 PM Page 53 5 www.free-ebooks-download.org Simpo. given method, but also the return value and incoming parameters. The MethodInfo type provides the ReturnType property and GetParameters() method for these very tasks. In the following code, notice. Main() The Main() method of the Program class prompts the user for the fully qualified name of a type. Once you obtain this string data, you pass it into the Type.GetType() method and send the

Ngày đăng: 12/08/2014, 23:20

TỪ KHÓA LIÊN QUAN