Configuration with Custom Attributes


In Chapter 10 in the section "A Meta View of Data: Metadata," I discussed the use of metadata as data. At that time, I briefly mentioned the role that attributes played by being able to extend assembly metadata. I then scratched the surface of attributes by quickly mentioning that there were two groups of attributes: those that are intrinsically available to you and those that are referred to as custom attributes.

In this chapter I drill down much further into the topic of attributes. I focus both on the use of attributes for configuration and on the actual creation of custom attributes.

You were introduced to XML Web services in Chapter 13. In that chapter I briefly mentioned the <WebMethod> attribute as a configuration requirement if you want to change the behavior of a publicly exposed method. The new WebMethod-styled method would then take on the behavior of an XML Web service member. The exact syntax from that earlier chapter is as follows :

 . . . . . .VB.NET <WebMethod()> Public Function HelloWorld() As String               HelloWorld = "Hello World" End Function     . . . . . .  COBOL.NET  000530 METHOD-ID. HELLOWORLD AS "HelloWorld" CUSTOM-ATTRIBUTE IS CA-WEBMETHOD. 000540 DATA DIVISION. 000550 LINKAGE SECTION. 000560 01 RET-VAL OBJECT REFERENCE CLASS-STRING. 000570 PROCEDURE DIVISION RETURNING RET-VAL. 000580    SET RET-VAL TO N"Hello World". 000590 END METHOD HELLOWORLD. 

The .NET Framework makes available a base class called System.Attribute . From there, you will find many built-in attributes (or custom attributes) that derive from System.Attribute. The WebMethod attribute ( System.Web.Services.WebMethodAttribute ) is one example ”an example that you have used.

Tip  

You are free to create your own attributes. Simply by using the System.AttributeUsageAttribute attribute and then inheriting the System.Attribute class into your own classes, you can create new custom attributes. As I noted in Chapter 10, this is a creative way to add documentation into your application. Documentation added this way is available as "data" even after compilation.

You can use a process referred to as reflection (using several classes from the System.Reflection namespace) to query the assembly metadata. You will recall that I demonstrated the use of reflection in Chapter 10 in the section "A Meta View of Data: Metadata." As mentioned in that earlier discussion, you can read the metadata, including any metadata associated with custom attributes. Optionally, you can use the ILDASM.exe command-line tool to read the assembly metadata (and intermediate language).

Cross-Reference  

In the section "Using Reflection to Retrieve the Custom Attributes" later in this chapter, I extend the reflection demonstration presented in Chapter 10 in the section "A Meta View of Data: Metadata." Although you have seen reflection demonstrated before, I now add custom attributes and several other elements to make the demonstration that much more useful.

Generally speaking, you use custom attributes to configure your .NET appli cation. Specifically, you can use custom attributes to influence the runtime behavior of the assembly, to influence the compilation of the assembly, or to extend the assembly metadata. A partial listing of the other available predefined custom attributes appears in the next section.

The Family of Predefined Custom Attributes

When you take a closer look at the System.Attribute base class, you will notice that 190 classes derive directly from the System.Attribute base class. Certainly a gen erous offering available for your configuration needs. Although I would rather not show all 190 of them here, Listing 18-2 shows a few of them.

