Skip to content

Instantly share code, notes, and snippets.

@tqk2811
Last active April 5, 2024 15:53
Show Gist options
  • Save tqk2811/b3fc2d1fa217de07dcf0030d35042180 to your computer and use it in GitHub Desktop.
Save tqk2811/b3fc2d1fa217de07dcf0030d35042180 to your computer and use it in GitHub Desktop.
DeepCopy and keep your pointer of class not change. Note: for IEnumerable<T> only support Array, IList, IDictionary and can't keep pointer of items.
public static class DeepCopyExtension
{
public static T CloneByJson<T>(this T obj, JsonSerializerSettings? jsonSerializerSettings = null)
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj, jsonSerializerSettings), jsonSerializerSettings)!;
}
public static object? CloneByJson(this object obj, Type type, JsonSerializerSettings? jsonSerializerSettings = null)
{
return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(obj, jsonSerializerSettings), type, jsonSerializerSettings)!;
}
public static void DeepCopyTo<T>(this T current, T? target, JsonSerializerSettings? jsonSerializerSettings = null) where T : class
{
if (current is null) throw new ArgumentNullException(nameof(current));
if (target is null) throw new ArgumentNullException(nameof(target));
current.DeepCopyTo(target, current.GetType(), jsonSerializerSettings);
}
public static void DeepCopyTo(this object current, object target, Type type, JsonSerializerSettings? jsonSerializerSettings = null)
{
if (current is null) throw new ArgumentNullException(nameof(current));
if (target is null) throw new ArgumentNullException(nameof(target));
if (type is null) throw new ArgumentNullException(nameof(type));
if (type.IsValueType || type.IsArray)
throw new InvalidOperationException($"Type '{type.FullName}' can't be enum, struct, array");
//check type first
void CloneIList(IList current, IList target, JsonSerializerSettings? jsonSerializerSettings = null)
{
target.Clear();
foreach (var item in current)
{
target.Add(item.CloneByJson(item.GetType(), jsonSerializerSettings));
}
}
void CloneIDictionary(IDictionary current, IDictionary target, JsonSerializerSettings? jsonSerializerSettings = null)
{
target.Clear();
foreach (DictionaryEntry pair in (IDictionary)current.CloneByJson(current.GetType(), jsonSerializerSettings)!)
{
target[pair.Key] = pair.Value;
}
}
if (type.IsAssignableTo(typeof(IEnumerable)))
{
if (current is IList list_current && target is IList list_target)
{
CloneIList(list_current, list_target, jsonSerializerSettings);
}
else if (current is IDictionary dict_current && target is IDictionary dict_target)
{
CloneIDictionary(dict_current, dict_target, jsonSerializerSettings);
}
else throw new NotSupportedException(type.FullName);
}
else
{
foreach (PropertyInfo propertyInfo in type.GetProperties().Where(x => x.CanWrite && x.CanRead))
{
object? f_current = propertyInfo.GetValue(current);
if (f_current is null)
{
propertyInfo.SetValue(target, null);
}
else
{
if (propertyInfo.PropertyType.IsValueType ||
propertyInfo.PropertyType.IsEnum ||
propertyInfo.PropertyType.Equals(typeof(string))
)//struct/enum or string
{
propertyInfo.SetValue(target, propertyInfo.GetValue(current));
}
else if (propertyInfo.PropertyType.IsArray)//array
{
propertyInfo.SetValue(target, f_current.CloneByJson(f_current.GetType(), jsonSerializerSettings));
}
else if (propertyInfo.PropertyType.IsAssignableTo(typeof(IEnumerable)))
{
object? f_target = propertyInfo.GetValue(target);
if (f_target is null)
{
//just clone
propertyInfo.SetValue(target, f_current.CloneByJson(f_current.GetType(), jsonSerializerSettings));
}
else
{
if (f_target is IList list_target)//just dont break pointer of List<T>
{
CloneIList((IList)f_current, list_target, jsonSerializerSettings);
}
else if (f_target is IDictionary dict_target)//just dont break pointer of Dictionary<TKey,TValue>
{
CloneIDictionary((IDictionary)f_current, dict_target, jsonSerializerSettings);
}
else throw new NotSupportedException(propertyInfo.PropertyType.FullName);
}
}
else//class
{
var f_target = propertyInfo.GetValue(target);
if (f_target is null)
{
propertyInfo.SetValue(target, f_current.CloneByJson(f_current.GetType(), jsonSerializerSettings));
}
else
{
f_current.DeepCopyTo(f_target, f_current.GetType());
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment