The Reflection Method
Reflection is simply the process of discovering information about a type at runtime and using that information to access the type programmatically. For example, we can determine method and parameter information at runtime and dynamically invoke this functionality for any object or type. There is a performance penalty to using reflection, so we need to decide when it is appropriate by application. We can oftentimes cache the type information to help offset this performance penalty as well.
The DirectoryEntry class already provides some easy access to the reflection-based approach through the Invoke, InvokeGet, and InvokeSet methods. These methods in turn use .NET reflection under the hood to call ADSI interfaces dynamically. The NativeObject property provides us with a System.Object reference that we can use for reflection against the underlying ADSI IADs interface as well, but all of that functionality is essentially available via the helper methods.
The more useful place for reflection is with values that are returned from PropertyValueCollection that represent IADsLargeInteger and IADsSecurityDescriptor types (and IADsDNWithBinary, to a lesser extent). If we are confronted with the need to simply read or write an IADsLargeInteger type, it seems silly to drag the whole activeds.tlb interop assembly around with us.
For that, we can simply use reflection to read the HighPart and LowPart members of the interface to get at the data in which we are interested. Listing A.1 shows what a simple function for conversion from IADsLargeInteger to Int64 might look like.
Listing A.1. Using Reflection to Read IADsLargeInteger
using System.Reflection; public static Int64 ConvertToInt64(object largeInteger) { Int32 lowPart; Int32 highPart; Type largeIntType; largeIntType = largeInteger.GetType(); try { highPart = (Int32) largeIntType.InvokeMember( "HighPart", BindingFlags.GetProperty | BindingFlags.Public, null, largeInteger, null ); lowPart = (Int32) largeIntType.InvokeMember( "LowPart", BindingFlags.GetProperty | BindingFlags.Public, null, largeInteger, null ); return (long)highPart << 32 | (uint)lowPart;; } catch (MissingMethodException ex) { throw new ArgumentException( "Argument must be IADsLargeInteger!", ex ); } } |
This approach is also possible in Visual Basic .NET, with some simple conversion. However, Visual Basic .NET and other .NET languages that allow late binding offer an even easier approach. By disabling Option Strict in Visual Basic .NET, we can simply invoke the HighPart and LowPart properties directly and let the Visual Basic runtime do the heavy lifting, as shown in Listing A.2.
Listing A.2. Using Visual Basic.NET Late Binding to Access IADsLargeInteger
Option Strict Off 'given an object largeInt that contains an IADsLargeInteger... Dim highPart As Integer Dim lowPart as Integer highPart = largeInt.HighPart lowPart = largeInt.LowPart |
From there, we just take the high and low parts and reassemble them, as we instructed in Chapter 6. Note that we are not huge fans of Visual Basic .NET's ability to disable Option Strict, as it throws out lots of useful checking by the compiler that makes our code more robust. However, it can be applied at the file level, so if used surgically, it can be effective without sacrificing too much.
Advantages
The advantage of this approach is that it is easier to deploy for small situations.
Disadvantages
The downsides of this approach are as follows.
- It is slower than an interop assembly or declaration.
- It is probably too much work if we are using many types or members.
- Reflection-based programming can be somewhat tedious and error prone.