a study about Reflection Class for C# programmer

Reflection provides objects (of type Type) that describe assemblies, modules and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties ( thanks to Microsoft for the definition  Ref: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection ).

I prepare sort of codes to make Reflection approach closer to you :)

Here are the keywords to be mentioned in the study:
  • Load Assembly, Initiate Class then invoke method(s) with/out parameters - Beginner Level
  • Load Class and Invoke method through Type Def. (string name) with parameters - Beginner Level
  • Suppress cast (convert) operation for incompatible classes with Reflection - Intermediate Level
  • Anonymous Type calls via Reflection - Advance Level

First of all, I would like to describe all classes to be used in the lab. I am going to play with Mathematics mainly Geometry.

IShape.cs : Interface of the shape class.



namespace MathematicsForReflectionScenarios
{
    public interface IShape
    {
        int ColorCode { get; set; }
    }
}


Shape.cs : Rests class are going to be inherited from Because, I want to generate bad conversion from one class 2 other class without Reflection.


namespace MathematicsForReflectionScenarios
{
    public class Shape : IShape
    {
        private int colorCode=-1;
        public int ColorCode { get { return colorCode; } set { colorCode = value; } }

        public int Area()
        {
            throw new NotImplementedException();
        }

        public int Perimeter()
        {
            throw new NotImplementedException();
        }
    }
}

Rectangle.cs : Operations will take in place.


namespace MathematicsForReflectionScenarios
{
    public sealed class Rectangle : Shape
    {
        int height;
        int weight;
        public Rectangle()
        {

        }

        public Rectangle(int Height, int Weight)
        {
            height = Height;
            weight = Weight;
        }

        public new int Area()
        {
            return height * weight;
        }

        public new int Perimeter()
        {
            return 2 * (weight + height);
        }

        public int customArea(int Height, int Weight)
        {
            return Height * Weight;
        }

        public int customPerimeter(int Height, int Weight)
        {
            return 2 * (Height + Weight);
        }
    }
}

Square.cs : Operations will take in place.



namespace MathematicsForReflectionScenarios
{
    public sealed class Square : Shape
    {
        int side;
        public Square()
        {

        }

        public Square(int Side)
        {
            side = Side;
        }

        public new int Area()
        {
            return side * side;
        }
        public new int Perimeter()
        {
            return 4 * side;
        }

        public int customArea(int Side)
        {
            return Side * Side;
        }

        public int customPerimeter(int Side)
        {
            return 4 * Side;
        }
    }
}


It was compiled and built so "MathematicsForReflectionScenarios.dll" is generated. 

According to above design, Let's start our journey.

Scenario 1:
- Load library
- Initiate Rectangle class 
- Invoke Method with/out Parameters for Rectangle
- Write the returning value of method to the Console.




 // Load the library from file
 Assembly assembly = Assembly.LoadFrom("MathematicsForReflectionScenarios.dll");
 MethodInfo methodInfo = null;
 object result = null;

 //Rectangle
 Console.WriteLine("Rectangle ----------");
 Type type = assembly.GetType("MathematicsForReflectionScenarios.Rectangle");
 if (type != null)
 {
     // Create a constructor for the Rectangle class.
     // Rectangle class already takes two parameters : Height (int) and Width (int) so
     // create Type[] array for each parameter seperately
     ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(int), typeof(int) });

     // Initate the class with 4 and 7
     object instance = constructor.Invoke(new object[] { 4, 7 });
     try
     {
         // sample of a method call without parameter
         methodInfo = type.GetMethod("Area");
         result = methodInfo.Invoke(instance, null);
         // Write the result to the output channel
         Console.WriteLine("Area Method = " + result.ToString());

         // sample of a method call without parameter
         methodInfo = type.GetMethod("Perimeter");
         result = methodInfo.Invoke(instance, null);
         // Write the result to the output channel
         Console.WriteLine("Perimeter Method = " + result.ToString());

         // sample of a method call with parameter
         object[] parametersArray = new object[] { 3, 8 };
         methodInfo = type.GetMethod("customArea");
         result = methodInfo.Invoke(instance, parametersArray);
         // Write the result to the output channel
         Console.WriteLine("customArea Method = " + result.ToString());

         // sample of a method call with parameter
         parametersArray = new object[] { 3, 8 };
         methodInfo = type.GetMethod("customPerimeter");
         result = methodInfo.Invoke(instance, parametersArray);
         // Write the result to the output channel
         Console.WriteLine("customPerimeter Method = " + result.ToString());
     }
     catch (Exception ex)
     {
         Console.WriteLine(ex.Message);
     }
 }

