.NET Reflection

System.Reflection – Working with the Type Class

Posted on Updated on

In a previous post, we took a brief look at the System.Reflection.Assembly class. In this post, we are going to move down a level and take a look at the Type class. Every object that resides within a .NET application is a specific Type.

Getting an Object’s Type (Type.GetType() method)

Let’s go ahead and dive in to some code samples that allow us to work with types a little. In the previous post we created a very simple Visual Studio solution that contained a C# Class Library and a C# Console Application. In this post we will add a new class to the console application then from the Program class’s static Main() method we will instantiate our new object. We will work with our new class and add methods to perform certain tasks related to getting Type information.

We will add a new class to the console application and we will name it ‘Person’. Since a person has qualities such as a name, age, height, weight, and so on, we will add a few properties to the class that we will then retrieve and work with via Reflection.

So when we add a few properties and a couple of nonsensical methods to the class, the full code of the Person class looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyConsoleApplication
{
    public class Person
    {
        #region Public Properties

        public string Name
        {
            get;
            set;
        }

        public Int32 Age
        {
            get;
            set;
        }

        public Int32 Height
        {
            get;
            set;
        }

        public Int32 Weight
        {
            get;
            set;
        }

        #endregion Public Properties

        #region Public Methods

        public string SayHello()
        {
            return "Hello!";
        }

        public string Speak(string wordsToSpeak)
        {
            return wordsToSpeak;
        }

        #endregion Public Methods
    }
}

To create an instance of a Person, we add the following code to our console application’s Program class’s Main() method:

Person person = new Person();
Type personType = person.GetType();

Now, we will put a breakpoint on the second line shown above and hit F5 to run the console application. When we hit the breakpoint, we will right-click and set a Watch on the personType object. The results are shown below:

Watch window view of the personType object
Watch window view of the personType object

There are more properties of the Type object than I can display in the illustration above but this is more than enough to get us started. If you scan through the property list, you will that there is a lot of information about the type available.

Let’s talk about a few of the more important properties shown.

First, look at the Assembly property. You can see that the value describes the ConsoleApplication assembly, including its version, culture information, public key token (which is null because the assembly is not strongly-named).

Next, the AssemblyQualifiedName shows the same stuff, except the full namespace and type name of the Person object are prepended.

The BaseType property describes the type from which the Person class inherits. In this case, it inherits from System.Object. Had our class inherited another class, the BaseType would reflect that type, and its BaseType would reflect its base type and so on. Ultimately, the lowest level base class would have a BaseType of System.Object. Make sense?

All if the properties that start with Is* are Boolean values that indicate what their names describe.

So far so good? Great! Let’s move on to some things that are a little more interesting.

Getting the Properties within a Type (Type.GetProperties() method)

The Type class’s GetProperties() method allows us to retrieve all properties within a type. This method returns an array of PropertyInfo objects. The PropertyInfo type provides us access to the metadata of a property. Sound confusing? Let’s just dive into some code and clear it up!

Let’s return to our Main() method and add the following code:

Person person = new Person();
Type personType = person.GetType();

//get the PropertyInfo array for all properties
PropertyInfo[] properties = personType.GetProperties();

//create a StringBuilder to hold our formatted output
StringBuilder sb = new StringBuilder();

//iterate the properties and write output
foreach (PropertyInfo property in properties)
{
    sb.AppendLine("================================================================");
    sb.AppendLine(String.Format("Property Name: {0}", property.Name));
    sb.AppendLine("================================================================");

    sb.AppendLine(String.Format("Property Type Name: {0}", property.PropertyType.Name));
    sb.AppendLine(String.Format("Property Type FullName: {0}", property.PropertyType.FullName));

    sb.AppendLine(String.Format("Can we read the property?: {0}", property.CanRead.ToString()));
    sb.AppendLine(String.Format("Can we write the property?: {0}", property.CanWrite.ToString()));
}

string output = sb.ToString();

