Skip to content

MarcinJuraszek/CloneExtensions

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

* Add ArrayPrimitiveTypeExpressionFactory.
When cloning Primitives all that needs to be done is copy the source
array to the target array.  Instead of calling the
PrimitiveTypeExpressionFactory call for every element you can use the
static Array.Copy method to assist.

Depending upon the primitive type, the performance gain is different.
In the tests I have, which are coded against 4.6.1, will have a
performance gain of any where from about 98% to almost 225%.  Byte
Array, for whatever reason, has a performance gain of about 6800% ... I
have no idea why byte arrays are so much different.

Int Arrays go from about 2,386,634 ops per sec to 4,727,659 (98% increase)
String arrays go from about 1,926,782 ops per sec to 5,730,086 (197% increase)
TimeSpan arrays go from about 1,988,071 ops per sec to 4,650,697 (133% increase)
DateTime arrays go from about 1,930,501 ops per sec to 4,210,105 (118% increase)
Delegate arrays go from about 1,659,751 ops per sec to 5,404,864 (225% increase)
Byte arrays go from about 58,083 ops per sec to 4,053,648 (6879% increase)

I would like supply a unit test to easily display the difference in
performance in the master project, but given the interface, it is not
exactly straight forward.  I generated the numbers above from running
the changes suggested from a local release build in my current test
setup.

* Small fix.

* Add support for polymorphism.

In order to support polymorphism, we have to find the runtime type and run the appropriate CloneDelegate against the source object.  I added the CloneIdDelegateCache to assist with this process.  I needed to make a couple changes to the ComplexTypeExpression factory to add polymorphism support.  In order to maintain backwards compatibility, if an initializer was supplied by the user, that should be used before the call to the new code.  I added a new test class that asserts several scenarios, including the scenario outlined in issue #7.  Also in the new unit test, I added some SpeedComparions tests  (which I commented out for integration purposes) to help give an idea of what type of performance hit this change will cause.

Let me know what you think.

* Revert "Add support for polymorphism."

This reverts commit d2035d9.

* Supply a Expression factory for a List of primitives.

Similar to the Array of primitives, there is no need to call the clone method for primitivies.  Since List is so common, providing a custom Expression factory should be quite useful.  According to my testing, here are the performance differences:

List of Strings:   22,815 ops per second ->1,111,000 (about 4700% increase)
List of Bytes:  46,153 ops per second ->1,199,880 (about 2500% increase)
List of Ints: 44,863 ops per second -> 990,000 (about 2100% increase)
List of Delegates: 1,119,194 ops per second ->5,221,409 (about 365% increase)
List of DateTime: 1,274,697 ops per second -> 4,629,166 (about 263% increase)
List of TimeSpan: 1,251,564 ops per second -> 5,221,409 (about 317% increase)

One thing I had to do in order to address an assertion made by the unit tests was to not clone the element is the Flag.CollectionItems was missing.  Which I found surpising, should there be such an assertion of Arrays of items as well?  Currently the Array expression factorys do not check flags at all.

* There is no need to clone any primitive types, they can be used directly.  This change set removes all cloning calls for primitives.  Here is a incomplete summary of the performance changes (all figures are in operations per second).

Clone an Array of classes: 18,761 ->26,702 (about 42% increase)
Clone an Array of Structs: 27,461 ->40,526 (about 47% increase)
Clone a simple class (all primitive properties): 1,973,684 -> 2,918,287 (about 47% increase)
Clone a complex class: 1,776,198 -> 2,056,202 (about 15% increase)
Clone a Class:List<Class>: 0.999 -> 1.176 (about 17% increase)
Clone a Class:List<int>: 1.608 -> 3.115 (about 93% increase)
Clone a KeyValue<int,int>: 9,569,377 -> 14,903,129 (about 55% increase)
Clone a Tuple<int,int>: 3,992,015 -> 10,033,444 (about 151% increase)
Clone a Nullable<int>: 19,607,843 -> 33,025,099 (about a 68% increase)

* I am not sure if this is considered a bug or not, but Tuples are classes.  I added Check for nulls.  Add them to the cloned object dictionary and reuse them if found.  Added unit tests to verify this functionality.

* Leverage backing fields for automatic properties.

* Revert "Leverage backing fields for automatic properties."

This reverts commit af1740c.

* Potential fix for issue #25

* Add Unit tests for Abstract and Virtual properties.

* Add a "new" Field to unit test base class and sub class.
4ce9574

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
src
 
 
 
 
 
 
 
 
 
 

