refactor: remove Copy/Move component functionality

This may be returned at a later point.
This commit is contained in:
Oliver Booth 2023-04-10 13:26:38 +01:00
parent 844f697754
commit 77ab429f72
No known key found for this signature in database
GPG Key ID: 20BEB9DC87961025
4 changed files with 1 additions and 689 deletions

View File

@ -1,6 +1,5 @@
#nullable enable #nullable enable
using System;
using System.Collections; using System.Collections;
using NUnit.Framework; using NUnit.Framework;
using UnityEngine; using UnityEngine;
@ -11,67 +10,6 @@ namespace X10D.Unity.Tests
{ {
public class ComponentTests public class ComponentTests
{ {
[UnityTest]
public IEnumerator CopyTo_ShouldCopyComponent_GivenComponent()
{
var source = new GameObject();
var sourceComponent = source.AddComponent<Rigidbody>();
sourceComponent.mass = 10.0f;
sourceComponent.useGravity = false;
var target = new GameObject();
sourceComponent.CopyTo(target);
Assert.That(target.TryGetComponent(out Rigidbody targetComponent));
Assert.That(targetComponent.mass, Is.EqualTo(10.0f));
Assert.That(targetComponent.useGravity, Is.False);
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator CopyTo_ShouldThrowArgumentNullException_GivenNullComponent()
{
var target = new GameObject();
Rigidbody rigidbody = null!;
Assert.Throws<ArgumentNullException>(() => rigidbody.CopyTo(target));
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator CopyTo_ShouldThrowArgumentNullException_GivenNullTarget()
{
var source = new GameObject();
var rigidbody = source.AddComponent<Rigidbody>();
GameObject target = null!;
Assert.Throws<ArgumentNullException>(() => rigidbody.CopyTo(target));
Object.Destroy(source);
yield break;
}
[UnityTest]
public IEnumerator CopyTo_ShouldThrowInvalidOperationException_GivenDuplicate()
{
var source = new GameObject();
var rigidbody = source.AddComponent<Rigidbody>();
var target = new GameObject();
target.AddComponent<Rigidbody>();
Assert.Throws<InvalidOperationException>(() => rigidbody.CopyTo(target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest] [UnityTest]
public IEnumerator GetComponentsInChildrenOnly_ShouldIgnoreParent() public IEnumerator GetComponentsInChildrenOnly_ShouldIgnoreParent()
{ {
@ -90,70 +28,5 @@ namespace X10D.Unity.Tests
Object.Destroy(parent); Object.Destroy(parent);
Object.Destroy(child); Object.Destroy(child);
} }
[UnityTest]
public IEnumerator MoveTo_ShouldCopyComponent_GivenComponent()
{
var source = new GameObject();
var sourceComponent = source.AddComponent<Rigidbody>();
sourceComponent.mass = 10f;
sourceComponent.useGravity = false;
var target = new GameObject();
sourceComponent.MoveTo(target);
// effects of Destroy only take place at end of frame
yield return null;
Assert.That(sourceComponent == null);
Assert.That(source.TryGetComponent(out Rigidbody _), Is.False);
Assert.That(target.TryGetComponent(out Rigidbody targetComponent));
Assert.That(targetComponent.mass, Is.EqualTo(10.0f));
Assert.That(targetComponent.useGravity, Is.False);
Object.Destroy(source);
Object.Destroy(target);
}
[UnityTest]
public IEnumerator MoveTo_ShouldThrowArgumentNullException_GivenNullComponent()
{
var target = new GameObject();
Rigidbody rigidbody = null!;
Assert.Throws<ArgumentNullException>(() => rigidbody.MoveTo(target));
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator MoveTo_ShouldThrowArgumentNullException_GivenNullTarget()
{
var source = new GameObject();
var rigidbody = source.AddComponent<Rigidbody>();
GameObject target = null!;
Assert.Throws<ArgumentNullException>(() => rigidbody.MoveTo(target));
Object.Destroy(source);
yield break;
}
[UnityTest]
public IEnumerator MoveTo_ShouldThrowInvalidOperationException_GivenDuplicate()
{
var source = new GameObject();
var rigidbody = source.AddComponent<Rigidbody>();
var target = new GameObject();
target.AddComponent<Rigidbody>();
Assert.Throws<InvalidOperationException>(() => rigidbody.MoveTo(target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
} }
} }

View File

@ -1,6 +1,5 @@
#nullable enable #nullable enable
using System;
using System.Collections; using System.Collections;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using NUnit.Framework; using NUnit.Framework;
@ -12,97 +11,6 @@ namespace X10D.Unity.Tests
{ {
public class GameObjectTests public class GameObjectTests
{ {
[UnityTest]
public IEnumerator CopyComponent_ShouldCopyComponent_GivenComponent()
{
var source = new GameObject();
var sourceComponent = source.AddComponent<Rigidbody>();
sourceComponent.mass = 10.0f;
sourceComponent.useGravity = false;
var target = new GameObject();
source.CopyComponent<Rigidbody>(target);
Assert.That(target.TryGetComponent(out Rigidbody targetComponent));
Assert.That(targetComponent.mass, Is.EqualTo(10.0f));
Assert.That(targetComponent.useGravity, Is.False);
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator CopyComponent_ShouldThrowArgumentNullException_GivenNullComponentType()
{
var source = new GameObject();
var target = new GameObject();
Type componentType = null!;
Assert.Throws<ArgumentNullException>(() => source.CopyComponent(componentType, target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator CopyComponent_ShouldThrowArgumentNullException_GivenNullGameObject()
{
var target = new GameObject();
GameObject source = null!;
Assert.Throws<ArgumentNullException>(() => source.CopyComponent<Rigidbody>(target));
Assert.Throws<ArgumentNullException>(() => source.CopyComponent(typeof(Rigidbody), target));
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator CopyComponent_ShouldThrowArgumentNullException_GivenNullTarget()
{
var source = new GameObject();
GameObject target = null!;
Assert.Throws<ArgumentNullException>(() => source.CopyComponent<Rigidbody>(target));
Assert.Throws<ArgumentNullException>(() => source.CopyComponent(typeof(Rigidbody), target));
Object.Destroy(source);
yield break;
}
[UnityTest]
public IEnumerator CopyComponent_ShouldThrowInvalidOperationException_GivenInvalidComponent()
{
var source = new GameObject();
var target = new GameObject();
Assert.Throws<InvalidOperationException>(() => source.CopyComponent<Rigidbody>(target));
Assert.Throws<InvalidOperationException>(() => source.CopyComponent(typeof(Rigidbody), target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator CopyComponent_ShouldThrowInvalidOperationException_GivenDuplicate()
{
var source = new GameObject();
source.AddComponent<Rigidbody>();
var target = new GameObject();
target.AddComponent<Rigidbody>();
Assert.Throws<InvalidOperationException>(() => source.CopyComponent<Rigidbody>(target));
Assert.Throws<InvalidOperationException>(() => source.CopyComponent(typeof(Rigidbody), target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest] [UnityTest]
public IEnumerator GetComponentsInChildrenOnly_ShouldIgnoreParent() public IEnumerator GetComponentsInChildrenOnly_ShouldIgnoreParent()
{ {
@ -160,101 +68,6 @@ namespace X10D.Unity.Tests
yield break; yield break;
} }
[UnityTest]
public IEnumerator MoveComponent_ShouldCopyComponent_GivenComponent()
{
var source = new GameObject();
var sourceComponent = source.AddComponent<Rigidbody>();
sourceComponent.mass = 10.0f;
sourceComponent.useGravity = false;
var target = new GameObject();
source.MoveComponent<Rigidbody>(target);
// effects of Destroy only take place at end of frame
yield return null;
Assert.That(sourceComponent == null);
Assert.That(source.TryGetComponent(out Rigidbody _), Is.False);
Assert.That(target.TryGetComponent(out Rigidbody targetComponent));
Assert.That(targetComponent.mass, Is.EqualTo(10.0f));
Assert.That(targetComponent.useGravity, Is.False);
Object.Destroy(source);
Object.Destroy(target);
}
[UnityTest]
public IEnumerator MoveComponent_ShouldThrowArgumentNullException_GivenNullComponentType()
{
var source = new GameObject();
var target = new GameObject();
Type componentType = null!;
Assert.Throws<ArgumentNullException>(() => source.MoveComponent(componentType, target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator MoveComponent_ShouldThrowArgumentNullException_GivenNullGameObject()
{
var target = new GameObject();
GameObject source = null!;
Assert.Throws<ArgumentNullException>(() => source.MoveComponent<Rigidbody>(target));
Assert.Throws<ArgumentNullException>(() => source.MoveComponent(typeof(Rigidbody), target));
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator MoveComponent_ShouldThrowArgumentNullException_GivenNullTarget()
{
var source = new GameObject();
GameObject target = null!;
Assert.Throws<ArgumentNullException>(() => source.MoveComponent<Rigidbody>(target));
Assert.Throws<ArgumentNullException>(() => source.MoveComponent(typeof(Rigidbody), target));
Object.Destroy(source);
yield break;
}
[UnityTest]
public IEnumerator MoveComponent_ShouldThrowInvalidOperationException_GivenInvalidComponent()
{
var source = new GameObject();
var target = new GameObject();
Assert.Throws<InvalidOperationException>(() => source.MoveComponent<Rigidbody>(target));
Assert.Throws<InvalidOperationException>(() => source.MoveComponent(typeof(Rigidbody), target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest]
public IEnumerator MoveComponent_ShouldThrowInvalidOperationException_GivenDuplicate()
{
var source = new GameObject();
source.AddComponent<Rigidbody>();
var target = new GameObject();
target.AddComponent<Rigidbody>();
Assert.Throws<InvalidOperationException>(() => source.MoveComponent<Rigidbody>(target));
Assert.Throws<InvalidOperationException>(() => source.MoveComponent(typeof(Rigidbody), target));
Object.Destroy(source);
Object.Destroy(target);
yield break;
}
[UnityTest] [UnityTest]
public IEnumerator SetLayerRecursively_ShouldSetLayerRecursively() public IEnumerator SetLayerRecursively_ShouldSetLayerRecursively()
{ {

View File

@ -1,8 +1,4 @@
using System.Globalization; using UnityEngine;
using System.Reflection;
using UnityEngine;
using X10D.Reflection;
using Object = UnityEngine.Object;
namespace X10D.Unity; namespace X10D.Unity;
@ -11,93 +7,6 @@ namespace X10D.Unity;
/// </summary> /// </summary>
public static class ComponentExtensions public static class ComponentExtensions
{ {
/// <summary>
/// Copies the component to another game object.
/// </summary>
/// <param name="component">The component to copy.</param>
/// <param name="target">The game object to which the component will be copied.</param>
/// <typeparam name="T">The type of the component to copy.</typeparam>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="component" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <paramref name="target" /> already has a component of type <typeparamref name="T" />.
/// </exception>
/// <remarks>
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
/// </remarks>
public static void CopyTo<T>(this T component, GameObject target)
where T : Component
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
if (target.TryGetComponent(out T targetComponent))
{
string message = ExceptionMessages.ComponentAlreadyExists;
message = string.Format(CultureInfo.CurrentCulture, message, target.name, typeof(T).Name);
throw new InvalidOperationException(message);
}
targetComponent = target.AddComponent<T>();
var typeInfo = typeof(T).GetTypeInfo();
CopyFields(typeInfo, component, targetComponent);
}
/// <summary>
/// Copies the component to another game object.
/// </summary>
/// <param name="component">The component to copy.</param>
/// <param name="target">The game object to which the component will be copied.</param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="component" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <paramref name="target" /> already has a component of the same type.
/// </exception>
/// <remarks>
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
/// </remarks>
public static void CopyTo(this Component component, GameObject target)
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
var componentType = component.GetType();
if (target.TryGetComponent(componentType, out Component targetComponent))
{
string message = ExceptionMessages.ComponentAlreadyExists;
message = string.Format(CultureInfo.CurrentCulture, message, target.name, componentType.Name);
throw new InvalidOperationException(message);
}
targetComponent = target.AddComponent(componentType);
var typeInfo = componentType.GetTypeInfo();
CopyFields(typeInfo, component, targetComponent);
}
/// <summary> /// <summary>
/// Returns an array of components of the specified type, excluding components that live on the object to which this /// Returns an array of components of the specified type, excluding components that live on the object to which this
/// component is attached. /// component is attached.
@ -109,107 +18,4 @@ public static class ComponentExtensions
{ {
return component.gameObject.GetComponentsInChildrenOnly<T>(); return component.gameObject.GetComponentsInChildrenOnly<T>();
} }
/// <summary>
/// Moves the component to another game object.
/// </summary>
/// <param name="component">The component to move.</param>
/// <param name="target">The game object to which the component will be moved.</param>
/// <typeparam name="T">The type of the component to move.</typeparam>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="component" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <paramref name="target" /> already has a component of type <typeparamref name="T" />.
/// </exception>
/// <remarks>
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
/// </remarks>
public static void MoveTo<T>(this T component, GameObject target)
where T : Component
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
component.CopyTo(target);
Object.Destroy(component);
}
/// <summary>
/// Moves the component to another game object.
/// </summary>
/// <param name="component">The component to move.</param>
/// <param name="target">The game object to which the component will be moved.</param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="component" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <paramref name="target" /> already has a component of the same type.
/// </exception>
/// <remarks>
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
/// </remarks>
public static void MoveTo(this Component component, GameObject target)
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
component.CopyTo(target);
Object.Destroy(component);
}
private static void CopyFields<T>(TypeInfo typeInfo, T component, T targetComponent)
where T : Component
{
foreach (FieldInfo field in typeInfo.DeclaredFields)
{
if (field.IsStatic || !field.IsPublic && !field.HasCustomAttribute<SerializeField>())
{
continue;
}
if (field.HasCustomAttribute<NonSerializedAttribute>())
{
continue;
}
object fieldValue = GetNewReferences(component, targetComponent, field.GetValue(component));
field.SetValue(targetComponent, fieldValue);
}
}
private static object GetNewReferences<T>(T component, T targetComponent, object value)
where T : Component
{
if (ReferenceEquals(value, component))
{
value = targetComponent;
}
else if (ReferenceEquals(value, component.gameObject))
{
value = targetComponent.gameObject;
}
return value;
}
} }

View File

@ -1,6 +1,4 @@
using System.Globalization;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object;
namespace X10D.Unity; namespace X10D.Unity;
@ -9,90 +7,6 @@ namespace X10D.Unity;
/// </summary> /// </summary>
public static class GameObjectExtensions public static class GameObjectExtensions
{ {
/// <summary>
/// Copies the component of the specified type from one game object to another.
/// </summary>
/// <param name="gameObject">The game object from which to copy the component.</param>
/// <param name="target">The game object to which the component will be copied.</param>
/// <typeparam name="T">The type of the component to copy.</typeparam>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="gameObject" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <para><paramref name="gameObject" /> does not have a component of type <typeparamref name="T" />.</para>
/// -or-
/// <para><paramref name="target" /> already has a component of type <typeparamref name="T" />.</para>
/// </exception>
public static void CopyComponent<T>(this GameObject gameObject, GameObject target)
where T : Component
{
if (gameObject == null)
{
throw new ArgumentNullException(nameof(gameObject));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
if (!gameObject.TryGetComponent(out T sourceComponent))
{
string message = ExceptionMessages.ComponentDoesNotExist;
message = string.Format(CultureInfo.CurrentCulture, message, gameObject.name, typeof(T).Name);
throw new InvalidOperationException(message);
}
sourceComponent.CopyTo(target);
}
/// <summary>
/// Copies the component of the specified type from one game object to another.
/// </summary>
/// <param name="gameObject">The game object from which to copy the component.</param>
/// <param name="componentType">The type of the component to copy.</param>
/// <param name="target">The game object to which the component will be copied.</param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="gameObject" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <para><paramref name="gameObject" /> does not have a component of type <paramref name="componentType" />.</para>
/// -or-
/// <para><paramref name="target" /> already has a component of type <paramref name="componentType" />.</para>
/// </exception>
public static void CopyComponent(this GameObject gameObject, Type componentType, GameObject target)
{
if (gameObject == null)
{
throw new ArgumentNullException(nameof(gameObject));
}
if (componentType is null)
{
throw new ArgumentNullException(nameof(componentType));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
if (!gameObject.TryGetComponent(componentType, out Component sourceComponent))
{
string message = ExceptionMessages.ComponentDoesNotExist;
message = string.Format(CultureInfo.CurrentCulture, message, gameObject.name, componentType.Name);
throw new InvalidOperationException(message);
}
sourceComponent.CopyTo(target);
}
/// <summary> /// <summary>
/// Returns an array of components of the specified type, excluding components that live on this game object. /// Returns an array of components of the specified type, excluding components that live on this game object.
/// </summary> /// </summary>
@ -257,100 +171,6 @@ public static class GameObjectExtensions
gameObject.transform.LookAt(target, worldUp); gameObject.transform.LookAt(target, worldUp);
} }
/// <summary>
/// Moves the component of the specified type from one game object to another.
/// </summary>
/// <param name="gameObject">The game object from which to move the component.</param>
/// <param name="target">The game object to which the component will be moved.</param>
/// <typeparam name="T">The type of the component to copy.</typeparam>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="gameObject" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <para><paramref name="gameObject" /> does not have a component of type <typeparamref name="T" />.</para>
/// -or-
/// <para><paramref name="target" /> already has a component of type <typeparamref name="T" />.</para>
/// </exception>
/// <remarks>
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
/// </remarks>
public static void MoveComponent<T>(this GameObject gameObject, GameObject target)
where T : Component
{
if (gameObject == null)
{
throw new ArgumentNullException(nameof(gameObject));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
if (!gameObject.TryGetComponent(out T sourceComponent))
{
string message = ExceptionMessages.ComponentDoesNotExist;
message = string.Format(CultureInfo.CurrentCulture, message, gameObject.name, typeof(T).Name);
throw new InvalidOperationException(message);
}
sourceComponent.MoveTo(target);
Object.Destroy(sourceComponent);
}
/// <summary>
/// Moves the component of the specified type from one game object to another.
/// </summary>
/// <param name="gameObject">The game object from which to move the component.</param>
/// <param name="componentType">The type of the component to copy.</param>
/// <param name="target">The game object to which the component will be moved.</param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="gameObject" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="componentType" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="target" /> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="InvalidOperationException">
/// <para><paramref name="gameObject" /> does not have a component of type <paramref name="componentType" />.</para>
/// -or-
/// <para><paramref name="target" /> already has a component of type <paramref name="componentType" />.</para>
/// </exception>
/// <remarks>
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
/// </remarks>
public static void MoveComponent(this GameObject gameObject, Type componentType, GameObject target)
{
if (gameObject == null)
{
throw new ArgumentNullException(nameof(gameObject));
}
if (componentType is null)
{
throw new ArgumentNullException(nameof(componentType));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
if (!gameObject.TryGetComponent(componentType, out Component sourceComponent))
{
string message = ExceptionMessages.ComponentDoesNotExist;
message = string.Format(CultureInfo.CurrentCulture, message, gameObject.name, componentType.Name);
throw new InvalidOperationException(message);
}
sourceComponent.MoveTo(target);
Object.Destroy(sourceComponent);
}
/// <summary> /// <summary>
/// Sets the new layer of this game object and its children, recursively. /// Sets the new layer of this game object and its children, recursively.
/// </summary> /// </summary>