When we retrieve the string contents of our StringBuilder, we will see the following results:

================================================================
Property Name: Name
================================================================
Property Type Name: String
Property Type FullName: System.String
Can we read the property?: True
Can we write the property?: True
================================================================
Property Name: Age
================================================================
Property Type Name: Int32
Property Type FullName: System.Int32
Can we read the property?: True
Can we write the property?: True
================================================================
Property Name: Height
================================================================
Property Type Name: Int32
Property Type FullName: System.Int32
Can we read the property?: True
Can we write the property?: True
================================================================
Property Name: Weight
================================================================
Property Type Name: Int32
Property Type FullName: System.Int32
Can we read the property?: True
Can we write the property?: True

All of the properties that defined in our Person class are shown here with their data types (both short and full names) and with boolean values that indicate whether they are read-only or write-only properties. The presence of get and set accessors means that our properties are read and write properties. Pretty simple.

Getting and Setting Properties through Reflection

Being able to retrieve our properties is pretty cool, but we can also read values from and write values to our properties using Reflection. Now if we did just simply through the object itself, we could do it very simply like this:

person.Name = "John Nelson";
person.Age = 44;
person.Weight = 240;
person.Height = 70;

Now, to do it via Reflection is still simple, but slightly different. See the code below:

personType.GetProperty("Name").SetValue(person, "John Nelson", null);
personType.GetProperty("Age").SetValue(person, 44, null);
personType.GetProperty("Weight").SetValue(person, 240 , null);
personType.GetProperty("Height").SetValue(person, 70, null);

Okay, this warrants some discussion. To set property values via Reflection, you must use the Type.GetProperty() method, then invoke the PropertyInfo.SetValue() method. The default overload that we used accepts the object in which to set the property value, the value itself, and an object array, which in our example is null. The MSDN resource here explains the overloads of the SetValue() method in more detail.

Now that we’ve set the values using Reflection, let’s get them in the same way. We would do that as shown below:

string name = personType.GetProperty("Name").GetValue(person).ToString();
int age = (int)personType.GetProperty("Age").GetValue(person);
int weight = (int)personType.GetProperty("Weight").GetValue(person);
int height = (int)personType.GetProperty("Height").GetValue(person);

Getting property values is pretty straightforward and easy using Reflection.

Getting the Methods within a Type (Type.GetMethods() method)

In a similar manner to getting the properties within a type, getting and working with methods is pretty simple. For this sample, our methods do not contain generic arguments and I did this to keep the sample simple. We will cover invoking generically-typed methods in a later post.

The following code gets all the methods within our Person type and writes information about each method to a StringBuilder.

Person person = new Person();
Type personType = person.GetType();

//get the methods in the type
MethodInfo[] methods = personType.GetMethods();

//create a StringBuilder to hold our formatted output
StringBuilder sb = new StringBuilder();

//iterate the methods and write output
foreach (MethodInfo method in methods)
{
    sb.AppendLine("================================================================");
    sb.AppendLine(String.Format("Method Name: {0}", method.Name));
    sb.AppendLine("================================================================");

    sb.AppendLine(String.Format("Contains Generic Parameters: {0}", method.ContainsGenericParameters.ToString()));
    sb.AppendLine(String.Format("Is Abstract?: {0}", method.IsAbstract.ToString()));

    sb.AppendLine(String.Format("Is a Constructor?: {0}", method.IsConstructor.ToString()));
    sb.AppendLine(String.Format("Is it Generic?: {0}", method.IsGenericMethod.ToString()));

    sb.AppendLine(String.Format("Is it Private?: {0}", method.IsPrivate.ToString()));
    sb.AppendLine(String.Format("Is it Public?: {0}", method.IsPublic.ToString()));
    sb.AppendLine(String.Format("Is it Static?: {0}", method.IsStatic.ToString()));
    sb.AppendLine(String.Format("Is is Virtual?: {0}", method.IsVirtual.ToString()));

    //if the method is a void, the Return type will be null
    //otherwise, it will return a Type
    if (method.ReturnType != null && !String.IsNullOrEmpty(method.ReturnType.Name))
    {
        sb.AppendLine(String.Format("Return Type: {0}", method.ReturnType.Name.ToString()));
    }

    //there are more properties of the MethodInfo you could output...
}

string output = sb.ToString();

If we execute this code and retrieve the results of the StringBuilder.ToString() method, we will see the following output:

================================================================
Method Name: get_Name
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type?: String
================================================================
Method Name: set_Name
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: get_Age
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Int32
================================================================
Method Name: set_Age
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: get_Height
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Int32
================================================================
Method Name: set_Height
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: get_Weight
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Int32
================================================================
Method Name: set_Weight
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: SayHello
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: String
================================================================
Method Name: Speak
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: String
================================================================
Method Name: ToString
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: True
Return Type: String
================================================================
Method Name: Equals
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: True
Return Type: Boolean
================================================================
Method Name: GetHashCode
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: True
Return Type?: Int32
================================================================
Method Name: GetType
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Type

This is odd because we only defined two methods for our class, right? Well, it actually makes perfect sense because we retrieve ALL of the methods for the Person Type.

Beginning from the top of the results list, take a look at the methods whose names begin with “get_” and “set_”. It just so happens that properties are implemented in MSIL as methods! The get accessor is actually a method with a name that begins with “get_” and the set accessor is a method that begins with “set_”. So in reality, each of our properties created two (2) methods.

The ToString(), Equals(), GetHashCode(), and GetType() methods are actually inherited from the System.Object class which is the default base class of ALL objects in .NET.

So if we eliminate these methods, we are left with our two public methods, SayHello() and Speak().

Invoking a Method via Reflection

When dealing with non-generic methods, there are two basic ways to invoke them dynamically.

  1. The first approach is to get a reference to the MethodInfo object for the method to be called, then calling the MethodInfo.Invoke(object, object[]) method. With this approach, the first argument is the object within which the method is to be called. In our case the ‘person’ object. The second argument is an object array that accepts the arguments expected by the method in the order they are specified in the method.
  2. The second approach is to Type.InvokeMenber(methodName, Binder, object, object[]) method overload. I chose this overload of the InvokeMember method because it is the simplest and is perfect for illustrative purposes.

So let’s get going with some code to test this out! Let’s stick with our iterative approach to loop through the methods as we did above, but lets add some code that checks to see if the method name is “Speak” or “SayHello”, then invoke the method using Approach #1 first.

If we replace the code with the code listed below (see new lines 32-44):

//get the methods in the type
MethodInfo[] methods = personType.GetMethods();

//create a StringBuilder to hold our formatted output
StringBuilder sb = new StringBuilder();

//iterate the methods and write output
foreach (MethodInfo method in methods)
{
    sb.AppendLine("================================================================");
    sb.AppendLine(String.Format("Method Name: {0}", method.Name));
    sb.AppendLine("================================================================");

    sb.AppendLine(String.Format("Contains Generic Parameters: {0}", method.ContainsGenericParameters.ToString()));
    sb.AppendLine(String.Format("Is Abstract?: {0}", method.IsAbstract.ToString()));

    sb.AppendLine(String.Format("Is a Constructor?: {0}", method.IsConstructor.ToString()));
    sb.AppendLine(String.Format("Is it Generic?: {0}", method.IsGenericMethod.ToString()));

    sb.AppendLine(String.Format("Is it Private?: {0}", method.IsPrivate.ToString()));
    sb.AppendLine(String.Format("Is it Public?: {0}", method.IsPublic.ToString()));
    sb.AppendLine(String.Format("Is it Static?: {0}", method.IsStatic.ToString()));
    sb.AppendLine(String.Format("Is it Virtual?: {0}", method.IsVirtual.ToString()));

    //if the method is a void, the Return type will be null
    //otherwise, it will return a Type
    if (method.ReturnType != null && !String.IsNullOrEmpty(method.ReturnType.Name))
    {
        sb.AppendLine(String.Format("Is is Private?: {0}", method.ReturnType.Name.ToString()));
    }

    if (method.Name == "SayHello")
    {
        object result = method.Invoke(person, new object[] { });
        if (result != null)
        {
            sb.AppendLine(String.Format("***Calling the SayHello() method. The result of the method call is: {0}", result.ToString()));
        }
    }
    else if (method.Name == "Speak")
    {
        object result = method.Invoke(person, new object[] { "How are you today?" });
        sb.AppendLine(String.Format("***Calling the Speak() method. The result of the method call is: {0}", result.ToString()));
    }

    //there are more properties of the MethodInfo you could output...
}

string output = sb.ToString();

When we execute the new code the output for the Speak() and SayHello() methods now includes the results too:

================================================================
Method Name: SayHello
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Is it Private?: String
***Calling the SayHello() method. The result of the method call is: Hello!
================================================================
Method Name: Speak
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Is it Private?: String
***Calling the Speak() method. The result of the method call is: How are you today?

Pay attention to the lines in blue. We actually called those methods and added the results in a formatted way to our sample output StringBuilder. Pretty simple, right?

Now we take a slightly different approach and simply invoke the method via Reflection by name. This is very straightforward and can be done as shown below:

//invoke the SayHello method
object methodResult = personType.GetMethod("SayHello").Invoke(person, null);
if (methodResult != null)
{
    string thePersonSaid = methodResult.ToString();
}

//invoke the Speak method and pass the string argument in
object methodResult2 = personType.GetMethod("Speak").Invoke(person, new object[] { "I have spoken." });
if (methodResult2 != null)
{
    string thePersonThenSaid = methodResult2.ToString();
}

So now we have looked at a couple of ways to invoke methods via Reflection. One method accepts no arguments and the other accepted a string. What if we had a method that accepted two or more arguments?

Go back to our Person class and add the following method:

public string CallTheChildrenInForDinner(string firstChild, string secondChild)
{
    return String.Format("{0}, {1} it is time for dinner!", firstChild, secondChild);
}

We can invoke this method with the following code:

object methodResult3 = personType.GetMethod("CallTheChildrenInForDinner").Invoke(person, new object[] { "Bobby", "Gene" });
if (methodResult3 != null)
{
    string dinnerCall = methodResult3.ToString();
}

The result of this method call is: Bobby, Gene it is time for dinner!.

Getting the Interfaces Implemented by a Type

Within C#, a class can only inherit one base class (i.e. can only have one BaseType). A class can, however implement multiple interfaces. An interface looks much like a class, but only contains the definitions (signatures) of the public properties, methods, events, and indexers within the class that implements it. When discussing interfaces, we use the term “implements” for any class whose construction is governed by an interface. It is an accepted rule that interface names should generally begin with the letter “I”.

The Type class contains methods that allow us to retrieve interface implementation details for any given type. To take a look at this, let’s add an IPerson interface to our class library project. The code below is the full definition of the IPerson interface:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyConsoleApplication
{
    public interface IPerson
    {
        #region Public Properties

        string Name { get; set; }       

        Int32 Age { get; set; }

        Int32 Height {get; set; }

        Int32 Weight { get; set; }        

        #endregion Public Properties

        #region Public Methods

        string SayHello();

        string Speak(string wordsToSpeak);

        string CallTheChildrenInForDinner(string firstChild, string secondChild);

        #endregion Public Methods
    }
}

Now that we’ve created our interface, let’s flip over to our Person class and implement the interface.

public class Person : IPerson

Now that we’ve done this, let’s modify the code in our Main() method to find the interfaces implemented by the Person type. See the code below:

Person person = new Person();
Type personType = person.GetType();
Type[] interfaces = personType.GetInterfaces();

If we place a breakpoint on the last line of the code above and add a Watch to the interfaces object, we will see the output displayed below:

Watch output for personType.GetInterfaces() method
Watch output for personType.GetInterfaces() method

This shows us that the IPerson interface is implemented by the Person type. If we expand the watch definition for the IPerson type we see all the properties for that that we saw for the Person class. The differences will be in the IsClass and IsInterface properties. Pretty simple.

System.Reflection – Working with the Assembly Class

Posted on Updated on

The objects within the System.Reflection namespace of the .NET Framework provide us a means to get assembly and type information at runtime. Reflection is useful in situations where you need to introspect assemblies and their types, either to gain information or to make use of their functionality dynamically without knowing its specifics in advance. Sounds pretty cool, right? Well it is!

This article will walk you through the basics of Reflection and the Assembly class and will be followed by additional articles that dive much more deeply into the objects within the System.Reflection namespace.

The System.Reflection.Assembly Class

The Assembly class is the primary class within the System.Reflection namespace. This will be our starting point as we take a look at Reflection.

First, we will create a simple Visual Studio solution that we will call ‘ReflectionWalkthroughSolution’.

In this solution, we will add a C# Class Library project which we will call ‘MyClassLibrary’.

Next, we will add a Console Application which we will call ‘MyConsoleApplication’. These names are nonsensical, but this solution is merely for demonstration purposes :).

So, after doing these things, our Solution Explorer should look like this:

.NET Reflection - Solution Explorer Getting Started

MyConsoleApplication is bolded because I have set it as the startup project. This will allow us to hit F5 to debug the app and be able to use the console application as our entry point.

Next, expand MyConsoleApplication and Add a Reference to the MyClassLibrary project. Once this is done we are ready to start writing a little code.

Assembly.GetExecutingAssembly() Method

The first thing we will look at is how to get a reference to the assembly that is currently executing. To do this we will add some simple code in the Main() method of our console application as shown below. After writing this code, place a breakpoint on the line, then hover the thisAssembly object and take a look at the properties of the assembly and their values.

static void Main(string[] args)
{
    //get the currently executing assembly
    Assembly thisAssembly = Assembly.GetExecutingAssembly();

}

With the code running, when the breakpoint is hit, we can right-click and Add a Watch to the thisAssembly object. When we hit F10 to step over the line, the Watch window is populated with information pertaining to the ‘thisAssembly’ object. See the illustration below.

Hint: Click the image to view it full-size.

.NET Reflection - Assembly.GetExecutingAssembly() Watch Window
Properties of the ‘thisAssembly’ Assembly object

 

Let’s discuss some of the properties that are shown in the Watch window above.

The CodeBase property returns the full path at which the assembly resides. In other words, the execution location for the assembly, including the name of the assembly.dll file itself.

The CustomAttributes property is an IEnumerable<CustomAttributeData> type that displays all of the custom attributes that exist at the assembly level. If you take a look you will see that many of these come directly from the AssemblyInfo.cs file that is found under the Properties folder of the project.

Assembly.CustomAttributes Illustration
The Assembly.CustomAttributes property expanded.

We will dive into custom attributes in a later post. Attributes can be applied to assemblies, classes, properties, methods, and more and through Reflection can be retrieved and read using the appropriate objects. These attributes exist at the assembly level. For now, just be aware that these attributes can be retrieved in this way.

The DefinedTypes property is an IEnumerable<TypeInfo> type that displays all of the types (classes) defined within the assembly. In our simple little console application we only have one defined type. See below.

The Assembly.DefinedTypes property expanded
The Assembly.DefinedTypes property expanded

Looking ahead to later posts, once you have a reference to an Assembly, knowing the types defined in the assembly allow you to dive into the assembly and then into each type as needed.

The FullName property is a string value that displays the full name of the assembly. This is important when discussing a topic such as strong naming.

