// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
//#define ASSERT_CLONE_TYPES
using System.Collections.Generic;
using UnityEngine;
namespace Animancer
{
/// An object that can be cloned.
/// See for example usage.
/// https://kybernetik.com.au/animancer/api/Animancer/ICloneable_1
public interface ICloneable
{
/************************************************************************************************************************/
/// Creates a new object with the same type and values this.
///
///
/// Calling this method directly is not generally recommended.
/// Use if you don't have a `context`
/// or if you do have one.
///
/// Example:
/// class BaseClass : ICloneable
/// {
/// // Explicit implementation so that the recommended methods will be used instead.
/// object ICloneable.Clone(CloneContext context)
/// {
/// var clone = (BaseClass)MemberwiseClone();
/// clone.CopyFrom(this, context);
/// return clone;
/// }
///
/// // Protected method which should only be called by Clone.
/// protected virtual void InitializeClone(CloneContext context)
/// {
/// // Alter any necessary BaseClass fields according to the context.
/// }
/// }
///
/// class DerivedClass : BaseClass
/// {
/// protected override void InitializeClone(CloneContext context)
/// {
/// base.CopyFrom(copyFrom, context);
///
/// var derived = (DerivedClass)copyFrom;
/// // Alter any necessary DerivedClass fields according to the context.
/// }
/// }
///
T Clone(CloneContext context);
/************************************************************************************************************************/
}
/// Extension methods for .
/// https://kybernetik.com.au/animancer/api/Animancer/CloneableExtensions
public static partial class CloneableExtensions
{
/************************************************************************************************************************/
///
/// Calls using a from the
/// and casts the result.
///
///
/// Returns null if the `original` is null.
///
/// Use
/// if you already have a .
///
public static T Clone(this ICloneable original)
{
if (original == null)
return default;
var context = CloneContext.Pool.Instance.Acquire();
var clone = original.Clone(context);
CloneContext.Pool.Instance.Release(context);
return clone;
}
/************************************************************************************************************************/
/// [Assert-Conditional] Asserts that the `clone` has the same type as the `original`.
[System.Diagnostics.Conditional(Strings.Assertions)]
internal static void AssertCloneType(this ICloneable original, object clone)
{
#if UNITY_ASSERTIONS
var cloneType = clone.GetType();
var originalType = original.GetType();
if (cloneType != originalType)
Debug.LogError($"Cloned object type ({cloneType.FullName}" +
$" doesn't match original {originalType.FullName}." +
$"\n• Original: {original}" +
$"\n• Clone: {clone}");
#endif
}
/************************************************************************************************************************/
}
/// A dictionary which maps objects to their copies.
///
/// This class is used to clone complex object trees with potentially interconnected references so that
/// references to original objects can be replaced with references to their equivalent clones.
///
public class CloneContext : Dictionary