Let's start from the classic "Hello, wrold!!!" application :)

public class A
{
	public string message;
}
public class B
{
	public string message = "Hello, world!!!";
}

What if we have an instance of class B and we are going to convert it to an similar instance of class A and we don't want to copy fields manually? Solution is rather simple:

B b = new B();
A a = ObjectMapperManager.DefaultInstance.GetMapper<B, A>().Map(b);

The main class of the library is ObjectMapperManager. This class performs mappers building (run-time IL code emitting) and contains mappers cache.

/// <summary>
/// Class for maintaining and generating Mappers.
/// </summary>
public class ObjectMapperManager
{
    public static ObjectMapperManager DefaultInstance
    public ObjectMapperManager();
    
    /// <summary>
    /// Returns a Mapper instance for specified types.
    /// </summary>
    /// <typeparam name="TFrom">Type of source object</typeparam>
    /// <typeparam name="TTo">Type of destination object</typeparam>
    /// <returns></returns>
    public ObjectsMapper<TFrom, TTo> GetMapper<TFrom, TTo>();

    /// <summary>
    /// Returns a Mapper instance for specified types.
    /// </summary>
    /// <typeparam name="TFrom">Type of source object</typeparam>
    /// <typeparam name="TTo">Type of destination object</typeparam>
    /// <param name="ShallowCopy">True, if reference members of same type should be copied by
    /// reference (shallow copy, without creating new instance for destination object)</param>
    /// <returns></returns>
    public ObjectsMapper<TFrom, TTo> GetMapper<TFrom, TTo>(bool ShallowCopy);

    /// <summary>
    /// Returns a Mapper instance for specified types.
    /// </summary>
    /// <typeparam name="TFrom">Type of source object</typeparam>
    /// <typeparam name="TTo">Type of destination object</typeparam>
    /// <param name="ShallowCopy">True, if reference members of same type should be copied by
    /// reference (shallow copy, without creating new instance for destination object)</param>
    /// <param name="mappingConfigurator">Delegate which configures mapping.</param>
    /// <param name="mapperName">Name of mapper. For two mappers with identical type parameters
    /// but different names will be generated two different classes.</param>
    /// <returns></returns>
    public ObjectsMapper<TFrom, TTo> GetMapper<TFrom, TTo>(
        bool ShallowCopy,
        MappingConfigurator mappingConfigurator,
        string mapperName);

    /// <summary>
    /// Returns a mapper implementation instance for specified types.
    /// </summary>
    /// <param name="from">Type of source object</param>
    /// <param name="to">Type of destination object</param>
    /// <param name="ShallowCopy">True, if reference members of same type should
    /// be copied by reference (shallow copy, without creating new instance for destination object)</param>
    /// <param name="mappingConfigurator">Delegate which configures mapping.</param>
    /// <returns></returns>
    public ObjectsMapperBaseImpl GetMapperImpl(
        Type from,
        Type to,
        bool ShallowCopy,
        MappingConfigurator mappingConfigurator);

    /// <summary>
    /// Returns a mapper implementation instance for specified types.
    /// </summary>
    /// <param name="from">Type of source object</param>
    /// <param name="to">Type of destination object</param>
    /// <param name="ShallowCopy">True, if reference members of same type should
    /// be copied by reference (shallow copy, without creating new instance for destination object)</param>
    /// <param name="mappingConfigurator">Delegate which configures mapping.</param>
    /// <returns></returns>
    public ObjectsMapperBaseImpl GetMapperImpl(
        Type from,
        Type to,
        bool ShallowCopy,
        MappingConfigurator mappingConfigurator,
        string mapperName);
}

In general you don't have to create instance of ObjectMapperManager if you don't need separate mappers cache and you can just use thread-save ObjectMapperManager.DefaultInstance member.

Using class ObjectMapperManager you can create instance of type ObjectsMapper which is responsible for mapping objects of concrete types.

public class ObjectsMapper<TFrom, TTo>
{
    public TTo Map(TFrom from, TTo to);
    public TTo Map(TFrom from);
}

The ordinary scenario of using the Emit Objects Library contains 2 steps:

Step 1: Create an ObjectsMapper using class ObjectMapperManager:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<B, A>();

Step 2: Map objects using mapper created in step 1:
A a = mapper.Map(new B());

For example we have two different classes "A" and "B" with similar structure.

public class A
{
	public enum En
	{
		En1,
		En2,
		En3
	}
	public class AInt
	{
		internal int intern = 13;
		public string str = "AInt";

		public AInt()
		{
			intern = 13;
		}
	}

	private string m_str1 = "A::str1";

	public string str1
	{
		get
		{
			return m_str1;
		}
		set
		{
			m_str1 = value;
		}
	}

	public string str2 = "A::str2";
	public AInt obj;
	public En en = En.En3;

	int[] m_arr;

	public int[] arr
	{
		set
		{
			m_arr = value;
		}
		get
		{
			return m_arr;
		}
	}

	public AInt[] objArr;

	public string str3 = "A::str3";

	public A()
	{
		Console.WriteLine("A::A()");
	}
}

public class B
{
	public enum En
	{
		En1,
		En2,
		En3
	}
	public class BInt
	{
		public string str = "BInt";
	}

	public string str1 = "B::str1";
	public string str2
	{
		get
		{
			return "B::str2";
		}

	}
	public BInt obj = new BInt();
	public En en = En.En2;

	public BInt[] objArr;

	public int[] arr
	{
		get
		{
			return new int[] { 1, 5, 9 };
		}
	}

	public object str3 = null;


	public B()
	{
		Console.WriteLine("B::B()");

		objArr = new BInt[2];
		objArr[0] = new BInt();
		objArr[0].str = "b objArr 1";
		objArr[1] = new BInt();
		objArr[1].str = "b objArr 2";
	}
}

Mapping from class "B" to class "A" can be done as below:

A a = new A();
B b = new B();
ObjectMapperManager.DefaultInstance.GetMapper<B, A>().Map(b, a);

Assert.AreEqual(a.en, A.En.En2);
Assert.AreEqual(a.str1, b.str1);
Assert.AreEqual(a.str2, b.str2);
Assert.AreEqual(a.obj.str, b.obj.str);
Assert.AreEqual(a.obj.intern, 13);
Assert.AreEqual(a.arr.Length, b.arr.Length);
Assert.AreEqual(a.arr[0], b.arr[0]);
Assert.AreEqual(a.arr[1], b.arr[1]);
Assert.AreEqual(a.arr[2], b.arr[2]);
Assert.AreEqual(a.objArr.Length, b.objArr.Length);
Assert.AreEqual(a.objArr[0].str, b.objArr[0].str);
Assert.AreEqual(a.objArr[1].str, b.objArr[1].str);
Assert.IsNull(a.str3);

Last edited Jan 6, 2010 at 10:45 PM by romankovs, version 6

Comments

diamondiamon Mar 20, 2013 at 2:08 PM 
woo,it is a very usefully tool ,I like it

GerhardKreuzer Sep 2, 2010 at 12:01 PM 
What's about lazy loading? If the source object cames out of a good ORM, like OpenAccess by Telerik, than it supports lazy loading for properties returning lists. Now I map such an opject to another type, also including such a list, and I don't use the content of the list in my special use case, is the list filled up or not?
I still need the content of such a list from time to time, but seldom, so lazy loading is a big advantage and loosing that feature would be a huge drawback.

Thanks for a nice clearification!!