logo

.NET Reflection - Copy Class Properties

By Robbe Morris
Printer Friendly Version
View My Articles
3159 Views
    

This code sample demonstrates how to copy class properties from one class to another even if they are not the same type. It also demonstrates how to validate a class's required properties dynamically. Both of these can increase your coding productivity especially when dealing with web service versions of your business classes.


PropertyHandler.cs
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Diagnostics;

namespace MyApplication
{
    public class PropertyHandler
    {
        #region Set Properties
        public static void SetProperties(PropertyInfo[] fromFields,
                                         PropertyInfo[] toFields,
                                         object fromRecord,
                                         object toRecord)
        {
            PropertyInfo fromField = null;
            PropertyInfo toField = null;

            try
            {

                if (fromFields == null)
                {
                   return;
                }
                if (toFields == null)
                {
                   return;
                }

                for (int f = 0; f < fromFields.Length; f++)
                {

                    fromField = (PropertyInfo)fromFields[f];

                    for (int t = 0; t < toFields.Length; t++)
                    {

                        toField = (PropertyInfo)toFields[t];

                        if (fromField.Name != toField.Name)
                        {
                            continue;
                        }

                        toField.SetValue(toRecord,
                                         fromField.GetValue(fromRecord, null),
                                         null);
                        break;

                    }

                }
 
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion

        #region Set Properties
        public static void SetProperties(PropertyInfo[] fromFields,
                                         object fromRecord,
                                         object toRecord)
        {
            PropertyInfo fromField = null;
         
            try
            {

                if (fromFields == null)
                {
                    return;
                }

                for (int f = 0; f < fromFields.Length; f++)
                {

                    fromField = (PropertyInfo)fromFields[f];

                    fromField.SetValue(toRecord,
                                       fromField.GetValue(fromRecord, null),
                                       null);
                }

            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion
  
        
    }
} 
PropertyHandler Sample For Identical Classes
MyClass record = new MyClass();
MyClass newRecord = new MyClass();
PropertyInfo[] fromFields = null;

fromFields = typeof(MyClass).GetProperties();

PropertyHandler.SetProperties(fromFields, record, newRecord);
 
PropertyHandler Sample For Similar Classes
 
MyClass record = new MyClass();
MyOtherClass newRecord = new MyOtherClass();
PropertyInfo[] fromFields = null;
PropertyInfo[] toFields = null;

fromFields = typeof(MyClass).GetProperties();
toFields = typeof(MyOtherClass).GetProperties();

PropertyHandler.SetProperties(fromFields,toFields,record, newRecord);
 
Self Validation Methods
// Some of the code below relies on runtime reflection.  Certain aspects
// of reflection are detrimental to performance.  Where possible, you
// can create static instances of the reflection results.  It will give
// the power without the overhead of using reflection.

 // Here is a sample business class layer self validation method.

 
 private bool ValidateSave(CellAlignment record)
 {
 
   object[,] properties = null;
   List<string> returnMessages = new List<string>();

   try
   {

     // Use the .GetFields() method mentioned later in this sample.

     properties = this.GetFields(this.GetType());

     if (!record.ValidateRequiredProperties(properties,
                                            returnMessages))
     {
        for (int i = 0; i < returnMessages.Count; i++)
        {
           Debug.WriteLine(returnMessages[i]);
        }
        return false;
     }

   }
   catch (Exception) { throw;}
   return true;
 
 }
 



 // This code was extracted from the ADO.NET Code Generator mentioned
 // above.

 // Here is a sample property to demonstrate how to use the
 // ColumnAttributes class.  It sets this as being required
 // and tells the validator that it is an int data type.

 private int cellAlignmentID = 0;
 
 [ColumnAttributes("CellAlignmentID",true,"int")]
 public int CellAlignmentID
 {
   get 
   {
      return cellAlignmentID;
   }
   set 
   { 
      if (value != cellAlignmentID)
      { 
        cellAlignmentID = valuue;
      }
   }
 }





// The ColumnAttributes class itself.

 
[AttributeUsage(AttributeTargets.Property,AllowMultiple = true)] 
 public sealed class ColumnAttributes : System.Attribute  
 { 
 
   private string columnName;
   private bool isRequired;
   private string propertyType;
 
   public string ColumnName
   {
     get { return columnName; } 
   }
 
   public bool IsRequired
   {
     get { return isRequired; } 
   }
 
   public string PropertyType
   {
      get { return propertyType; } 
   }
 
   public ColumnAttributes(string columnNameValue,
                           bool isRequiredValue,
                           string propertyTypeValue)
   { 
      columnName = columnNameValue; 
      isRequired = isRequiredValue; 
      propertyType = propertyTypeValue; 
   } 
 
 } 




// Here is a method we can use to get an array of
// PropertyInfo objects as well as custom attributes.
// Make sure every class has this in method available
// to run on itself.  The ADO.NET Code Generator has
// this apart of the CustomAttributes.cs that all
// DataObjects inherit.

 
 public object[,] GetFields(Type t) 
 { 
 
   PropertyInfo[] fields = t.GetProperties(); 
   PropertyInfo field; 
   Attribute[] attributes; 
   object[,]  structureInfo = new object[fields.Length,2];  
 
   try 
   { 
      for(int i =0;i<fields.Length;i++) 
      {       
        field = fields[i];
        attributes =  Attribute.GetCustomAttributes(field,
                                           typeof(DataObjects.Tables.ColumnAttributes),
                                           false); 
        structureInfo[i,0] = field; 
        structureInfo[i,1] = attributes; 
      } 
   } 
   catch (Exception) { throw; } 
   return structureInfo; 
 } 
  
 
 public bool ValidateRequiredProperties(object[,] properties,
                                        List<string> returnMessages)
 {
    bool returnValue = true;
    PropertyInfo property;
    Attribute[] attributes;
    ColumnAttributes columnAttribute = null;

    try
    {


      if (properties == null)
      {
        throw new Exception("Please pass in the results of this.GetFields().");
      }
 
      if (returnMessages != null) 
      { 
         returnMessages.Clear(); 
      } 
 
      for (int i = 0; i <= properties.GetUpperBound(0); i++) 
      { 
 
          property = (PropertyInfo)properties[i, 0]; 
          attributes = (Attribute[])properties[i, 1]; 

          foreach (Attribute attribute in attributes) 
          { 

            columnAttribute = (ColumnAttributes)attribute; 

            if (!columnAttribute.IsRequired) 
            { 
              continue; 
            } 

            //    Debug.WriteLine(columnAttribute.ColumnName); 
           //     Debug.WriteLine(property.GetValue(this,null)); 

           if (!this.ValidateRequiredPropertyInfo(columnAttribute, 
                                                  property)) 
           { 

              returnValue = false; 

              if (returnMessages != null) 
              { 
                 returnMessages.Add(columnAttribute.ColumnName + " is required.");
              } 
           } 

         } 

     } 
     } 
     catch (Exception) { throw;}
     return returnValue;
   }
   
 
     public bool ValidateRequiredProperties(object[,] properties) 
     { 
        List<string> returnMessages = null; 

        try
        {
           return ValidateRequiredProperties(properties,
                                             returnMessages); 
        } 
        catch (Exception) { throw; } 
     } 
 
     public bool ValidateRequiredPropertyInfo(ColumnAttributes columnAttribute, 
                                              PropertyInfo property) 
     { 
        try 
        { 
          switch (columnAttribute.PropertyType.ToLower()) 
          { 

              case "int": 

                  if ((int)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

              case "int16": 

                  if ((Int16)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

              case "int32": 

                  if ((Int32)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

              case "int64": 

                  if ((Int64)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

              case "uint": 

                  if ((uint)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

              case "uint16": 

                  if ((UInt16)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

              case "uint32": 

                  if ((UInt32)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

              case "uint64": 

                  if ((UInt64)property.GetValue(this, 
                                        null) == 0) 
                  { 
                      return false; 
                  } 
                  
                  break; 

                case "double": 

                   if ((double)property.GetValue(this, 
                                                 null) == 0) 
                   { 
                       return false; 
                   } 

                   break; 

                 case "float": 

                     if ((float)property.GetValue(this, 
                                                  null) == 0) 
                     { 
                          return false; 
                     } 

                      break; 

                 case "single": 

                     if ((Single)property.GetValue(this, 
                                                  null) == 0) 
                     { 
                          return false; 
                     } 

                      break; 

                  case "string": 
                  
                      if ((string)property.GetValue(this, 
                                                    null) == String.Empty) 
                      {
                          return false; 
                      } 
                      
                      break; 

                  case "guid": 
                  
                      if ((Guid)property.GetValue(this, 
                                                  null) == Guid.Empty) 
                      {
                          return false; 
                      } 
                      
                      break;
                      

                  default: 

                      if (property.GetValue(this, 
                                            null) == null) 
                      { 
                          return false; 
                      } 
                      
                      break; 
              } 
            } 
            catch (Exception) { throw; } 
            return true; 
         } 
 
 

Biography - Robbe Morris
Robbe has been a Microsoft MVP in C# since 2004. He is also the co-founder of EggHeadCafe.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.


Didn't Find The Answer You Were Looking For?

EggHeadCafe has experts online right now that may know the answer to your question.  We pay them a bonus for answering as many questions as they can.  So, why not help them and yourself by becoming a member (free) and ask them your question right now?
Ask Question In Live Forum

If you have an OpenID and do not want to become a member of the EggHeadCafe forum, you can also sign on to Chat Chaos and post your question to our real time Silverlight chat application.
Ask Question In Chat Chaos

Article Discussion: .NET Reflect - Copy Class Properties
Robbe Morris posted at Tuesday, October 03, 2006 1:48 PM
Original Article
 

  .NET Reflect - Copy Class Properties
Marc Gravell replied to Robbe Morris at Sunday, October 08, 2006 3:33 AM

Interesting read; one comment: it is a shame it uses PropertyInfo and not PropertyDescriptor; this would make it more usable for dynamic (runtime) properties, and would also allow use of .ShouldSerializeValue() to remove all of ValidateRequiredPropertyInfo (including support for DefaultValueAttribute). Obviously, when writing values and if storing only non-defaults you may need to .ResetValue() the ones that don't have entries.

This would still allow full reflective usage via TypeDescriptor.

Marc

 

Want a suggestion
Baishakhi Banerjee replied to Robbe Morris at Thursday, November 16, 2006 3:56 AM
I have a question. I am creating a web form that has about 115 fields in it values of which need to be assigned, inserted,updated or deleted from/into an SQL server database. Writing functions in which values from all controls are passed as parameters in stored procedures seems a tedious jobs. I was thinking in terms of creating an class, since the form is related to master database updation, thus creating a class with properties sames as the master table fields. But i do have problem how do i get the list of properties of a particular object and how to mark fields as mandatory in the class and how to map appropriate controls to appropriate object properties. Pls help.
 

Simply download the ado.net code generator for sql server
Robbe Morris replied to Baishakhi Banerjee at Thursday, November 16, 2006 7:14 AM

It does all of that.  You just click the button and it generates all the code.  Then tweak the code as the article describes where necessary.

http://www.eggheadcafe.com/articles/adonet_source_code_generator.asp

I'd also download this web services sample.  It shows business layer code that

calls the same methods to auto populate classes and how to auto validate them.

http://www.eggheadcafe.com/tutorials/aspnet/af62d138-fa2f-491e-901f-d36a82f107e8/net-web-services--excep.aspx

 

 

  Optimization with generics
paul mcmanus replied to Robbe Morris at Monday, June 18, 2007 12:07 PM
Hi there,
I liked your methods to update an object by reflection. I think I have improved it by adding generics:

public static T SetProperties<T>(T fromRecord, T toRecord)
{
foreach (PropertyInfo field in typeof (T).GetProperties())
{
field.SetValue(toRecord, field.GetValue(fromRecord, null), null);
}
return toRecord;
}


and of course:

public static T SetProperties<T, U>(PropertyInfo[] fromFields, PropertyInfo[] toFields, U fromRecord, T toRecord)
{
foreach (PropertyInfo fromField in fromFields)
{
foreach (PropertyInfo toField in toFields)
{
if (fromField.Name == toField.Name)
{
toField.SetValue(toRecord, fromField.GetValue(fromRecord, null), null);
break;
}
}

}
return toRecord;
}

Hope you like it.
 

further...
paul mcmanus replied to Robbe Morris at Monday, June 18, 2007 12:13 PM
My second mathod should have read:
public static T SetProperties<T, U>(U fromRecord, T toRecord)
{
foreach (PropertyInfo fromField in typeof(U).GetProperties())
{
if (fromField.Name != "Id")
{
foreach (PropertyInfo toField in typeof(T).GetProperties())
{
if (fromField.Name == toField.Name)
{
toField.SetValue(toRecord, fromField.GetValue(fromRecord, null), null);
break;
}
}
}
}
return toRecord;
}
 

Faster copy properties
Nadeem Douba replied to Robbe Morris at Tuesday, November 06, 2007 2:35 PM

Hi,

I know this doesn't belong here, but thanks to your code I was able to create something similar in vb.net. It should be a bit faster than what you have implemented since there is no need for a nested for loop.

Cheers,

Nadeem

 

Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Text
Imports System.Diagnostics

Module ClassUtils

    Public Sub CopyProperties(ByRef dst As Object, ByRef src As Object)

        Dim srcProperties() As PropertyInfo = src.GetType.GetProperties()
        Dim dstType = dst.GetType

        Try

            If srcProperties Is Nothing Or dstType.GetProperties Is Nothing Then
                Return
            End If

            For Each srcProperty As PropertyInfo In srcProperties
                Dim dstProperty As PropertyInfo = dstType.GetProperty(srcProperty.Name)

                If dstProperty IsNot Nothing Then
                    If dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType) = True Then
                        dstProperty.SetValue(dst, srcProperty.GetValue(src, Nothing), Nothing)
                    End If
                End If
            Next

        Catch ex As Exception

        End Try
    End Sub

End Module

 

You'd better test that first
Robbe Morris replied to Nadeem Douba at Tuesday, November 06, 2007 2:55 PM

You are making calls to .GetProperty() again and again.  My code

sample keeps static copies of this so it only needs to be called

once during the lifetime of the app.

 






  $1000 Contest    [)ia6l0 iii - $228  |  Jonathan VH - $161  |  Huggy Bear - $135  |  F Cali - $95  |  egg egg - $94  |  more Advertise  |  Privacy  |   (c) 2010