Click here to Skip to main content
14,265,882 members

Creating Dynamic Instances Using Custom Attributes with Constructor Values

Rate this:
3.10 (13 votes)
Please Sign up or sign in to vote.
3.10 (13 votes)
24 Nov 2016CPOL
How to create dynamic instances using custom attributes with constructor values

Introduction

This tip will show you how to create dynamic instances using custom attributes with constructor values.

Background

During refactoring of the code, I came across switch statement and one of my architects suggested that I use Strategy Pattern to refactor the code.

I did understand the Strategy Pattern and implemented the same. I was a little unhappy with the set of new objects that we were creating in the Context Class of Strategy Pattern.

Every time a new case was added, I had to create a class for the newly added type and add the dictionary value in the Context class code.

Something struck my mind and I thought of using the Reflection powered by Custom Attributes.

Although Reflection is a powerful tool, we should use it in places where it is really required. (It's like with great power comes great responsibility.)

Using the Code

Let's take a quick look at the problem at hand.

public enum ePassengerTitle
{
          Mr,
          Mrs,
          Doctor,
}
ePassengerTitle title = ePassengerTitle.Doctor;
switch (title)
{
            case ePassengerTitle.Mr:
                // do something
                break;
            case ePassengerTitle.Mrs:
                // do something
                break;
            case ePassengerTitle.Doctor:
                // do something
                break;
            default:
                break;
}

In the above code, there are 3 case conditions and if the cases keep growing, the maintainability of the code takes a hit.

Solution to the Problem

There are multiple ways in which this problem could be solved. I am going to show how we use custom attributes powered with reflections.

We create different classes for all the "Case" Statements - A little smell of Strategy Pattern.

An Interface defined.

public interface IPassengerTitleStrategy
 {
       void DoSomthing(string title);
 }

 [AutoResolve("Mr")]
   public class MrPassengerTitleStrategy : IPassengerTitleStrategy
   {
       public void DoSomthing(string title)
       {
           Console.WriteLine("The Title is" + title);
       }
   }

   [AutoResolve("Mrs")]
   public class MrsPassengerTitleStrategy : IPassengerTitleStrategy
   {
       public void DoSomthing(string title)
       {
           Console.WriteLine("The Title is" + title);
       }
   }

   [AutoResolve("Doctor")]
   public class DoctorPassengerTitleStrategy : IPassengerTitleStrategy
   {
       public void DoSomthing(string title)
       {
           Console.WriteLine("The Title is" + title);
       }
   }

If you closely observe each class, we have a Custom Attribute. That attribute is the Key to the solution.

Let's define it now.

public class AutoResolveAttribute : Attribute
{
       public AutoResolveAttribute(string name)
       {

       }
}

It has a constructor. It's an important aspect.

Next, we need to write Reflection logic which will find you all the classes where the attribute is decorated.

public static IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit)
           where TAttribute : System.Attribute
{
           return from a in AppDomain.CurrentDomain.GetAssemblies()
                  from t in a.GetTypes()
                  where t.IsDefined(typeof(TAttribute), inherit)
                  select t;
}

So what does the above method do. It searches through all the assemblies in the AppDomain and finds those classes that are decorated with AutoResolve attribute. It is a generic method so you can pass any attribute of your choice.

Once we get all the classes, we need to loop through those classes to find out which class has been decorated with the string value that we are looking for.

private static string GetAttributeName(string value)
{
         var getAttribute = GetTypesWith<AutoResolveAttribute>(true);
         foreach (var iAttributeValue in getAttribute)
         {

             var attributeValue = iAttributeValue.CustomAttributes.Select
                                  (x => x.ConstructorArguments[0].Value).First();
             if (attributeValue.ToString().Contains(value))

                 return iAttributeValue.FullName;

         }
         return string.Empty;
 }

So the above method takes the string as parameter and returns the FullName of the assembly.

Once we get the full name of the assembly, we now need to get an instance. So we use the below code for creating an instance.

public static object GetInstance(string strFullyQualifiedName)
{
          Type type = Type.GetType(strFullyQualifiedName);
          if (type != null)
              return Activator.CreateInstance(type);
          return null;
}

So finally, what do we do now.

We use the code. That is so obvious.

ePassengerTitle title = ePassengerTitle.Doctor;

 var typeCode = GetAttributeName(title.ToString());
 var getInstance = GetInstance(typeCode) as IPassengerTitleStrategy;
 getInstance?.DoSomthing(title.ToString());

So we get an instance of the specific switch value and you use it. ??

Finally, what did we achieve by doing so.

Pros

  1. Eradicated the need to write switch case
  2. Wrote more cleaner, more maintainable and scalable code
  3. Code that adheres to SOLID principles

Con

  1. Reflection Code - A powerful tool that needs to be used with care

License

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

Share

About the Author

Vaibhav M Nalwad
Software Developer (Senior) Tavant Technologies
India India
Crazy Software Developer and a passionate lover of Amour.

My only aim as a developer is to write more cleaner, more maintainable and extensible code.

Comments and Discussions

 
GeneralMy vote of 3 Pin
E. Scott McFadden18-Nov-16 7:17
professionalE. Scott McFadden18-Nov-16 7:17 
GeneralRe: My vote of 3 Pin
Jeremy Stafford 120-Nov-16 15:59
memberJeremy Stafford 120-Nov-16 15:59 
GeneralRe: My vote of 3 Pin
Vaibhav M Nalwad21-Nov-16 1:22
professionalVaibhav M Nalwad21-Nov-16 1:22 

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.

Tip/Trick
Posted 16 Nov 2016

Tagged as

Stats

12.2K views
9 bookmarked