Reflection
is the mechanism provided by the .NET
Framework to allow you to inspect how a program is constructed. Using
reflection, you can obtain information such as the name of an
assembly and what other assemblies a given assembly imports. You can
even dynamically call methods on a type in a given assembly.
Reflection also allows you to create code dynamically and compile it
to an in-memory assembly or to build a symbol table of type entries
in an assembly. Reflection is a very powerful feature of the
framework, and, as such, is guarded by the runtime, requiring
the
ReflectionPermission
be granted to assemblies
doing this type of work. “Code Access
Security” has only two permission sets that give all
reflection access by default:
FullTrust
and
Everything
. The
LocalIntranet
permission set allows for the
ReflectionEmit
privilege that allows for emitting metadata and creating assemblies,
but not the
TypeInformation
privilege for inspecting other assemblies or the
MemberAccess
privilege for performing dynamic invocation of methods on types in
assemblies. In this chapter, you will see how you can use reflection
to dynamically invoke members on types, figure out all of the
assemblies a given assembly is dependent on, and inspect assemblies
for different types of information. Reflection is a great way to
understand how things are put together in .NET; this chapter provides
a starting point.
You need to determine each assembly imported by a particular assembly. This information can show you if this assembly is using one or more of your assemblies or if your assembly is using another specific assembly.
Use the
Assembly.GetReferencedAssemblies
method to obtain
the imported assemblies of an
assembly:
using System; using System.Reflection; using System.Collections.Specialized; public static void BuildDependentAssemblyList(string path, StringCollection assemblies) { // maintain a list of assemblies the original one needs if(assemblies == null) assemblies = new StringCollection( ); // have we already seen this one? if(assemblies.Contains(path)==true) return; Assembly asm = null; // look for common path delimiters in the string // to see if it is a name or a path if((path.IndexOf(@"",0,path.Length)!=-1)|| (path.IndexOf("/",0,path.Length)!=-1)) { // load the assembly from a path asm = Assembly.LoadFrom(path); } else { // try as assembly name asm = Assembly.Load(path); } // add the assembly to the list if(asm != null) { assemblies.Add(path); } // get the referenced assemblies AssemblyName[] imports = asm.GetReferencedAssemblies( ); // iterate foreach (AssemblyName asmName in imports) { // now recursively call this assembly to get the new modules // it references BuildDependentAssemblyList(asmName.FullName,assemblies); } }
This code fills a StringCollection
containing the
original assembly, all imported assemblies, and the dependent
assemblies of the imported assemblies.
If you ran this method against the assembly
C:CSharpRecipesinDebugCSharpRecipes.exe
,
you’d get the following dependency tree:
C:CSharpRecipesinDebugCSharpRecipes.exe mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Xml, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Runtime.Serialization.Formatters.Soap, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a REGEX_Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null FileIODenied, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null FileIOPermitted, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Obtaining the imported types in an assembly is useful in determining what assemblies another assembly is using. This knowledge can greatly aid in learning to use a new assembly. This method can also help determine dependencies between assemblies for shipping purposes.
The
GetReferencedAssemblies
method of the
System.Reflection.Assembly
class obtains a list of
all the imported assemblies. This method accepts no parameters and
returns an array of AssemblyName
objects instead
of an array of Types
. The
AssemblyName
type is made up of members that allow
access to the information about an assembly, such as the name,
version, culture information, public/private key pairs, and other
data.
Note that this method does not account for assemblies loaded using
the Assembly.Load*
methods, as it is only
inspecting for compile-time references.