CloneExtensions

Clone extension method library. Performs fast, deep clone using simple assignment operations generated by Expression Tree runtime code compilation.

Build status

Cloning objects does not sound like a really difficult task. However, making it right is not as easy as it seems to be. There are couple popular methods, including ICloneable interface implementation, copying constructors, Serialization and Reflection. All of them have some pros and cons, but non of them is perfect.

CloneExtensions uses both Reflection and Expression Trees to generate and compile all necessary code at runtime when you use GetCode for the first time. Starting from second time GetClone is as fast as if you'd written you all by yourself.

It's compiled as Portable Class Library and can be used with .NET Framework 4 and higher, Silverlight 5, Windows Phone 8, Windows Store app (Windows 8) and higher.

Why should I use CloneExtensions?

Are you sick of writing copy constructors or Clone methods with code like that:

public Foo Clone()
{
    var newInstance = new Foo();

    newInstance.Property1 = this.Property1;
    newInstance.Property2 = this.Property2.Clone();
    // ...
    newInstance.Property12 = this.Property12;

    return newInstance;
}

Do you need something faster than solutions based on reflection or serialization?

If the answer is yes, CloneExtensions is right for you! Wouldn't it be easier to write just:

var newInstance = CloneExtensions.CloneFactory.GetClone(source);

or even simpler, using Extension Method:

var newInstance = source.GetClone();

and get all these assignments out of the box, without implementing Clone method by yourself?

How does it work?

Using the power of Expression Trees, introduced in .NET 3.5, you can generate code, compile it into a delegate and run without performance loss. That's exactly how CloneExtensions works. When GetClone method is used for the first time for given parameter type, proper clone expression tree is generated, compiled and remembered. It's done within class static constructor, so runtime takes care to only do that once. After that, whenever you call the method again, compiled delegate is invoke and you get performance as good as if you'd written the clone method code by yourself.

What can be cloned?

  • Primitive (int, uint, byte, double, char, etc.), known immutable types (DateTime, TimeSpan, String) and delegates (including Action<T>, Func<T1, TResult>, etc)
  • Nullable<T>
  • T[] arrays
  • Custom classes and structs, including generic classes and structs.

Following class/struct members are cloned internally##

  • Values of public, not readonly fields
  • Values of public properties with both get and set accessors
  • Collection items for types implementing ICollection<T>

Execution customization

You can affect compiled delegate execution providing parameters:

  • CloningFlags bit flag enum, to specify which members should be copied (fields, properties, collection items, etc.)
  • Initializers dictionary, to specify custom initialization code which will be used instead of parameterless constructor call

Sample usage

Simple cloning

var source = new GenericClass<int> { _field = 10, Property = 10 };
var newInstance = CloneExtensions.CloneFactory.GetClone(source);

Simple cloning, using Extension Method syntax

var source = new GenericClass<int> { _field = 10, Property = 10 };
var newInstance = source.GetClone();

Cloning with initializer specified

var source = (AbstractClass)new DerivedClass() { AbstractProperty = 10 };
var initializers = new Dictionary<Type, Func<object, object>>() {
    { typeof(AbstractClass), (s) => new DerivedClass() }
};
var clone = source.GetClone(initializers);

Limitations

Of course, nothing is perfect. There are couple limitations you have to be aware of, when it comes to GetClone of your custom classes or structs:

  • Ony public fields without readonly specified can be cloned
  • Only public property with both get and set accessors can be cloned
  • Parameterless constructor has to be available or custom initializer has to be provided to create new instance of cloned typed

Performance

When it comes to performance it's always easier to compare solutions with each other then describe one of them separately.

Is CloneExtensions the fastest way to clone objects? No, it's not. If you really care about every tick you should write all Clone methods by yourself. That's the fastest way.

Is CloneExtensions faster than other known and popular solutions? It depends on how many instances of the same type you clone. That's because just before first GetClone is performed for the first time Expression Tree has to be generated and cloning method has to be compiled. And to do that CloneExtensions uses Reflection. However, starting from the second time method is used with the same T, it has the same performance as if you'd write the logic by yourself. That's why CloneExtensions is definitely faster then reflection-based solutions and is faster then serialization-based solutions when you clone a lot of instances.

Internals

Read about thrown exceptions, generated expressions and more in Documentation.

About

Clone extension method library. Performs fast, deep or shallow clone using simple assignment operations generated by Expression Tree runtime code compilation.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages