Click here to Skip to main content
15,887,214 members
Articles / Programming Languages / C#

Dynamic Construction of Functionality of an Application by Reflection Tools

Rate me:
Please Sign up or sign in to vote.
4.45/5 (8 votes)
6 Oct 2016CPOL7 min read 14.9K   242   10   9
It is demonstrated how to load and to analyze at run time a class library, how to select proper methods marked by the custom attributes and to use them in order to extend the application's functionality.

Introduction

The .NET reflection provides developers with complete tools to discover and, furthemore, to construct an executable code at run time. There are many articles on codeproject.com dedicated to these tools. We will recall some of their important features and we will demonstrate a practical example of using them.

We will explore some class library and select a collection of the specialized methods wich we need to run in separated threads. The custom attribute will help us to identify the proper methods and to extend the application's GUI.

Background

Some times ago I have published on codeproject.com the article "Covering a BackgroundWorker by an Own Class to Lighten the Creation of a Multithread Application" about execution of different sorting algorithms in parallel threads. The described application has a defect: the set of algorithms is hard-coded in the program and an user can not change it. This defect troubles me till now, thus I decided to fix it by dynamic loading of proper methods at run time. Interestingly, such approach makes it possible to reduce dependences in the application and to improve its architecture.

Reflection tools

The process of examination at run time of a compiled code is known as reflection. In the .NET world we can pick and investigate any exe or dll programmatically, obtain detailed information about their classes, dynamically instantiate these classes and execute their code, etc. Namespace System.Reflection contains a set of classes that serve for these purposes. I'll recall some of them that are needed to solve my task.

Assembly loading

Suppose we need TheImportantClass for our application, we have several assemblies with the class implementation produced by different manufacturers and we don't know at design time wich assembly we will prefer. Can we do the choice of assembly at run time? Yes we can! It is possible to determine the name of the assembly at run time and to load it dynamically.

Look at the code snippet below.

C#
uses System.Reflection

