Saturday, 30 June 2012

Generic with .NET Reflection


Making a copy of a generic list to another generic list. This seems a silly question if the type of the List is known in Advance.

Suppose I have following 3 classes –

    public class A_Student
    {
        public int RollNo { get; set; }
        public string  Name { get; set; }
        public string Address { get; set; }
    }

    public class B_Student
    {
        public int RollNo { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
    }

    public class C_Student
    {
        public int RollNo { get; set; }       
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Address { get; set; }
    }

Now say we created a List of  A_Student as below –

    List<A_Student> A_Students = new List<A_Student>()
    {
       new A_Student() { RollNo = 1, Name = "ABC", Address = "Bangalore" },
       new A_Student() { RollNo = 2, Name = "XYZ", Address = "Delhi" }
    };

Next I want the same copy of  A_Students into List<B_Student> B_Students. This is really simple question – iterate the source list and add it to destination list as below -

public List<B_Student> Copy(List<A_Student> aStudents)
        {
            List<B_Student> bStudents = new List<B_Student>();
            foreach (A_Student aStudent in aStudents)
            {
                B_Student bStudent = new B_Student();
                bStudent.RollNo = aStudent.RollNo;
                bStudent.Name = aStudent.Name;
                bStudent.Address = aStudent.Address;
                bStudents.Add(bStudent);
            }
            return bStudents;
        }
Now I want the same into List<C_Student>. What you think is creation of the similar method for C_Student. What if there are 100s of Students Type then you are going to create 100 Copy methods. This is like programming by a Kid. What about a single method can be used for all Such Cases. There cames Generic Method in picture.

Now the challenge comes when you want the make the above Copy method Generic. Meaning the Type of the Source object and Destination object  is not known in advance. Now several questions will come to your mind like – How can I create the object just by knowing the type? How will I get the properties of the Source object  and set the same value to Destination object.

Here is the solution- .NET Reflection is the hero in solving the above mentioned problem. Find below the code.

public List<U> Copy<U,T>(List<T> sourceList)
        {
            Type sourceType = typeof(T);
            List<object> destinationList = new List<object>();
            var properties = sourceType.GetProperties();
           
            foreach (T item in sourceList)
            {
                var destinationObj = Activator.CreateInstance(typeof(U));
                var props = destinationObj.GetType().GetProperties();
                foreach (PropertyInfo pi in props)
                {
                    object propValue;
                    try
                    {
                        propValue = (typeof(T)).GetProperty(pi.Name).GetValue(item, null);
                    }
                    catch (Exception ex)
                    {
                        propValue = null;
                    }
                    pi.SetValue(destinationObj, propValue,null);
                }
                destinationList.Add(destinationObj);
            }
            return destinationList.Cast<U>().ToList() ;
        }

To call the above method simply call –

List<B_Student> B_Students = Copy<B_Student, A_Student>(A_Students);

There is a small catch in the above Generic Method. What if the destination object property doesn’t match with source property. Like,  C_Student Doesn’t have Name property instead FirstName and LastName. So what we are going to get with the following -

List<C_Student> C_Students = Copy<C_Student, A_Student>(A_Students);



Since the property of the Destination object doesn’t have any match in the Source type it will remain null as below –