Repository: FlaxEngine/FlaxEngine
File: Source/Editor/CustomEditors/Editors/CollectionEditor.cs (C#)

Code before:
        /// </summary>
        /// <param name="srcIndex">Index of the source item.</param>
        /// <param name="dstIndex">Index of the destination item to swap with.</param>
        private void Move(int srcIndex, int dstIndex)
        {
            if (IsSetBlocked)
                return;

            var cloned = CloneValues();
            var tmp = cloned[dstIndex];
            cloned[dstIndex] = cloned[srcIndex];
            cloned[srcIndex] = tmp;
            SetValue(cloned);
        }
        
        /// <summary>
        /// Duplicates the list item.
        /// </summary>
        /// <param name="index">The index to duplicate.</param>
        public void Duplicate(int index)
        {
            if (IsSetBlocked)
                return;

            var count = Count;
            Resize(count + 1);
            RefreshInternal(); // Force update values.
            Shift(count, index + 1);
            RefreshInternal(); // Force update values.
            var cloned = CloneValues();
            cloned[index + 1] = Utilities.Utils.CloneValue(cloned[index]);
            SetValue(cloned);
        }

        /// <summary>
        /// Shifts the specified item at the given index and moves it through the list to the other item. It supports undo.
        /// </summary>
        /// <param name="srcIndex">Index of the source item.</param>
        /// <param name="dstIndex">Index of the destination to move to.</param>
        private void Shift(int srcIndex, int dstIndex)
        {
            if (IsSetBlocked)
                return;

            var cloned = CloneValues();
            if (dstIndex > srcIndex)
            {
                for (int i = srcIndex; i < dstIndex; i++)
                {
                    var tmp = cloned[i + 1];
                    cloned[i + 1] = cloned[i];

Review comment (refactor):
Calling `Resize` adds another undo value via `SetValue` being callled under the hood. Can we do this action in more atomic way without those `RefreshInternal` hacks? (`Allocate` -> duplicate items -> `SetValue``)

Code after:
        /// </summary>
        /// <param name="srcIndex">Index of the source item.</param>
        /// <param name="dstIndex">Index of the destination item to swap with.</param>
        private void Move(int srcIndex, int dstIndex)
        {
            if (IsSetBlocked)
                return;

            var cloned = CloneValues();
            var tmp = cloned[dstIndex];
            cloned[dstIndex] = cloned[srcIndex];
            cloned[srcIndex] = tmp;
            SetValue(cloned);
        }
        
        /// <summary>
        /// Duplicates the list item.
        /// </summary>
        /// <param name="index">The index to duplicate.</param>
        public void Duplicate(int index)
        {
            if (IsSetBlocked)
                return;

            var count = Count;
            var newValues = Allocate(count + 1);
            var oldValues = (IList)Values[0];
   
            for (int i = 0; i <= index; i++)
            {
                newValues[i] = oldValues[i];
            }

            newValues[index + 1] = Utilities.Utils.CloneValue(oldValues[index]);

            for (int i = index + 1; i < count; i++)
            {
                newValues[i + 1] = oldValues[i];
            }

            SetValue(newValues);
        }

        /// <summary>
        /// Shifts the specified item at the given index and moves it through the list to the other item. It supports undo.
        /// </summary>
        /// <param name="srcIndex">Index of the source item.</param>
        /// <param name="dstIndex">Index of the destination to move to.</param>
        private void Shift(int srcIndex, int dstIndex)
        {
            if (IsSetBlocked)