public void LoadAssembly()
{
    // Get the assembly name in any way
    string assemblyName = myApplication.GetAssemblyName();
    // Load the assembly dynamically
    try
    {
        Assembly dynamicAssembly = Assembly.Load(assemblyName);
    }
    catch (System.IO.FileNotFoundException ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message, "Assembly error",
           MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
    // We suppose that the assembly name coincides with the name of its namespace
    string nameOfNamespace = assemblyName;
    // Use the assembly by GetTypes() and so on
    System.Type importantClassType = dynamicAssembly.GetType(nameOfNamespace + ".TheImportantClass");
    // Use the class with help of the instance of System.Type
    // ...

Static method Assembly.Load() takes a name of the assembly - the file name without any extension - and looks for the assembly in the application local folder and than in the global cache of assemblies.

Use static method Assembly.LoadFrom() to determine the full path to the assembly:

C#
    string assemblyPath = myApplication.GetAssemblyPath();
    Assembly dynamicAssembly = Assebly.Load(assemblyName);
    string nameOfNamespace = System.IO.Path.GetFileNameWithoutExtension(assemblyPath);
    System.Type importantClassType = dynamicAssembly.GetType(nameOfNamespace + ".TheImportantClass");
    // ...

Type examination

When the assembly is loaded we get the instance of Assembly wich helps us to discover types in the assembly. We can get a certain type if we know its name (like in the snippets below):

C#
// Get certain type
    Type aClass = dynamicAssembly.GetType(theQualifiedNameOfClass);
    // where string theQualifiedNameOfClass describes the class name preceded by the name of
    // its namespace like this: "CoolAssembly.TheImportantClass"

// Deal with aClass

Or can get an array of all types defined in the assembly:

C#
// Get ALL types defined in the assembly
    Туре[] classes = dynamicAssembly.GetTypes();
    foreach (Type definedType in classes)
    {
        myApplication.DoSomethingWith(definedType);
    }

Every instance of System.Type is a key to reflection of the corresponding class. It provides a set of properties to explore the class characteristics: IsAbstract, IsEnum, IsSealed etc. It also provides a set of methods in singular and plural forms, like Assembly.GetType(typeName) and Assembly.GetTypes(), in order to get any member of the class. For example, we can use Type.GetProperty(propName) to get a single PropertyInfo object or Type.GetProperties() to retrieve an array PropertyInfo[]. Therefore it becomes possible to analyze either one specific property of the class or all the properies.

The PropertyInfo object is useful to get (or set) a value of the corresponding property of any instance of the discovered type. Just call the method aPropertyInfo.GetValue(instanceOfDiscoveredType).

In order to solve my task I need to get an information about all sorting methods in the class. So I will use Type.GetMethods() and analyze the received array MethodInfo[]. The MethodInfo instance provides us with the complete information about the method. We can use a wide range of properties (such as IsAbstract, IsConstructor, IsPublic, IsStatic, Name, ReturnType etc) to examine it and methods (such as CreateDelegate(), GetCustomAttributes(), GetParameters(), Invoke() etc) to manipulate it.

How can I separate the proper sorting methods from all the methods defined in the class? In other words, how can I select corresponding elements of the array MethodInfo[]? The method that is able to sort an array of integers in a background thread (in my application) takes three arguments of certain types: int[], BackgroundWorker and DoWorkEventArgs. We can get the detailed description of the method's parameters as an array ParameterInfo[] by GetParameters() method. Then we can check the quantity of parameters, their types and so on:

C#
List<MethodInfo> methods = new List<MethodInfo>();
foreach (MethodInfo method in sortMethodProviderType.GetMethods())
{
    parms = method.GetParameters();
    if (parms.Length == 3 && parms[0].ParameterType == typeof(int[]) &&
        parms[1].ParameterType == typeof(BackgroundWorker) &&
        parms[2].ParameterType == typeof(DoWorkEventArgs))
    {
        methods.Add(method);
    }
}

The MethodInfo.Name property can be used to display the selected methods in the GUI:

C#
// methodComboBox is the GUI element for making selection of a sorting method
   foreach (MethodInfo method in methods)
   {
       methodComboBox.Items.Add(method.Name);
   }

In order to start any method we can use the method MethodInfo.Invoke(theMethodOwner,Parameters):

C#
// First argument must be null for the static method
   selectedMethod.Invoke(null, new object[] { arrayToSort, actualBackgroundWorker, eventArgs });
// where selectedMethod is the instance of MethodInfo

So far, I have described a possible but not the best way to select the required methods and to construct the GUI. If the application's code hasn't a reference to the assebly where the parameters types are defined, then the method recognition by type comparison becomes impossible. If we use code names of the methods in the application combobox (or listbox) then the GUI becomes unclear. We cope with these difficulties by code attributes described below.

Custom attributes

Attributes are special objects that allow us to decorate the code with additional meta-data. There are many attributes defined in the .NET namespaces: CLSCompliantAttribute, ObsoleteAttribute, SerializableAttribute, TestClassAttribute and more and more. The compiler, a testing framework or another program reads the attributes in order to make a decision on how to work with our code. For example, when the compiler reads [CLSCompliant] attribute before a class definition, it will check if each public member of this class is compatible with CLS. The BinaryFormatter.Serialize() method is looking for the [Serializable] and so on. If a class has an attribute, then the attribute type signals about certain "property" of the class. Thus, the type of an attribute is the most important. Furthemore, an attribute can provide any additional information in its properties. For example, [Obsolete("The Warning Message")] attribute forces the compiler to add the warning with text "The Warning Message" to the Error List of the IDE.

For our purpose we can use a proper predefined attribute or define our own one. A custom attribute class must inherit the System.Attribute class and its name must terminate with the suffix "Attribute". An attribute incapsulates data in its property wich must be set up by the constructor. We can define additional rules of usage of the attribute by the "attribute of attribute" [AttributeUsage]: we can specify target of the attribute, restrict the multiple usage of it and so on.

To mark the proper sorting methods I used the MethodNameAttribute described below:

C#
    // The special attribute marks sorting methods designed for binding at run time.
    // It holds the method name(s) for the user interface
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class MethodNameAttribute: System.Attribute
    {
        public string OuterName { get; set; }
        // the optional property holds ukrainian name of a method
        public string LocalName { get; set; }
        public MethodNameAttribute(string name)
        {
            OuterName = name;
        }
    }

The usage of the attribute looks like this:

C#
    // Bubble sort algorithm (simple exchange method)
    [MethodName("Bubble sort", LocalName = "Метод бульбашки")]
    public static void BubbleSortInBackground(int[] arrayToSort, BackgroundWorker worker,
        DoWorkEventArgs e)
    {
        for ...

It is not necessary to write a full name of the attribute - the compiler adds suffix "Attribute" itself. The constructor of MethodNameAttribute takes a string argument to initialize the property OuterName. If we want to set up an optional property of the attribute, then we have to use the special syntax "property = value", as in the code above.

Reflection in action

Let me recall the goal: to expand functionality of the application with ability to load a set of sorting methods at run time. I did several steps to achieve this goal.

First. I don't know the name of an assembly containing definition of sorting methods. I will get it as a result of an OpenFileDialog execution. But I am sure that the correct assembly contains classes SortMethodProvider and MethodNameAttribute. I will check this.

C#
public bool LoadAssembly(string assemblyName)
{
    ...
    System.Type sortMethodProviderType = assembly.GetType(nameOfNamespace + ".SortMethodProvider");
    System.Type methodNameAttrType = assembly.GetType(nameOfNamespace + ".MethodNameAttribute");
    if (sortMethodProviderType == null || methodNameAttrType == null)
    {
        MessageBox.Show(
            "SortMethodProvider or MethodNameAttribute not found in " + assemblyName + " assembly",
            "Sorting assembly error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return false;
    } ...
}

Second. I will select proper sorting method from all SortMethodProvider's methods which are static and marked by the MethodNameAttribute. One of the easiest way to execute the selection uses LINQ:

C#
    MethodInfo[] methods = (from m in sortMethodProviderType.GetMethods()
                 where (m.IsStatic && m.IsDefined(methodNameAttrType, false))
                 select m).ToArray<MethodInfo>();

Third. I will read the UI name of the method from the property of its attribute:

C#
    string[] methodNames = new string[methods.Length];
    PropertyInfo propInfo = methodNameAttrType.GetProperty("OuterName");
    for (int i = 0; i < methods.Length; ++i)
        methodNames[i] = propInfo.GetValue(methods[i].GetCustomAttribute(methodNameAttrType)).ToString();

Fourth. I will create a new sorting thread and a new visual component in order to reflect the process of sorting dynamically on the user's request. (Don't forget to set Parent property of the visual component created dynamically!)

C#
    // Dynamic creation of a new arrayView component
    private void btnAdd_Click(object sender, EventArgs e)
    {
        // an array to sort and view
        int[] array = model.GetArray();
        ArrayView a = new ArrayView(new Point(xLocation[Views.Count], yLocation), array);
        // set names to the arrayView's combobox
        a.AddRange(controller.MethodNames);
        // set event handlers
        a.ComboIndexChanged += arrayView_ComboIndexChanged;
        a.SortingComplete += DecreaseThreadsRunning;
        // set a new backGroundSorter
        a.SetSorter(controller.GetSorter(array));
        // visualize new component
        a.Parent = this;
        // store the component
        Views.Add(a);
    }

The previous version of the application was constructed according to the MVC pattern, but the Model holds a reference to the Controller to make some changes with the arrays. It is not very good. In the new version only the View communicate with the Model and with the Controller separately.

Conclusion

The .NET reflection tools are cool. They work good and are very useful to construct flexible applications.

Get fun with the new version of "Sorting in Threads" application!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer Ivan Franko National University of Lviv
Ukraine Ukraine
Head of Programming department in Ivan Franko National University of Lviv, UKRAINE

Comments and Discussions

 
GeneralMy vote of 5 Pin
LightTempler22-Jul-21 9:28
LightTempler22-Jul-21 9:28 
GeneralRe: My vote of 5 Pin
Сергій Ярошко22-Jul-21 10:28
professionalСергій Ярошко22-Jul-21 10:28 
QuestionThank you Pin
GuyThiebaut29-Mar-18 0:07
professionalGuyThiebaut29-Mar-18 0:07 
GeneralGood Pin
Hooman_Kh12-Sep-16 7:20
Hooman_Kh12-Sep-16 7:20 
GeneralRe: Good Pin
Сергій Ярошко12-Sep-16 8:15
professionalСергій Ярошко12-Sep-16 8:15 
GeneralMy vote of 5 Pin
D V L3-Aug-16 22:35
professionalD V L3-Aug-16 22:35 
GeneralRe: My vote of 5 Pin
Сергій Ярошко4-Aug-16 5:06
professionalСергій Ярошко4-Aug-16 5:06 
QuestionGreat Article Pin
Garth J Lancaster3-Aug-16 1:57
professionalGarth J Lancaster3-Aug-16 1:57 
AnswerRe: Great Article Pin
Сергій Ярошко3-Aug-16 7:18
professionalСергій Ярошко3-Aug-16 7:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.