It is time to make situation complex.
Suppose that we have a method and we send Rectangle class than cast it to the Square which should throw error (Cast mismatch) :)


namespace PlayWithReflection
{
    class Program
    {
        public Shape moveColorCodeTo1 (Shape shape)
        {
            Assembly assembly = Assembly.LoadFrom("MathematicsForReflectionScenarios.dll");
            Type type = assembly.GetType(shape.GetType().ToString());
            ConstructorInfo constructor = type.GetConstructor(new Type[] { typeof(int), typeof(int) });
            object instance = constructor.Invoke(new object[] { 4, 7 });
            PropertyInfo propInfo = type.GetProperty("ColorCode");


            propInfo.SetValue(instance, 1);

            return (Shape)instance;
        }
        static void Main(string[] args)
        {
            Program prg = new Program();
                
            Rectangle rec = new Rectangle(1, 2);
            rec.ColorCode = 0;
            Console.WriteLine("Reflaction : Dynamic Invoke parameter and return objects are different classes Rectangle to Square ----------");
            Square s = (Square)prg.moveColorCodeTo3(rec);
            Console.WriteLine("Color Code = " + s.ColorCode.ToString());
        }
    }
}


Let's see how are we going to invoke properties and methods of Anonymous Type. We define Circle type and call methods as well as properties:



namespace PlayWithReflection
{
    class Program
    {
        public object CircleClassOnTheFly()
        {
             var Circle = new
            {
                shape = new Shape() ,
                radious = -1,
                pi = -1,
                Area = new Func<int>(() => { return (2 * 4 * 4); }),
                Perimeter = new Func<int>(() => { return (2 * 3 * 4); })
            };
            Assembly assembly = Assembly.LoadFrom("MathematicsForReflectionScenarios.dll");
            Type type = Circle.GetType();
            ConstructorInfo constructor = type.GetConstructors()[0];
            object instance = constructor.Invoke(new object[] { new Shape() { ColorCode = 1 } , 4 , 3, Circle.Area, Circle.Perimeter });

            return instance;
        }
        static void Main(string[] args)
        {
           Program prg = new Program();
                
           MethodInfo methodInfo = null;
           object result = null;
Console.WriteLine("Reflaction : Circle class through generic type ----------");
           var genericClass = prg.CircleClassOnTheFly();
           type = genericClass.GetType();
           foreach(PropertyInfo prop in type.GetProperties())
           {
               object instanceTemp = prop.GetValue(genericClass);
               Type propType = prop.GetValue(genericClass).GetType();
                                   
               if (propType.GetProperties().Length > 0) // property is a reference to another class or struct
               {
                   foreach (PropertyInfo pInfo in propType.GetProperties())
                   {
                       if (pInfo.Name == "Method")
                       {
                           methodInfo = (MethodInfo)pInfo.GetValue(instanceTemp);
                           Func<int> converted = (Func<int>) Delegate.CreateDelegate(pInfo.ReflectedType, null, methodInfo);
                           Console.WriteLine(prop.Name + " Method = " + converted().ToString());

                       }
                       else
                           if (pInfo.Name != "Target")
                               Console.WriteLine(pInfo.Name + " = " + pInfo.GetValue(instanceTemp));
                   }
               }
               else
               {
                   Console.WriteLine(prop.Name + " = " + prop.GetValue(genericClass).ToString());
               }           
   }
 }
}




Here is the source code, download it, debug it, play it !

https://github.com/TheMaty/PlayWithReflection

I do not have any chance to consider the approach from performance perspective. But I will update the article if I have something on that.


Enjoy.




Comments

Popular posts from this blog

Assembly Microsoft.Dynamics.Service.Plugins.dll can not be loaded. Dynamics CRM 365 Engine version 9 - CRM User creation error

Exception caught instantiating TERADATA report server extension SQL Reporting Services

Could not load file or assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. The system cannot find the file specified at Configuration class initiation in CrmServiceHelper.cv