Listing 18-2: A Partial Listing of Attribute Classes That Derive Directly from the System.Attribute Class
start example
 System.AttributeUsageAttribute  System.CLSCompliantAttribute  . . .  System.EnterpriseServices.ApplicationAccessControlAttribute  System.EnterpriseServices.ApplicationActivationAttribute  System.EnterpriseServices.ApplicationIDAttribute  System.EnterpriseServices.ApplicationNameAttribute  System.EnterpriseServices.ApplicationQueuingAttribute  System.EnterpriseServices.AutoCompleteAttribute  . . .  System.EnterpriseServices.ComponentAccessControlAttribute  System.EnterpriseServices.COMTIIntrinsicsAttribute  System.EnterpriseServices.ConstructionEnabledAttribute  System.EnterpriseServices.DescriptionAttribute  System.EnterpriseServices.EventClassAttribute  System.EnterpriseServices.EventTrackingEnabledAttribute  System.EnterpriseServices.ExceptionClassAttribute  System.EnterpriseServices.IISIntrinsicsAttribute  System.EnterpriseServices.InterfaceQueuingAttribute  System.EnterpriseServices.JustInTimeActivationAttribute  System.EnterpriseServices.LoadBalancingSupportedAttribute  System.EnterpriseServices.MustRunInClientContextAttribute  System.EnterpriseServices.ObjectPoolingAttribute  System.EnterpriseServices.PrivateComponentAttribute  System.EnterpriseServices.SecureMethodAttribute  System.EnterpriseServices.SecurityRoleAttribute  System.EnterpriseServices.SynchronizationAttribute  System.EnterpriseServices.TransactionAttribute  . . .  System.Reflection.AssemblyAlgorithmIdAttribute  System.Reflection.AssemblyCompanyAttribute  System.Reflection.AssemblyConfigurationAttribute  System.Reflection.AssemblyCopyrightAttribute  System.Reflection.AssemblyCultureAttribute  System.Reflection.AssemblyDefaultAliasAttribute  System.Reflection.AssemblyDelaySignAttribute  System.Reflection.AssemblyDescriptionAttribute  System.Reflection.AssemblyFileVersionAttribute  System.Reflection.AssemblyFlagsAttribute  System.Reflection.AssemblyInformationalVersionAttribute  System.Reflection.AssemblyKeyFileAttribute  System.Reflection.AssemblyKeyNameAttribute  System.Reflection.AssemblyProductAttribute  System.Reflection.AssemblyTitleAttribute  System.Reflection.AssemblyTrademarkAttribute  System.Reflection.AssemblyVersionAttribute  System.Reflection.DefaultMemberAttribute  . . .  System.Security.Permissions.SecurityAttribute  System.Security.SuppressUnmanagedCodeSecurityAttribute  System.Security.UnverifiableCodeAttribute  . . .  System.Web.Services.WebMethodAttribute  System.Web.Services.WebServiceAttribute  System.Web.Services.WebServiceBindingAttribute  . . . 
end example
 

Please take a moment to review Listing 18-2. You will notice that I have pulled out a few groupings of attribute classes (from the full list of 190). Of these, I would like to highlight the following few groups:

  • Enterprise Services: Formerly known as COM+, this topic is discussed in Chapter 19.

  • Security: This topic is discussed in the section "Configuring for Code Access Security" later in this chapter.

  • Reflection: This topic is discussed in the next section.

In the next section, you will take a quick look at an example of using custom attributes to extend the assembly metadata. In that context, you will see a broader range of opportunities available when you use custom attributes for configuration.

Tip  

Three predefined custom attributes are specific to Visual Basic .NET (VB .NET): COMClassAttribute , VBFixedStringAttribute , and VBFixedArray. Depending on your needs, you may want to keep these attributes in mind. Personally, I have used the VBFixedStringAttribute custom attribute to force the creation of a fixed-length string in a structure. Coming from a mainframe COBOL background, I found that the use of the VBFixedStringAttribute custom attribute actually looked and felt more like the fixed-length strings (e.g., PIC X) that we mainframe programmers have traditionally created.

Extending Assembly Metadata with Custom Attributes

I have created two sample applications for this discussion: AttributesExampleVB.sln and AttributesExampleCobol.sln. Each sample application is a .NET Windows application that does absolutely nothing ”even less than a "Hello, World" application. For demonstration purposes, this will be sufficient.

Start by opening the VB .NET sample application, AttributesExampleVB.sln, in Visual Studio .NET (VS .NET). When you navigate to the VS .NET Solution Explorer window, you will notice the existence of a file named AssemblyInfo.vb (see Figure 18-1).


Figure 18-1: The VS .NET Solution Explorer window showing the AssemblyInfo.vb file

The AssemblyInfo.vb file is automatically created for you when you use VB .NET. I found this to be true for Console, Windows, and ASP.NET project types. Microsoft refers to the AssemblyInfo.vb file as the assembly manifest (the file is occasionally referred to as the assembly information file ). After you open the AssemblyInfo.vb file, you will notice a handful of attribute statements prefaced with "Assembly" (see Figure 18-2).

click to expand
Figure 18-2: The contents of the AssemblyInfo.vb (assembly manifest) file

You can modify each of these attributes as part of your assembly metadata configuration. Optionally, you can add other attributes. Please refer back to the previous section to review the "reflection" subset of attribute classes. For demon stration purposes, I modified a few of the attribute values. The following code snippet reflects the few changes that I made:

 . . . <Assembly: AssemblyTitle("AttributesExample")>  <Assembly: AssemblyDescription("This is the Attributes Example Using VB")>  <Assembly: AssemblyCompany("www.eClecticSoftwareSolutions.com")>  <Assembly: AssemblyProduct _ ("COBOL and Visual Basic on .NET: A Guide for the Reformed Mainframe Programmer")> . . . 