The Assembly.FullName property
The Assembly.FullName property

There are other properties of the Assembly object that are noteworthy for this introduction. They are the GlobalAssemblyCache property which tells you whether or not the assembly is GAC’ed, the ImageRuntimeVersion which tells you the full version of the .NET Framework against which the assembly is compiled, and the MainfestModule which gives specific information about the assembly itself.

Getting Assemblies loaded in the Current AppDomain (AppDomain.Current.GetAssemblies() Method)

Now that we’ve taken a quick look at the properties of an Assembly and how to get a reference to the currently executing assembly, let’s branch out a little and retrieve all of the assemblies referenced and loaded into the current AppDomain. An AppDomain is an isolated environment in which an application executes. The AppDomain class belongs to the System namespace. To retrieve the AppDomain for the currently executing application, we will the AppDomain.Current property.

If we return to the Main() method of the Program class of our little console application and remove the code we wrote originally and replace it with this code:

static void Main(string[] args)
{
    Assembly thisAssembly = Assembly.GetExecutingAssembly();

    //Get assemblies loaded in the current AppDomain
    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

    //create a StringBuilder to hold assembly information
    StringBuilder sb = new StringBuilder();

    //iterate through the array and write info for each assembly
    foreach (Assembly assembly in assemblies)
    {
        //let's create some rudimentary formatted output to show property values
        //for each assembly
        sb.AppendLine("================================================================");
        sb.AppendLine(String.Format("Assembly: {0}", assembly.FullName));
        sb.AppendLine("================================================================");

        sb.AppendLine(String.Format("CodeBase: {0}", assembly.CodeBase));
        sb.AppendLine(String.Format("Location: {0}", assembly.Location));
        sb.AppendLine(String.Format("Number of Types: {0}", assembly.DefinedTypes.Count().ToString()));
        sb.AppendLine(String.Format("Number of Custom Attributes:  {0}", assembly.CustomAttributes.Count().ToString()));
        sb.AppendLine(String.Format(".NET Runtime Version: {0}", assembly.ImageRuntimeVersion));

        //you can add more stuff here to see more properties...
    }

    string output = sb.ToString();
}

When we view the formatted output from our StringBuilder, it should look something like this:

================================================================
Assembly: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll
Location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll
Number of Types: 3029
Number of Custom Attributes:  36
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.VisualStudio.HostingProcess.Utilities, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.HostingProcess.Utilities/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.HostingProcess.Utilities.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.dll
Number of Types: 12
Number of Custom Attributes:  17
.NET Runtime Version: v2.0.50727
================================================================
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Windows.Forms/v4.0_4.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
Number of Types: 2273
Number of Custom Attributes:  27
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Drawing/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
Number of Types: 303
Number of Custom Attributes:  25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
Number of Types: 2278
Number of Custom Attributes:  29
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.HostingProcess.Utilities.Sync/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities.Sync\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll
Number of Types: 5
Number of Custom Attributes:  18
.NET Runtime Version: v2.0.50727
================================================================
Assembly: Microsoft.VisualStudio.Debugger.Runtime, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.Debugger.Runtime/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Debugger.Runtime.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Debugger.Runtime\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Debugger.Runtime.dll
Number of Types: 10
Number of Custom Attributes:  17
.NET Runtime Version: v2.0.50727
================================================================
Assembly: vshost32, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Temp/ReflectionWalkthroughSolution/MyConsoleApplication/bin/Debug/MyConsoleApplication.vshost.exe
Location: C:\Temp\ReflectionWalkthroughSolution\MyConsoleApplication\bin\Debug\MyConsoleApplication.vshost.exe
Number of Types: 1
Number of Custom Attributes:  19
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Core/v4.0_4.0.0.0__b77a5c561934e089/System.Core.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
Number of Types: 929
Number of Custom Attributes:  25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml.Linq/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.Linq.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll
Number of Types: 86
Number of Custom Attributes:  22
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Data.DataSetExtensions/v4.0_4.0.0.0__b77a5c561934e089/System.Data.DataSetExtensions.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Data.DataSetExtensions\v4.0_4.0.0.0__b77a5c561934e089\System.Data.DataSetExtensions.dll
Number of Types: 25
Number of Custom Attributes:  24
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Microsoft.CSharp/v4.0_4.0.0.0__b03f5f7f11d50a3a/Microsoft.CSharp.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.CSharp\v4.0_4.0.0.0__b03f5f7f11d50a3a\Microsoft.CSharp.dll
Number of Types: 316
Number of Custom Attributes:  23
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_32/System.Data/v4.0_4.0.0.0__b77a5c561934e089/System.Data.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll
Number of Types: 1049
Number of Custom Attributes:  21
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll
Number of Types: 1229
Number of Custom Attributes:  25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: MyConsoleApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
================================================================
CodeBase: file:///C:/Temp/ReflectionWalkthroughSolution/MyConsoleApplication/bin/Debug/MyConsoleApplication.EXE
Location: C:\Temp\ReflectionWalkthroughSolution\MyConsoleApplication\bin\Debug\MyConsoleApplication.exe
Number of Types: 1
Number of Custom Attributes:  14
.NET Runtime Version: v4.0.30319

