Giter Site home page Giter Site logo

deepcloner's Introduction

DeepCloner

Library with extenstion to clone objects for .NET. It can deep or shallow copy objects. In deep cloning all object graph is maintained. Library actively uses code-generation in runtime as result object cloning is blazingly fast. Also, there are some performance tricks to increase cloning speed (see tests below). Objects are copied by its' internal structure, no methods or constructuctors are called for cloning objects. As result, you can copy any object, but we don't recommend to copy objects which are binded to native resources or pointers. It can cause unpredictable results (but object will be cloned).

You don't need to mark objects somehow, like Serializable-attribute, or restrict to specific interface. Absolutely any object can be cloned by this library. And this object doesn't have any ability to determine that he is clone (except with very specific methods).

Also, there is no requirement to specify object type for cloning. Object can be casted to inteface or as an abstract object, you can clone array of ints as abstract Array or IEnumerable, even null can be cloned without any errors.

Installation through Nuget:

	Install-Package DeepCloner

Supported Frameworks

DeepCloner works for .NET 4.0 or higher or for .NET Standard 1.3 (.NET Core). .NET Standard version implements only Safe copying variant (slightly slower than standard, see Benchmarks).

Limitation

Library requires Full Trust permission set or Reflection permission (MemberAccess). It prefers Full Trust, but if code lacks of this variant, library seamlessly switchs to slighlty slower but safer variant.

If your code is on very limited permission set, you can try to use another library, e.g. CloneExtensions. It clones only public properties of objects, so, result can differ, but should work better (it requires only RestrictedMemberAccess permission).

Usage

Deep cloning any object:

  var clone = new { Id = 1, Name = "222" }.DeepClone();

With a reference to same object:

  // public class Tree { public Tree ParentTree;  }
  var t = new Tree();
  t.ParentTree = t;
  var cloned = t.DeepClone();
  Console.WriteLine(cloned.ParentTree == cloned); // True

Or as object:

  var date = DateTime.Now;
  var obj = (object)date;
  obj.DeepClone().GetType(); // DateTime

Shallow cloning (clone only same object, not objects that object relate to)

  var clone = new { Id = 1, Name = "222" }.ShallowClone();

Cloning to existing object (can be useful for copying constructors, creating wrappers or for keeping references to same object)

public class Derived : BaseClass
{
	public Derived(BaseClass parent)
	{
		parent.DeepCloneTo(this); // now this has every field from parent
	}
}

Please, note, that DeepCloneTo and ShallowCloneTo requre that object should be class (it is useless for structures) and derived class must be real descendant of parent class (or same type). In another words, this code will not work:

public class Base {}
public class Derived1 : Base {}
public class Derived2 : Base {}

var b = (Base)new Derived1(); // casting derived to parent
var derived2 = new Derived2();
// will compile, but will throw an exception in runtime, Derived1 is not parent for Derived2
b.DeepCloneTo(derived2); 

Installation

Through nuget:

  Install-Package DeepCloner

Details

You can use deep clone of objects for a lot of situations, e.g.:

  • Emulation of external service or deserialization elimination (e.g. in Unit Testing). When code has received object from external source, code can change it (because object for code is own).
  • ReadOnly object replace. Instead of wrapping your object to readonly object, you can clone object and target code can do anything with it without any restriction.
  • Caching. You can cache data locally and want to ensurce that cached object hadn't been changed by other code

You can use shallow clone as fast, light version of deep clone (if your situation allows that). Main difference between deep and shallow clone in code below:

  // public class A { public B B; }
  // public class B { public int X; }
  var b = new B { X = 1 };
  var a = new A { B = b };
  var deepClone = a.DeepClone();
  deepClone.B.X = 2;
  Console.WriteLine(a.B.X); // 1
  var shallowClone = a.ShallowClone();
  shallowClone.B.X = 2;
  Console.WriteLine(a.B.X); // 2

So, deep cloning is guarantee that all changes of cloned object does not affect original. Shallow clone does not guarantee this. But it faster, because deep clone of object can copy big graph of related objects and related objects of related objects and related related related objects, and... so on...

This library does not call any method of cloning object: constructors, Equals, GetHashCode, propertes - nothing is called. So, it is impossible for cloning object to receive information about cloning, throw an exception or return invalid data. If you need to call some methods after cloning, you can wrap cloning call to another method which will perform required actions.

Extension methods in library are generic, but it is not require to specifify type for cloning. You can cast your objects to System.Object, or to an interface, add fields will be carefully copied to new object.

Performance

Cloning Speed can vary on many factors. This library contains some optimizations, e.g. structs are just copied, arrays also can be copied through Array.Copy if possible. So, real performance will depend on structure of your object.

Tables below, just for information. Simple object with some fields is cloned multiple times. Preparation time (only affect first execution) excluded from tests.

Example of object

var c = new C1 { V1 = 1, O = new object(), V2 = "xxx" };
var c1 = new C1Complex { C1 = c, Guid = Guid.NewGuid(), O = new object(), V1 = 42, V2 = "some test string", Array = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } };

Deep cloning

Method Time per object (ns) Comments
Manual 50 You should manually realize cloning. It requires a lot of work and can cause copy-paste errors, but it is fastest variant
DeepClone / Unsafe 570 This variant is really slower than manual, but clones any object without preparation
DeepClone / Safe 760 Safe variant based on on expressions
CloneExtensions 1800 Implementation of cloning objects on expression trees.
NClone 2890 Not analyzed carefully, but author says that lib has a problem with a cyclic dependencies
Clone.Behave! 41890 Very slow, also has a dependency to fasterflect
GeorgeCloney 6420 Has a lot limitations and prefers to clone through BinaryFormatter
Nuclex.Cloning n/a Crashed with a null reference exception
.Net Object FastDeepCloner 15030 Not analyzed carefully, only for .NET 4.5.1 or higher
DesertOctopus 1700 Not analyzed. Only for .NET 4.5.2 or higher
BinaryFormatter 49100 Another way of deep object cloning through serializing/deserializing object. Instead of Json serializers - it maintains full graph of serializing objects and also do not call any method for cloning object. But due serious overhead, this variant is very slow

Shallow cloning Shallow cloning is usually faster, because we no need to calculate references and clone additional objects.

Method Time per object (ns) Comments
Manual 16 You should manually realize clone, property by property, field by field. Fastest variant
Manual / MemberwiseClone 46 Fast variant to clone: call MemberwiseClone inside your class. Should be done manually, but does not require a lot of work.
ShallowClone / Unsafe 64 Slightly slower than MemberwiseClone due checks for nulls and object types
ShallowClone / Safe 64 Safe variant based on expressions
CloneExtensions 125 Implementation of cloning objects on expression trees.
Nuclex.Cloning 2498 Looks like interesting expression-based implementation with a some caching, but unexpectedly very slow

Performance tricks

We perform a lot of performance tricks to ensure cloning is really fast. Here is some of them:

  • Using a shallow cloning instead of deep cloning if object is safe for this operation
  • Copying an whole object and updating only required fields
  • Special handling for structs (can be copied without any cloning code, if possible)
  • Cloners caching
  • Optimizations for copying simple objects (reduced number of checks to ensure good performance)
  • Special handling of reference count for simple objects, that is faster than default dictionary
  • Constructors analyzing to select best variant of object construction
  • Direct copying of arrays if possible
  • Custom handling of one-dimensional and two-dimensional zero-based arrays (most of arrays in usual code)

License

MIT license

deepcloner's People

Contributors

dependabot[bot] avatar force-net avatar kostasgkoutis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

deepcloner's Issues

CLR exception with exit code 0x80131506

Hi,

at the moment I'm investigating why my tests are unstable, i.e. unexpected aborts. Switched from mstest/vstests to nunit, but issue remained. Decided to run them in a smaller chunks, interesting enough that sometimes 50 tests could pass 4-5 runs and fail on 6th, but sometimes fail on every run or on every other run. Moreover, almost always it was a different test running when "abort" has happened. And by abort I mean (and according to event log) - an execution engine exception happening in the clr runtime:

The process was terminated due to an internal error in the .NET Runtime at IP 00007FF9C27D92E0 (00007FF9C2220000) with exit code 80131506.

Interesting enough another set of tests was always working as expected. Further and still a preliminary investigation indicated that failing tests (some of them) have DeepCloner in use. As the next step I ran nunit-console along with "Debug Diagnostic Tool" and with a single test that used DeepCloner. Then checked the logs, probably the most important findings are:

DetailID = 4
	Count:    1
	Type:     System.BadImageFormatException
	Message:  An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
	Stack:    
		[InlinedCallFrame]
		[InlinedCallFrame]
		DomainNeutralILStubClass.IL_STUB_PInvoke(System.Reflection.RuntimeModule, Int32, IntPtr*, Int32, IntPtr*, Int32)
		System.ModuleHandle.ResolveMethodHandleInternalCore(System.Reflection.RuntimeModule, Int32, IntPtr[], Int32, IntPtr[], Int32)
		System.ModuleHandle.ResolveMethodHandleInternal(System.Reflection.RuntimeModule, Int32, System.RuntimeTypeHandle[], System.RuntimeTypeHandle[])
		System.Reflection.RuntimeModule.ResolveMethod(Int32, System.Type[], System.Type[])
		Force.DeepCloner.Helpers.DeepClonerMsilHelper.IsConstructorDoNothing(System.Type, System.Reflection.ConstructorInfo)
		Force.DeepCloner.Helpers.DeepClonerMsilGenerator.GenerateProcessMethod(System.Reflection.Emit.ILGenerator, System.Type, Boolean)
		Force.DeepCloner.Helpers.DeepClonerMsilGenerator.GenerateClonerInternal(System.Type, Boolean)
		Force.DeepCloner.Helpers.DeepClonerCache.GetOrAddClass[[System.__Canon, mscorlib]](System.Type, System.Func`2<System.Type,System.__Canon>)
		Force.DeepCloner.Helpers.DeepClonerGenerator.CloneClassInternal(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_Dictionary`2_82(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_ValidationFailure_81(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_ValidationFailure[]_80(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_List`1_79(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_RequiredInventoryReservation_5(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_RequiredInventoryReservation[]_4(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_List`1_3(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_RequiredInventoryReservationsCollection_2(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_Inventory_1(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		Force.DeepCloner.Helpers.DeepClonerGenerator.CloneObject[[System.__Canon, mscorlib]](System.__Canon)
		REDACTED+<TEST_IN_QUESTION>d__27.MoveNext()
--- rest is redacted

and the following:

DetailID = 5
	Count:    1
	Type:     System.ArgumentException
	Message:  A BadImageFormatException has been thrown while parsing the signature. This is likely due to lack of a generic context. Ensure genericTypeArguments and genericMethodArguments are provided and contain enough context.
		Type:     System.BadImageFormatException
		Message:  An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
	Stack:    
		[HelperMethodFrame]
		System.Reflection.RuntimeModule.ResolveMethod(Int32, System.Type[], System.Type[])
		[InlinedCallFrame]
		[InlinedCallFrame]
		DomainNeutralILStubClass.IL_STUB_PInvoke(System.Reflection.RuntimeModule, Int32, IntPtr*, Int32, IntPtr*, Int32)
		System.ModuleHandle.ResolveMethodHandleInternalCore(System.Reflection.RuntimeModule, Int32, IntPtr[], Int32, IntPtr[], Int32)
		System.ModuleHandle.ResolveMethodHandleInternal(System.Reflection.RuntimeModule, Int32, System.RuntimeTypeHandle[], System.RuntimeTypeHandle[])
		System.Reflection.RuntimeModule.ResolveMethod(Int32, System.Type[], System.Type[])
		Force.DeepCloner.Helpers.DeepClonerMsilHelper.IsConstructorDoNothing(System.Type, System.Reflection.ConstructorInfo)
		Force.DeepCloner.Helpers.DeepClonerMsilGenerator.GenerateProcessMethod(System.Reflection.Emit.ILGenerator, System.Type, Boolean)
		Force.DeepCloner.Helpers.DeepClonerMsilGenerator.GenerateClonerInternal(System.Type, Boolean)
		Force.DeepCloner.Helpers.DeepClonerCache.GetOrAddClass[[System.__Canon, mscorlib]](System.Type, System.Func`2<System.Type,System.__Canon>)
		Force.DeepCloner.Helpers.DeepClonerGenerator.CloneClassInternal(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_Dictionary`2_82(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_ValidationFailure_81(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_ValidationFailure[]_80(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_List`1_79(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_RequiredInventoryReservation_5(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_RequiredInventoryReservation[]_4(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_List`1_3(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_RequiredInventoryReservationsCollection_2(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		DynamicClass.DeepObjectCloner_Inventory_1(System.Object, Force.DeepCloner.Helpers.DeepCloneState)
		Force.DeepCloner.Helpers.DeepClonerGenerator.CloneObject[[System.__Canon, mscorlib]](System.__Canon)
		REDACTED+<TEST_IN_QUESTION>d__27.MoveNext()
--- rest is redacted

To verify the assumption I replaced DeepCloner with CloneExtensions for that test:

 var inventoryCopy = helper.Inventory.GetClone();//.DeepClone();

ran that particular test several times and it seems to be working as expected now.
As mentioned earlier, these are preliminary findings, yet they indicate(?) that DeepCloner generates a malformed IL that crashes the entire runtime. Next, I will migrate to CloneExtensions and run the entire test suite to confirm the assumption. Let's see...

Thank you!

Better error reporting

DeepClone is able to cause anonymous/untraceable errors in system code. I'm a few hours in now debugging an unpredictable exception. It has no stacktrace that would lead to my own code or to DeepClone.

The error has reported out as:

  • a null reference exception in System.Private.CoreLib
  • a null reference exception in System.Threading.Overlapped.Free
  • an Access Violation exception in unspecified system code

By stepping through my code, I've finally narrowed the issue down to a usage of ActionExecutedContext.Result.DeepClone(). I'm sure it's my fault, I'm probably cloning something I shouldn't be (something that has a reference to itself or a pointer to an object held by some other process or a system object...I've been reading the other issues on this repo so I understand there are some things that shouldn't ever be cloned).

I'm okay with the cloning restrictions and I'll figure out what's going on, but it would have saved me some time if DeepCloner had some error handling built in. try/catch stuff. It wouldn't need to tell me exactly what the problem is, it would just be nice to know that the error is happening in DeepCloner and not somewhere else, since you can see that isn't clear at all from the errors I'm getting.

Is that a possibility for a future version?

EDIT: On further investigation, it's not at all certain that the error is happening during the DeepClone so this might be a red herring. Sorry for the trouble.

Why version 0.X?

This library has a significant amount of downloads and appears to be stable and reliable. Why does it still have major version 0?

This makes the library look rather unprofessional to consumers and actually raised a red flag when I saw it being added in one of our projects. Usually version 0.X are reserved for alpha and beta versions, basically things that are not yet ready to market.

Can the next release bump the version to 1.0.0 if the library is considered stable and production-ready?

InvalidAccessViolation, other exceptions and slow down

I discovered several issues using your library.

Situation 1:
I have a parent form with datagridview. Mouse doubleclick on datagridview row retrieves binded record and open new child form that presents details of this record. In that child form I can click Save button to save updated record in database, after which child form is closed/disposed and parent form updates datagridview. In child form before saving to database I do deepclone using your library:

I removed everything unrelated.

' Property in form
Property Audit As AuditDefinition

' Entrypoint to form
Public Sub New(audit As AuditDefinition)
    InitializeComponent()
    Me.Audit = audit
End Sub

' Somewhere in form I await saving function and if return true I close the form
If Await SaveInDbAsync() then Close()

' Saving in database
Private Async Function SaveInDbAsync() As Task(Of Boolean)
    ' Cloning Audit property in case of saving in database failure (then restore changed child properties)
    cAudit = Audit.DeepClone
   ' Rest of code
End Function

Child form is properly initialized and destroyed with using/end using clause from parent form.

Problem:
Each next time when child form in created (with new record Audit) and deepclone is performed it is slower and slower. Example times:
132 ms (first use of deepclone in application)
15 ms
25 ms
53 ms
163 ms
291 ms
569 ms

What do you think about it?

Situation 2:
Since I switch making clone objects from my implementation to yours my application started to close unexpectedly with many different exceptions, mainly System.AccessViolationException in random System.xxxx.dll libraries.

For example during debugging situation 1 above I made a simple loop making cloning 10 times the same object receiving each time the same time (opposite to situation above where time is longer and longer with each call) but just after exiting the loop application receives AccessViolationException.

Dim cAudit As AuditDefinition

For i = 0 To 9
    Debug.WriteLine(i)
    cAudit = Audit.DeepClone
Next

Result:

0
1
2
3
4
5
6
7
8
Exception thrown: 'System.Runtime.InteropServices.SEHException' in System.Windows.Forms.dll
9
System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception.

And debugger can move to next lines of code but soon application is closed with

Cross-thread operation not valid: Control '{0}' accessed from a thread other than the thread it was created on.
Or
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Exception occurs randomly after 4 to 9 cloning operations.
Moving operation to non-async function doesn't help.

What do you think about it?

Possible Heap Corruption and Garbage Collection Interference...

Howdy...

We spent the last 3 weeks working with MS on an issues without SOAP interface where we have integrated the DeepCloner tool. According to MS Support the DeepCloner is causing HEAP corruption which is then causing the Garbage Collector to throw errors due to Access Violations. This causes the dotNet6 framwork to crash and make the SOAP interface non-responsive.

Has anyone else see this issue? Any more info that the devs can use to fix this issue?

What about clonning ExpressionTrees?

Is there an option, or configuration to evaluate the expression trees before deep copying a property? e.g. LINQ statements as nested property underneath an object.

JSONConvert, when serializing, evaluates the properties, and, I would like to achieve the same thing.

Ability to ignore Events or Properties

Hello, I have quite a few classes that implement INotifyPropertyChanged due to mvvm, cloning these classes seems to lead to a memory error and crash problem.

Is there a way to not clone events or specific properties?

HashSet contains method return false after deep clone

var clonedHashSet = new HashSet<object>{new object()}.DeepClone();
var clonedObject = clonedHashSet.First();
clonedHashSet.Contains(clonedObject); // false

clonedHashSet.Clear();
clonedHashSet.Add(clonedObject);
clonedHashSet.Contains(clonedObject); // true

Clone Action<T> delegate is incorrect

Clone Action delegate is incorrect,
DeepClone appears to Clone the Action.Target object,
which it shouldn't be, because the delegate itself should just be a pointer to the target method.

Memory corruption and program crash when cloning TaskCancelledException

I've run into a problem where using DeepClone to clone a TaskCancelledException is corrupting memory and leading to a crash of my application. The exception is being thrown by Task.Delay(int, CancellationToken) when the token is cancelled. I believe the source of the problem is when it goes to clone the TaskCancelledException.Task property. This particular task includes a System.Threading.TimerQueueTimer object that I believe has native resources stored in it.

Is it possible to have some way to skip or configure how certain properties are cloned? For example, you could have attributes such as [IgnoreClone] or [ShallowClone] to specify that a property should default to being shallow cloned instead of attempting to deep clone. This way I could mark my Exception property as [ShallowClone] and it wouldn't even attempt to clone something that might have unmanaged resources in it.

NHibernate Invalid cast exception

Hi there, first of all, thanks for the great library :)

We have our own implementation of deep cloning and wanted to test out this library to compare performance, but most of our tests fail when nhibernate tries to save object with error:

System.InvalidCastException : Unable to cast object of type 'NHibernate.Engine.Query.QueryExpressionPlan' to type 'System.Transactions.SafeIUnknown'.
at System.Transactions.Transaction.JitSafeGetContextTransaction(ContextData contextData)
at System.Transactions.Transaction.FastGetTransaction(TransactionScope currentScope, ContextData contextData, Transaction& contextTransaction)
at System.Transactions.Transaction.get_Current()
at NHibernate.Transaction.AdoNetWithSystemTransactionFactory.EnlistInSystemTransactionIfNeeded(ISessionImplementor session)
at NHibernate.Impl.AbstractSessionImpl.CheckAndUpdateSessionStatus()
at NHibernate.Impl.SessionImpl..ctor(SessionFactoryImpl factory, ISessionCreationOptions options)
at NHibernate.Impl.SessionFactoryImpl.SessionBuilderImpl`1.OpenSession()

Our assumption is that DeepCloner tries to clone nhibernate proxy that is created on runtime, and that issues happen there.
So I wanted to ask is this maybe a known issue, and is there a way to handle it?

This exception is occasionally thrown in clones “must be writeable (Parameter 'left')”

Expression must be writeable (Parameter 'left')
System.Linq.Expressions
at System.Linq.Expressions.Expression.RequiresCanWrite(Expression expression, String paramName)
at System.Linq.Expressions.Expression.Assign(Expression left, Expression right)
at Force.DeepCloner.Helpers.DeepClonerExprGenerator.GenerateProcessMethod(Type type, Boolean unboxStruct)
at Force.DeepCloner.Helpers.DeepClonerExprGenerator.GenerateClonerInternal(Type realType, Boolean asObject)
at Force.DeepCloner.Helpers.DeepClonerGenerator.GenerateCloner(Type t, Boolean asObject)
at Force.DeepCloner.Helpers.DeepClonerGenerator.<>c.b__1_0(Type t)
at Force.DeepCloner.Helpers.DeepClonerCache.GetOrAddClass[T](Type type, Func`2 adder)
at Force.DeepCloner.Helpers.DeepClonerGenerator.CloneClassRoot(Object obj)
at Force.DeepCloner.Helpers.DeepClonerGenerator.CloneObject[T](T obj)
at Force.DeepCloner.DeepClonerExtensions.DeepClone[T](T obj)

DeepCloneTo() creates new objects for target properties

Example:

class C1 { public int Prop {get; set;} }
class C2 { public C1 Prop {get; set;} }

var target = new C2 { Prop = new C1() }
var source = new C2 { Prop = new C1() }

var targetC1Before = target.Prop;
source.DeepCloneTo(target);
var targetC1After = target.Prop;

Debug.Assert(object.ReferenceEquals(targetC1Before, targetC1After); // FAIL

program crashed when deepclone multidimensional zero-length array

Array.CreateInstance(typeof(int), new[] {0, 0}).DeepClone(); // IndexOutOfRangeException
Array.CreateInstance(typeof(int), new[] {1, 0}).DeepClone(); // IndexOutOfRangeException
Array.CreateInstance(typeof(int), new[] {0, 1}).DeepClone(); // IndexOutOfRangeException
Array.CreateInstance(typeof(int), new[] {1, 1}).DeepClone(); // Works
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.Array.InternalGetReference(Void* elemRef, Int32 rank, Int32* pIndices)
   at System.Array.GetValue(Int32[] indices)
   at Force.DeepCloner.Helpers.DeepClonerGenerator.CloneAbstractArrayInternal(Array obj, DeepCloneState state)
   at Force.DeepCloner.Helpers.DeepClonerGenerator.CloneClassRoot(Object obj)
   at Force.DeepCloner.Helpers.DeepClonerGenerator.CloneObject[T](T obj)
   at Force.DeepCloner.DeepClonerExtensions.DeepClone[T](T obj)
   at ConsoleApp1.Program.Main(String[] args) in C:\Users\DemoJameon\RiderProjects\ConsoleApp1\ConsoleApp1\Program.cs:line 10

Mix of deep and shallow copy

Is there a provision in the library to make shallow copy of few expensive objects, while we deep copy the remaining object graph. I doubt on reviewing the APIs, I do plan to make code change, by supplying an explicit Dictionary with expensive objects added to it and in case object is there in the dictionary then create a Shallow version else a regular code which creates a deep version. Please suggest if my understanding is correct and may be some guidance to make code modification.

To provide more clarity:

My object is of following type, where both ODPair and ServiceProduct are the custom types. ServiceProduct is an expensive object, for which I want to retain the Shallow copy, while rest of the object is deep copied

Dictionary<ODPair,Dictionary<string,List<ServiceProduct>>>

Replace NuGet references with known security risks

The referenced NuGet package NETStandard.Library 1.6.1 references 2 NuGet packages that have known security risks.

  • System.Net.Http 4.3.0
  • System.Text.RegularExpressions 4.3.0

This could be solved for example by update NETStandard.Library to 2.0.3

Add DBNull.Value to Force.DeepCloner.Helpers.DeepClonerSafeTypes.KnownTypes

Preface

DBNull.Value represents ADO.NET NULL value and defined as:

public static readonly DBNull Value = new DBNull();

Problem

Code:

if(dataRow["col1"] != DBNull.Value && dataRow["col2"] == DBNull.Value) // ...

Let's assume both columns contain DBNull.Value. When dataRow is cloned by DeepCloner copy of DBNull.Value is created for both columns. After cloning first comparison is still true, second one changed result to false from true cause clone of DBNull is not DBNull.Value.

.NET FW has a lot of DBNull.Value comparisons itself which leads to a very strange effects, e.g. changing DBNull string in DataColumn from null to empty string.

Fix

Add DBNull to Force.DeepCloner.Helpers.DeepClonerSafeTypes.KnownTypes

Workaround

// Excluding DBNull from list of cloned types. DeepCloner creates DBNull.Value copy which is static readonly instance of DBNull itself which breaks DBNull.Value reference equality.
var assembly = Assembly.GetAssembly(typeof(DeepClonerExtensions));
var deepClonerSafeTypes = assembly.GetType("Force.DeepCloner.Helpers.DeepClonerSafeTypes");
var knownTypesField = deepClonerSafeTypes.GetField("KnownTypes", BindingFlags.Static | BindingFlags.NonPublic);
var knownTypes = (ConcurrentDictionary<Type, bool>)knownTypesField.GetValue(null);
knownTypes.TryAdd(typeof(DBNull), true);

Netstandard upgrade

DeepCloner is currently targeted at Netstandard 1.3, which, along with this version of netstandard, downloads the system.text.regularexpressions and system.net.http packages that have vulnerabilities.

Is it possible to target DeepCloner at Netstandard 2.0 to eliminate these vulnerabilities?

Can't clone dictionary

Here is a piece of code to recreate the bug, I hope.

using System.Collections.Generic;
using System.Linq;
using Force.DeepCloner;

namespace DeepClonerBug
{
internal class Program
{
static void Main(string[] args)
{
var org = new Dictionary<Location, Location[]>
{
{ new Location { Name = "A" }, new[] { new Location { Name = "1" } } }
};

        org[org.Keys.First()].ToString();

        var clone = org.DeepClone();

        // throws System.Collections.Generic.KeyNotFoundException: 'The given key was not present in the dictionary.'
        clone[clone.Keys.First()].ToString();
    }
}

public class Location
{
    public string Name { get; set; }
}

}

How to only clone the common properties?

abstract class Common { 
    public int P { get; set; }
}

class ChildA : Common {  
    public int Q { get; set; }
}

class ChildB : Common {
     public int W { get; set; }
}

Common a = new ChildA();
Common b = new ChildB();

a.ShallowCloneTo(b); // Only do b.P = a.P;

Can we have a attribute to ignore some field/property?

I have a class which implemented INotifyPropertyChanged interface.

How can I use deepClone without some property( PropertyChanged event )?

I have tried [field: NonSerialized] and [JsonIgnore], but it still clone the PropertyChanged event.

Support for cloning child to parent type

public class Parent{};
public class Child : Parent {};
var parent = new Parent();
var child = new Child();
child.DeepCloneTo(parent); // fails to compile

Expected behavior: copy child properties which are present in/inherited from the parent to the parent instance and ignore child's own properties.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.