For now, save and build the VB .NET project. Next, open the COBOL .NET sample project, AttributesExampleCobol.sln . Once you navigate over to the VS .NET Solution Explorer window, you will notice the absence of the assembly man ifest file. Fear not!

The good guys over at Fujitsu worked out a different solution for us. Take a look at the project's Property Pages window. (While you are in the Solution Explorer window, right-click the AttributesExampleCobol project file and select Properties from the context menu.) As shown in Figure 18-3, under the Common Properties General tab, the assembly level attributes are exposed.

click to expand
Figure 18-3: The COBOL .NET project Property Pages window showing the assembly-level attributes

After you enter the data for a few attributes, save and build the project. Being curious , after I saved and built the project, I navigated to the local application folder outside of VS .NET. I needed to take a look at the COBOL .NET raw project file (AttributesExampleCobol.cobp) just to see. Figure 18-4 shows my attribute updates.

click to expand
Figure 18-4: The COBOL .NET raw project file showing the updated attributes
Note  

You might ask why I cared if the attribute updates were stored in the COBOL .NET raw project file. I had two reasons. First, with VB .NET, you can edit the assembly manifest file (AssemblyInfo.vb) outside of VS .NET ”perhaps in Notepad ”as it is just a text file. I wanted to see if the same was true for COBOL .NET ”that is, if you could perform the needed edits outside of VS .NET. Second, because other assembly-level attributes are available other than those shown in the Property Pages window, I wanted to see how you might go about adding additional attributes. Granted, I did not try this myself . Nevertheless, if I were to try, my first attempt would be to add them to the project file (.cobp) using the same syntax structure shown for the other "default" attributes.

To make this configuration task complete, I wanted to verify that the actual assembly metadata had in fact been extended. You will first use ILDASM to read the MSIL and metadata. In the section "Using Reflection to Retrieve the Custom Attributes" later in this chapter, you will see a brief demonstration showing an alternative to using ILDASM.

As you may recall, I introduced the ILDASM tool in Chapter 6. I executed this tool on each sample .exe file (the .exe file is located in each respective application's \bin folder). The Manifest tab on each ILDASM display showed that in each project, using VB .NET and COBOL .NET, the assembly metadata had been extended. Listings 18-3 and 18-4 reflect the relevant portion of the ILDASM tool output. Mission accomplished!

Listing 18-3: Snippet from the ILDASM Output Showing the Extended Metadata for the AttributesExampleCobol Sample Application
start example
 . . . .assembly AttributesExampleCobol  {  . . . [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) =  (01 00 2A 54 68 69 73 20 69 73 20 74 68 65 20 41 // ..*This is the A 74 74 72 69 62 75 74 65 73 20 45 78 61 6D 70 6C // ttributes Exampl 65 20 55 73 69 6E 67 20 43 4F 42 4F 4C 00 00) // e Using COBOL..  .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) =  (01 00 11 41 74 74 72 69 62 75 74 65 73 45 78 61 // ...AttributesExa 6D 70 6C 65 00 00) // mple..  .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) =  (01 00 4D 43 4F 42 4F 4C 20 61 6E 64 20 56 69 73 // ..COBOL and Vis 75 61 6C 20 42 61 73 69 63 20 6F 6E 20 2E 4E 45 // ual Basic on .NE 54 3A 20 41 20 47 75 69 64 65 20 66 6F 72 20 74 // T: A Guide for t 68 65 20 52 65 66 6F 72 6D 65 64 20 4D 61 69 6E // he Reformed Main 66 72 61 6D 65 20 50 72 6F 67 72 61 6D 6D 65 72 // frame Programmer 00 00)   .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) =  (01 00 21 77 77 77 2E 65 43 6C 65 63 74 69 63 53 // ..!www.eClecticS 6F 66 74 77 61 72 65 53 6F 6C 75 74 69 6F 6E 73 // oftwareSolutions . . . 
end example
 
Listing 18-4: Snippet from the ILDASM Output Showing the Extended Metadata for the AttributesExampleVB Sample Application
start example
 . . . .assembly AttributesExampleVB  { . . . [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) =  (01 00 4D 43 4F 42 4F 4C 20 61 6E 64 20 56 69 73 // ..COBOL and Vis 75 61 6C 20 42 61 73 69 63 20 6F 6E 20 2E 4E 45  // ual Basic on .NE 54 3A 20 41 20 47 75 69 64 65 20 66 6F 72 20 74  // T: A Guide for t 68 65 20 52 65 66 6F 72 6D 65 64 20 4D 61 69 6E  // he Reformed Main 66 72 61 6D 65 20 50 72 6F 67 72 61 6D 6D 65 72  // frame Programmer 00 00)   .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) =  (01 00 11 41 74 74 72 69 62 75 74 65 73 45 78 61 // ...AttributesExa 6D 70 6C 65 00 00)                               // mple..   .custom instance void . . . [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) =  (01 00 21 77 77 77 2E 65 43 6C 65 63 74 69 63 53 // ..!www.eClecticS 6F 66 74 77 61 72 65 53 6F 6C 75 74 69 6F 6E 73   // oftwareSolutions 2E 63 6F 6D 00 00) // .com..  .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) =  (01 00 27 54 68 69 73 20 69 73 20 74 68 65 20 41 // ..'This is the A 74 74 72 69 62 75 74 65 73 20 45 78 61 6D 70 6C   // ttributes Exampl 65 20 55 73 69 6E 67 20 56 42 00 00)             // e Using VB.. 
end example
 

All right, perhaps it is not the "prettiest" display. I have just two comments. First of all, when you consider all the mainframe hexadecimal-based memory dumps that we all have combed through (fixing Abends and so forth), this ILDASM manifest display is not that bad. Second of all, for those who want a "nice" display, there is always the option of coding a utility application that performs reflection.

In the next section you will see a demonstration showing the use of reflection to read the assembly metadata to retrieve custom attributes ”and more.

Using Reflection to Retrieve the Custom Attributes

The System.Reflection .NET Framework namespace houses about 40 classes, about 12 enumerations, and a few interfaces, structures, and delegates. Not that impressive, huh? Compared to some of the much larger .NET Framework namespaces that you have been introduced to, it is rather small. So much for passing judgment based on size . As you will see in the following demonstration, you should not underestimate the power harnessed within the System.Reflection .NET Framework namespace.

Note  

For this reflection demonstration I have chosen to use VB .NET. As an exercise, you might consider rewriting the code using COBOL .NET. I am sure that it will be an interesting and educational experience. Bear in mind that I performed the reflection demonstration in Chapter 10 in both VB .NET and COBOL .NET.

For this demonstration, I have created a new VB .NET console project called ReflectionDemovb.sln. In the assembly manifest file (AssemblyInfo.vb), you will notice that the AssemblyTitle and AssemblyDescription attributes have been slightly modified. The following code snippet reflects the changes made to AssemblyInfo.vb:

 . . . <Assembly: AssemblyTitle("Reflection Demo")>  <Assembly: AssemblyDescription("This is the Reflection Demo Using VB.NET")>  <Assembly: AssemblyCompany("www.eClecticSoftwareSolutions.com")>  <Assembly: AssemblyProduct _ ("COBOL and Visual Basic on .NET: A Guide for the Reformed Mainframe  Programmer")> . . . 

Next, I wanted to make this reflection demonstration really interesting. Therefore, I added the following class modules to the ReflectionDemovb.sln sample application:

  • A CustomAttribute.vb class that uses the AttributeUsage attribute and inherits the System.Attribute class. In other words, you will create your very own custom attribute. The new custom attribute will be named MyDocumentationAttribute .

  • A DocumentedClass.vb class to use the MyDocumentationAttribute custom attribute.

  • An ExampleElements.vb class to add a variety of elements/types to the assembly. This will make the metadata that much more interesting to query. Just for fun, I included an example of the VB .NET “specific VBFixedStringAttribute custom attribute being used.

  • A ReflectionDrillDown.vb class to show the real power of the System.Reflection namespace and its reflection classes.

  • A ReflectionDriver.vb class to add some structure to the entire sample project. As you will see, it is this class that actually picks up the targeted assembly to query. For demonstration purposes, you will use the ReflectionDemoVB assembly as the "executing assembly."

As you will see, I have added lots of comments in the code to describe specific portions, options, and so forth. For your convenience, I provide code samples taken from the class modules. Listing 18-5 contains the CustomAttribute.vb class module.

Listing 18-5: The CustomAttribute.vb Class Module from the ReflectionDemoVB.sln Sample Application
start example
 Public Class MYCustomAttribute     'Create your own custom attributes     '(1) Use the AttributeUsage Tag     '(2) Decide on AttributeTarget scope, etc.     '(3) Use "Attribute" suffix     '(4) Inherit System.Attribute     <AttributeUsage(AttributeTargets.All, AllowMultiple:=True)> _     Public Class MyDocumentationAttribute       Inherits System.Attribute       Public RequestNumber As String       Public RequestNotes As String       Public Sub New(ByVal varDrNumber As String, ByVal varDrNotes As String)          MyBase.New()          RequestNumber = varDrNumber          RequestNotes = varDrNotes       End Sub       Public Overrides Function ToString() As String          Return _          ("Request Documentation: " + RequestNumber + " " + RequestNotes)       End Function     End Class End Class 
end example
 

Listing 18-6 contains the DocumentedClass.vb class module.

Listing 18-6: The DocumentedClass.vb Class Module from the ReflectionDemoVB.sln Sample Application
start example
 Public Class DocumentedClass1     'use our Custom Attribute     Inherits ReflectionDemo.MYCustomAttribute           'When using our Custom Attribute below,        'the "Attribute" suffix is optional       <MyDocumentationAttribute("Request_123", _       "Added Class to Support new Business Rule")> _       Public Class myDocumentedClass             <MyDocumentationAttribute("Request_456", _             "Changed Name or Variable")> _             Public myInt As System.Int32             <MyDocumentationAttribute("Request_789", _             "Changed Access attribute to Private")> _             Private myString As System.String                 <MyDocumentationAttribute("Request_ABC", _             "Modfied Sub Routines Scope")> _             Public Function myDocumentedFunction() As String                   'Do Nothing             End Function        End Class End Class 
end example
 

Listing 18-7 contains the ExampleElements.vb class module.

Listing 18-7: The ExampleElements.vb Class Module from the ReflectionDemoVB.sln Sample Application
start example
 'This Class was created simply to provide a variety of TYPES 'for the Reflection Demo Public Class ReflectionClassA     Private myArrayList As New ArrayList()     Friend myFriendVar As String     Public Shared mySharedVar As Boolean     Protected myProtectedVar As Long     Protected Friend myProtectedFRVar As Long         Structure AStructure        Public myStrucInteger As Integer        Dim myStrucPubString As String  <VBFixedString(10)> Private myStrucPriString As String  End Structure         Public Sub New(ByVal afield As ArrayList)        MyBase.new()        myArrayList = afield     End Sub           Public Sub New(ByVal afield As ArrayList, _       ByVal bfield As ArrayList)             MyBase.new()             myArrayList = bfield       End Sub           Dim myArray As String() = {"A", "B", "C"}     Public Property Name() As String()          Get             Return myArray          End Get          Set(ByVal Value() As String)             If Value(0) <> "" Then                myArray(0) = Value(0)             End If          End Set     End Property         Public Enum ScaleOfDifficulty          VeryEasy = 1          Easy = 2          SlightChallenge = 3          Challenging = 4          Difficult = 5          VeryDifficult = 6     End Enum         Public Class NestedClassA          Private myShort As Int16          Private myInt As Int32          Private myLong As Int64          Public myPublicShort As Int16          Public myPublicInt As Int32          Public myPublicLong As Int64          Dim myArrayInSideOfClass As String() _            = {"A", "B", "C", "D", "E", "F"}              Public Sub myFirstSub()               Static myStaticVar As Integer                For myInt = 0 To 1                   'Do nothing                Next          End Sub       End Class End Class 
end example
 

Listing 18-8 contains the ReflectionDrillDown.vb class module.

Listing 18-8: The ReflectionDrillDown.vb Class Module from the ReflectionDemoVB.sln Sample Application
start example
 Option Strict On Public Class ReflectionDrillDownClass     'Use Reflection to Drill down into each Assembly TYPE Public Shared Sub DrillDownIntoType(ByVal objType As Type)     'Use the FindMembers Reflection method to extract all Members Types 'Optionally, you can Filter by member type, BindingFlag, or Delegate. 'Use of the Reflection Delegate requires that you Modify the Delegate 'Parameter below as per your intent to Filter your FindMembers results Dim arrayMemberInfo() As System.Reflection.MemberInfo arrayMemberInfo = objType.FindMembers(System.Reflection.MemberTypes.All, _                                     System.Reflection.BindingFlags.Public Or _                                     System.Reflection.BindingFlags.Static _                                     Or System.Reflection.BindingFlags.NonPublic _                                     Or System.Reflection.BindingFlags.Instance, _                                     New System.Reflection.MemberFilter _                                    (AddressOf DelegateToSearchCriteria), _                                      " ")     Dim index As Integer For index = 0 To arrayMemberInfo.Length - 1 'Treat each Member Type according to desired information       Select Case arrayMemberInfo(index).MemberType.ToString()             Case "Field" 'Use FieldInfo Reflection Method to Drill down into Field type Members                     Dim FieldInfo As System.Reflection.FieldInfo                     FieldInfo = CType(arrayMemberInfo(index),                                 System.Reflection.FieldInfo)                         Dim FieldInfoStr As String                     If FieldInfo.IsPublic() Then                        FieldInfoStr = "Public"                     End If                     If FieldInfo.IsPrivate() Then                        FieldInfoStr = "Private"                     End If                     If FieldInfo.IsStatic() Then                        FieldInfoStr = "Static"                     End If     'Optionally use Reflection and conditional logic to filter as you wish      If arrayMemberInfo(index).DeclaringType.Namespace.ToString() <> _            "System" Then               Console.WriteLine("MemberType - " + ControlChars.Tab + _                   arrayMemberInfo(index).MemberType.ToString() + _                   ControlChars.Tab + _                   FieldInfoStr + _                   ControlChars.Tab + _                   "Name -" + ControlChars.Tab + _                   arrayMemberInfo(index).ToString() + ControlChars.Cr)          'Logic to support Custom Attributes - At Field Level      GetCustomAttibutes(arrayMemberInfo(index).GetCustomAttributes(True))      End If      Case "Method"      'Optionally use Reflection and conditional logic to filter as you wish  If arrayMemberInfo(index).DeclaringType.Namespace.ToString() <> _          "System" Then             Console.WriteLine("MemberType - " + ControlChars.Tab + _               arrayMemberInfo(index).MemberType.ToString() + _               ControlChars.Tab + _               "Name -" + ControlChars.Tab + _               arrayMemberInfo(index).ToString() + ControlChars.Cr)         'Logic to support Custom Attributes - At Member level      GetCustomAttibutes(arrayMemberInfo(index).GetCustomAttributes(True))      End If           Case "Constructor"             Console.WriteLine("Constructor - " + ControlChars.Tab + _             arrayMemberInfo(index).MemberType.ToString() + _              ControlChars.Tab + _             arrayMemberInfo(index).ToString() + ControlChars.Cr)           End Select         Next index  End Sub      'Optionally, customize this Reflection Delegate as you wish  Public Shared Function DelegateToSearchCriteria _       (ByVal objMemberInfo As System.Reflection.MemberInfo, _          ByVal objSearch As Object) As Boolean       'Optionally, modify the logic below (to filter) as per your modifications       'applied at the time of executing the FindMembers methods above.          If objMemberInfo.DeclaringType.Namespace.ToString() <> _             objSearch.ToString() Then                    Return True              Else                    Return False              End If        End Function            Public Shared Sub GetCustomAttibutes(ByVal myobj() As Object)             'Logic to support Custom Attributes             Dim SysAttrTypeFld As System.Attribute             Dim idxFld As Integer             For idxFld = 0 To UBound(myobj)                 SysAttrTypeFld = CType(myobj(idxFld), System.Attribute)                 'Single out one of the VB specific Attributes                 Select Case SysAttrTypeFld.ToString()                    Case "Microsoft.VisualBasic.VBFixedStringAttribute"                       Dim obj As Microsoft.VisualBasic.VBFixedStringAttribute                       obj = CType(SysAttrTypeFld, _                        Microsoft.VisualBasic.VBFixedStringAttribute)                            Console.WriteLine("Attribute:->" + _                            SysAttrTypeFld.ToString() + " " + obj.Length.ToString())                    Case Else                       Console.WriteLine("Attribute:->" + SysAttrTypeFld.ToString())             End Select          Next       End Sub End Class 
end example
 

Listing 18-9 contains the ReflectionDriver.vb class module.

Listing 18-9: The ReflectionDriver.vb Class Module from the ReflectionDemoVB.sln Sample Application
start example
 Option Strict On Public Class ReflectionDriverClass     Public Sub ReflectionDemo()           Dim MethodIndex As System.Int32       Dim ArrayIndex As System.Int32           'For Demo purposes, Currently Executing Assembly       'Optionally Pick up an existing referenced Assembly        'or perhaps do a late bind, and pick up unreferenced assemblies.       'Comment/Un-comment below as appropriate           'Dim UnReferencedAssemblyForLateBind As System.Object       'Dim ReferencedAssembly = New ReflectionDemo.ReflectionDriverClass()       Dim AssemblyClass As System.Reflection.Assembly       AssemblyClass = AssemblyClass.GetExecutingAssembly()       'AssemblyClass = AssemblyClass.GetAssembly(UnReferencedAssembly.GetType)       'AssemblyClass = AssemblyClass.GetAssembly(ReferencedAssembly.GetType)           'Use Reflection to display Assembly Attributes        '(stored in AssemblyInfo.vb file)       GetAssemblyAttributes(AssemblyClass)           'Use Reflection to report Referenced Assemblies       Dim AssemblyArray As System.Reflection.AssemblyName()       AssemblyArray = AssemblyClass.GetReferencedAssemblies()       For ArrayIndex = 0 To UBound(AssemblyArray)          Console.WriteLine("Referenced Assemblies: " + _          AssemblyArray(ArrayIndex).Name)       Next           'Use Reflection to list Types within Assembly       Dim MyTypes() As System.Type       MyTypes = AssemblyClass.GetTypes()           For ArrayIndex = 0 To UBound(MyTypes)          Console.WriteLine("*************************************")          Console.WriteLine("Type: " + MyTypes(ArrayIndex).FullName)          If MyTypes(ArrayIndex).IsEnum Then             Dim EnumStr() As String             'Use Reflection to get Details of Enums             EnumStr = System.Enum.GetNames(MyTypes(ArrayIndex))             Console.WriteLine(" This Enumeration Contains: ")             Dim xString As String                  For Each xString In EnumStr                     Dim sb As New System.Text.StringBuilder()                 sb.Append(" ")                 sb.Append(xString)                 sb.Append("--->")                 sb.Append(System.Enum.Format(MyTypes(ArrayIndex), _                 System.Enum.Parse(MyTypes(ArrayIndex), xString), "d"))                 Console.WriteLine(sb.ToString())                Next       Else                'Use Reflection to display TYPE level Custom Attributes            Dim CustAttr() As Object            CustAttr = MyTypes(ArrayIndex).GetCustomAttributes(True)            Dim SysAttrType As System.Attribute            Dim idx As Integer            For idx = 0 To UBound(CustAttr)                SysAttrType = CType(CustAttr(idx), System.Attribute)                Console.WriteLine("Attribute:->" + SysAttrType.ToString())            Next                'Use Reflection to get Details other TYPES            Dim mydemo As New ReflectionDrillDownClass()            mydemo.DrillDownIntoType(MyTypes(ArrayIndex))              End If          Console.WriteLine()       Next           Console.WriteLine(String.Empty)     End Sub         Public Sub GetAssemblyAttributes(ByVal AssemblyObj As _       System.Reflection.Assembly)              Dim CustAttr1() As Object          CustAttr1 = AssemblyObj.GetCustomAttributes(True)          Dim SysAttrType1 As System.Attribute          Dim idx1 As Integer          For idx1 = 0 To UBound(CustAttr1)              SysAttrType1 = CType(CustAttr1(idx1), System.Attribute)                  'There are other Attributes available. These were selected               'for Demo purposes (stored in AssemblyInfo.vb file)              Select Case SysAttrType1.ToString()                 Case "System.Reflection.AssemblyCompanyAttribute"                      Dim obj As System.Reflection.AssemblyCompanyAttribute                      obj = CType(SysAttrType1, _                      System.Reflection.AssemblyCompanyAttribute)                      Console.WriteLine("Company: " + obj.Company)                 Case "System.Reflection.AssemblyTitleAttribute"                      Dim obj As System.Reflection.AssemblyTitleAttribute                      obj = CType(SysAttrType1, _                      System.Reflection.AssemblyTitleAttribute)                      Console.WriteLine("Assembly Title: " + obj.Title)                 Case "System.Reflection.AssemblyDescriptionAttribute"                      Dim obj As System.Reflection.AssemblyDescriptionAttribute                      obj = CType(SysAttrType1, _                      System.Reflection.AssemblyDescriptionAttribute)                 Console.WriteLine("Assembly Description: " + obj.Description)                 Case "System.Reflection.AssemblyProductAttribute"                      Dim obj As System.Reflection.AssemblyProductAttribute                      obj = CType(SysAttrType1, _                      System.Reflection.AssemblyProductAttribute)                      Console.WriteLine("Assembly Product: " + obj.Product)          End Select       Next       Console.WriteLine("*************************************")         End Sub End Class 
end example
 

Please take a moment to review each of the class modules in the preceding listings. As you will notice, meaningful comments have been added to each. When the ReflectionDemoVB sample console application is executed, a series of lines will be written to the console. I have included a portion of the reflection console output in Listing 18-10.

Listing 18-10: A Portion of the Reflection Output As It Is Written to the Console Window for the ReflectionDemoVB Sample Application
start example
 Assembly Title: Reflection Demo Assembly Product: COBOL and Visual Basic on .NET: A Guide for the Reformed Mainframe  Programmer Assembly Description: This is the Reflection Demo Using VB.NET Company: www.eClecticSoftwareSolutions.com ************************************* Referenced Assemblies: mscorlib Referenced Assemblies: Microsoft.VisualBasic Referenced Assemblies: System Referenced Assemblies: System.Data Referenced Assemblies: System.Xml ************************************* Type: ReflectionDemo.MYCustomAttribute Constructor - Constructor Void .ctor() ************************************* Type: ReflectionDemo.MYCustomAttribute+MyDocumentationAttribute Attribute:->System.AttributeUsageAttribute MemberType - Method Name - System.String ToString() Constructor - Constructor Void .ctor(System.String, System.String) MemberType - Field Public Name - System.String RequestNumber MemberType - Field Public Name - System.String RequestNotes ************************************* . . . ************************************* Type: ReflectionDemo.ReflectionClassA+AStructure MemberType - Field Public Name - Int32 myStrucInteger MemberType - Field Public Name - System.String myStrucPubString MemberType - Field Private Name - System.String myStrucPriString Attribute:->Microsoft.VisualBasic.VBFixedStringAttribute 10 ************************************* Type: ReflectionDemo.ReflectionClassA+ScaleOfDifficulty     This Enumeration Contains:     VeryEasy--->1     Easy--->2     SlightChallenge--->3     Challenging--->4     Difficult--->5     VeryDifficult--->6 ************************************* . . . 
end example
 

As shown in Listing 18-10, you can use the reflection classes while program- matically formatting/structuring the resulting output. As compared with the display that the ILDASM tool creates for you, you may wish to create an "easier to read" type of display. On the other hand, the graphical tree view of the assembly metadata provided by the ILDASM tool has its strengths as well (see Figure 18-5).

click to expand
Figure 18-5: The graphical tree view display provided by the ILDASM tool using the ReflectionDemoVB assembly

Using the ILDASM tool, you can drill down into the DocumentedClass1 and myDocumentedClass type nodes (see Figure 18-5) to see the "documentation" that was added with your custom attribute (MYCustomAttribute.MyDocumentationAttribute ). As I discussed in the previous section, the assembly-level attributes are stored within the Manifest node.

Perhaps the reflection demonstration presented in Chapter 10 stirred your interest. I hope the reflection demonstration in this chapter provided you with a much broader picture of the capabilities and power of reflection. Combine the reflection process with custom attributes and metadata to enhance your configu ration processes.

Now you have seen the two choices available when you want to read metadata from an assembly. I believe that I have driven the point home that by using the process of reflection, the use of custom attributes for configuration becomes that much more attractive.

Tip  

Consider enhancing the ReflectionDemoVB sample application to write the output to a text file, an XML file, or a database table.

Feel free to dig down further into the topic of custom attributes and reflection according to your needs. You will find that the topic of custom attributes extends beyond configuration and reflection. Likewise, you will find that the topic of reflection extends beyond configuration and custom attributes. This chapter's dis cussion exploited the intersection of these topics, as they tend to complement each other . I have included references at the end of this chapter in the "To Learn More" section that will help your continued exploration.

In the next section, you will turn your attention to the use of XML-based con figuration files for your .NET application configuration needs.

Caution  

The following section discusses the set of XML-based configuration (config) files associated with the .NET Framework. Please be aware that if you damage either your Web.config or your App.config file, the recovery will not be that bad. In the worst-case scenario, one application could be temporarily disabled and the associated user group will be screaming at you. However, the same is not true for the Machine.config or security-related config files. Inadvertently damaging your Machine.config or security- related config files could be disastrous for you. You could cripple your entire machine and all applications running on it. This usually gets people into big trouble. That being the case, you will always want to make dependable backups of the Machine.config and security-related config files before opening them for editing or browsing purposes.




COBOL and Visual Basic on .NET
COBOL and Visual Basic on .NET: A Guide for the Reformed Mainframe Programmer
ISBN: 1590590481
EAN: 2147483647
Year: 2003
Pages: 204

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net