Pretty simple, right? Now let’s dig into the Assembly a little deeper.

Getting Types within an Assembly (Assembly.GetTypes() Method)

Every Assembly can contain one of more Types. We can retrieve all of these types via the Assembly.GetTypes() method. To try this out, we replace the code in our console application’s Program class’s Main() method with this code:

static void Main(string[] args)
{
    Assembly thisAssembly = Assembly.GetExecutingAssembly();

    //get all of the Types defined in this Assembly
    Type[] types = thisAssembly.GetTypes();

    //robust code always checks for null FIRST
    if (types != null && types.Length > 0)
    {
        //we'll create a StringBuilder for our formatted output
        StringBuilder sb = new StringBuilder();

        //iterate through the Type[] array
        foreach (Type type in types)
        {
            sb.AppendLine("===============================================================");
            sb.AppendLine(String.Format("Type Name: {0}", type.Name));
            sb.AppendLine("===============================================================");

            sb.AppendLine(String.Format("Type FullName: {0}", type.FullName));
            sb.AppendLine(String.Format("Namespace: {0}", type.Namespace));

            sb.AppendLine(String.Format("Is it a Class?: {0}", type.IsClass.ToString()));
            sb.AppendLine(String.Format("Is it an Interface?: {0}", type.IsInterface.ToString()));
            sb.AppendLine(String.Format("Is it Generic?: {0}", type.IsGenericType.ToString()));
            sb.AppendLine(String.Format("Is it Public?: {0}", type.IsPublic.ToString()));
            sb.AppendLine(String.Format("Is it Sealed?: {0}", type.IsSealed.ToString()));

            sb.AppendLine(String.Format("Qualified Name: {0}", type.AssemblyQualifiedName));

            if (type.BaseType != null && !String.IsNullOrEmpty(type.BaseType.Name))
            {
                sb.AppendLine(String.Format("Base Type: {0}", type.BaseType.Name));
            }

            //there are many, many more properties that an be shown...
        }

        string output = sb.ToString();
    }
}

In our console application, there is only one type, the Program class. When we execute the code above we see information for that single type.

===============================================================
Type Name: Program
===============================================================
Type FullName: MyConsoleApplication.Program
Namespace: MyConsoleApplication
Is it a Class?: True
Is it an Interface?: False
Is it Generic?: False
Is it Public?: False
Is it Sealed?: False
Qualified Name: MyConsoleApplication.Program, MyConsoleApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Base Type: Object

Now that we’ve moved down a level and looked at Types within an assembly, let’s move down one more level.

The next post discusses the System.Reflection.Type class and its properties and methods.